DataBinding viewBinding(视图绑定与数据双向绑定)简单案例 (kotlin)

news2025/1/18 11:42:55

先上效果:

4个view的文字都是通过DataBinding填充的。交互事件:点击图片,切换图片

创建项目(android Studio 2023.3.1)

Build.gradle(:app) 引入依赖库(完整源码)

    buildFeatures {
        viewBinding true
        compose true
    }
    dataBinding {
        enabled = true
    }

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    id 'kotlin-kapt'
}

android {
    namespace 'com.example.lanidemokt'
    compileSdk 31

    defaultConfig {
        applicationId "com.example.lanidemokt"
        minSdk 24
        targetSdk 30
        versionCode 1
        versionName "1.0"


        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    lintOptions {
        abortOnError false
    }
    buildFeatures {
        viewBinding true
        compose true
    }
    dataBinding {
        enabled = true
    }
}

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
    implementation "androidx.compose.ui:ui:1.0.1"
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'androidx.appcompat:appcompat-resources:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.compose.material:material:1.0.1'

    implementation 'com.github.bumptech.glide:compiler:4.11.0'
    implementation 'com.github.bumptech.glide:glide:4.11.0'
}

1. 基本使用意向绑定数据显示在界面 

MainActivity.kt (完整源码)

在MainActivity.kt里,Databinding和我们的XML文件绑定起来了,现在你点击Databinding会发现直接可以跳转到对应的XML文件里面去了,

package com.example.lanidemokt

import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.databinding.BaseObservable
import androidx.databinding.DataBindingUtil
import com.catchpig.utils.LogUtils
import com.example.lanidemokt.adapter.MainActivityBindingAdapter
import com.example.lanidemokt.databinding.ActivityMainBinding
import com.example.lanidemokt.viewmodel.ButtonClickListener
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.Date

class MainActivity : AppCompatActivity() {

    /*
    * DataBinding 对应一个Binding对象,对象名是布局文件文称加上Binding后缀
    * binding,activity_main.xml的布局实例
    * xml上所有变量与点击事件,必须是binding的成员属性或者成员方法函数,否则操作界面无效
    *
    * */
    var binding: ActivityMainBinding? = null // 操作布局实例
    private var login: Login? = null  //声明一个响应式对象,用于ui
    var clickListener: ButtonClickListener? = null // 布局点击对象封装


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) //        setContentView(R.layout.activity_main)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
        initView()
        initData()
        corGlobalTest()
    }

    private fun initData() {
        LogUtils.init(this)
    }

    /* *
     * 在Controller层,将我们的data与model相关联
     * */
    data class Login(var name: String = "LLL", var msg: String) //意向绑定响应式

    data class Student(var name: String = "LLL", var score: Int) : BaseObservable() //双击绑定响应式

    private fun initView() {
        binding?.msg?.setText("我是谁")
        login = Login("LANI", "我是谁")
        //  binding?.login = Login("LANI", "我是谁")
        //  这一步必须要,否则点击没反应,否则界面不显示对应的名字与信息
        binding?.setLogin(Login("LANI", "我是谁"))

        binding?.setStudent(Student("LEE", 199))
        binding?.picture1?.setOnClickListener {
            println("图片点击")
            MainActivityBindingAdapter.loadStudentDetails(
                it as ImageView,
                "http://192.168.1.207:8080/download/88.jpg"
            )
        }
        clickListener=ButtonClickListener()
        binding?.btnHandler = clickListener

    }

    /*
    * 协程创建
    * */
    fun corGlobalTest() {
        GlobalScope.launch {
            println("|--开始global${Date()}")
            delay(1000)
            println("|--END global${Date()}")
        }

        println("|--END ${Date()}")

    }
}
 activity_main.xml

现在我们就来看看如何给我们的XML文件里面的View设置值。

在XML文件的layout标签下,创建data标签,在data标签中再创建variable标签,variable标签主要用到的就是name属性和type属性,类似于Java语言声明变量时,需要为该变量指定类型和名称。新建一个名为Login的数据类。

在XML文件中声明好variable属性后,接下来就可以在XML使用它了。

使用variable属性时需要使用到布局表达式: @{ }

可以在布局表达式@{ }中获取传入variable对象的

 activity_main.xml (源码 )
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="login"
            type="com.example.lanidemokt.MainActivity.Login" />

        <variable
            name="student"
            type="com.example.lanidemokt.MainActivity.Student" />

    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{login.name}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="名字" />

        <TextView
            android:id="@+id/msg2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{login.msg}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/msg"
            tools:text="消息" />

        <TextView
            android:id="@+id/msg4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{ ` `+ student.score}"
            app:layout_constraintBottom_toBottomOf="@id/login"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/msg2"
            tools:text="消息2" />



    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

2. 给控件View添加响应事件:

方式一:直接在Controller层通过原来的方式添加

binding?.login?.setOnClickListener {
          
}

 方式二:

创建一个工具类,在类中定义响应的点击事件

第一步:创建点击的工具类 ButtonClickListener.kt

第二步:在XML文件中添加工具类 ,在XML文件中添加响应事件:

第三步:在XML文件中添加响应事件 android:onClick="@{btnHandler::click}"

第四步:在Controller里面进行关联 binding?.btnHandler = clickListener

 activity_main.xml  (增加点击事件的完整源码)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <!--需要使用到响应数据类引入
        data标签中再创建variable标签,variable标签主要用到的就是name属性和type属性 ,
        类似于Java语言声明变量时,需要为该变量指定类型和名称
        -->
        <import type="android.view.View" />
        <!--        <import type="com.example.lanidemokt.MainActivity" />-->


        <!--        <variable
                    name="login"
                    type="com.example.lanidemokt.MainActivity.Login" />-->

        <variable
            name="btnHandler"
            type="com.example.lanidemokt.viewmodel.ButtonClickListener" />

        <variable
            name="login"
            type="com.example.lanidemokt.MainActivity.Login" />

        <variable
            name="student"
            type="com.example.lanidemokt.MainActivity.Student" />

    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{login.name}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="名字" />

        <TextView
            android:id="@+id/msg2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{login.msg}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/msg"
            tools:text="消息" />

        <TextView
            android:id="@+id/msg4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{ ` `+ student.score}"
            app:layout_constraintBottom_toBottomOf="@id/login"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/msg2"
            tools:text="消息2" />

        <Button
            android:id="@+id/login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="杀生丸哥哥"
            android:onClick="@{btnHandler::msgTextClickListener}"
            android:layout_marginBottom="20dp"
            app:layout_constraintBottom_toBottomOf="@id/picture1"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/msg4"
            tools:text="消息2" />

        <ImageView
            android:id="@+id/picture1"
            android:layout_width="300dp"
            android:layout_height="200dp"
            android:layout_marginBottom="20dp"
            android:layout_marginTop="20dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/login"
            app:layout_constraintBottom_toBottomOf="parent"
            app:url="@{`http://192.168.1.207:8080/download/kn.png`}" />


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
 ButtonClickListener.kt 源码
package com.example.lanidemokt.viewmodel

import android.view.View
import android.widget.TextView
import com.example.lanidemokt.utils.LogSetting

class ButtonClickListener {
    /*
    * 界面点击事件封装
    * */
    fun msgTextClickListener(view: View) {
        view.text = "杀生丸丸哥哥一直很帅"
//        view.setText( "杀生丸丸哥哥一直很帅") //Use of setter method instead of property access syntax

    }
}

3. XXXBindingAdapter方法实现响应

使用DataBinding库时,DataBinding会针对控件属性生成对应的XXXBindingAdapter类,如TextViewBindingAdapter类,其对TextView的每个可以使用DataBinding的属性都生成了对应的方法,而且每个方法都使用了@BindingAdapter注解,注解中的参数就是对应View的属性。

自定义BindingAdapter 编写一个处理图片的自定义BindingAdapter类。然后定义一个静态方法,主要用于添加 BindingAdapter 注解,注解值是 ImageView 控件自定义的属性名,如下所示。

MainActivityBindingAdapter.kt (源码)

图片资源是部署到本地的Nginx上的:

http://192.168.1.207:8080/download/kn.png

package com.example.lanidemokt.adapter

import android.util.Log
import android.widget.ImageView
import androidx.databinding.BindingAdapter
import com.bumptech.glide.Glide
import com.catchpig.utils.LogUtils
import com.example.lanidemokt.utils.LogSetting

class MainActivityBindingAdapter {
    companion object {
        val TAG: String = "MainActivityBindingAdapter"
        /*
        * 通过默认adapter 设置自定app:xxx属性,并设置xx属性值,实现响应式修改更新
        *
        * */
        @BindingAdapter("url")
        @JvmStatic
        fun loadStudentDetails(
            view: ImageView,
            url: String = "http://192.168.1.207:8080/download/kn.png"
        ) {
            Glide.with(view!!).load(url).into(view)
        }
    }
}

多个参数的话,修改@BindingAdapter有value


        @BindingAdapter(value = ["url", "placeholder", "error"])
        @JvmStatic 

4.双向响应绑定(输入框)

输入数字时,消息text同步更新

Build.gradle(:app) 引入依赖库(完整源码)

增加自动生成BR实体的依赖库, 

id 'kotlin-kapt'
kapt {
    generateStubs = true
}
kapt  "androidx.room:room-compiler:2.4.0"

 

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    id 'kotlin-kapt'
}

android {
    namespace 'com.example.lanidemokt'
    compileSdk 31

    defaultConfig {
        applicationId "com.example.lanidemokt"
        minSdk 24
        targetSdk 30
        versionCode 1
        versionName "1.0"


        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    lintOptions {
        abortOnError false
    }
    buildFeatures {
        viewBinding true
//        dataBinding  true
        compose true
    }
   dataBinding {
        enabled = true
    }
    kapt {
        generateStubs = true
    }
}
dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
    implementation "androidx.compose.ui:ui:1.0.1"
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'androidx.appcompat:appcompat-resources:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.compose.material:material:1.0.1'

    implementation 'com.github.bumptech.glide:compiler:4.11.0'
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    kapt  "androidx.room:room-compiler:2.4.0"
}

 

MainActivity.kt 源码

增加绑定viewmodel:    binding?.order = OrderViewModel() // 绑定双向响应实体

package com.example.lanidemokt

import android.os.Bundle
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.databinding.BaseObservable
import androidx.databinding.DataBindingUtil
import com.catchpig.utils.LogUtils
import com.example.lanidemokt.adapter.MainActivityBindingAdapter
import com.example.lanidemokt.databinding.ActivityMainBinding
import com.example.lanidemokt.viewmodel.ButtonClickListener
import com.example.lanidemokt.viewmodel.OrderViewModel
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.Date

class MainActivity : AppCompatActivity() {

    /*
    * DataBinding 对应一个Binding对象,对象名是布局文件文称加上Binding后缀
    * binding,activity_main.xml的布局实例
    * xml上所有变量与点击事件,必须是binding的成员属性或者成员方法函数,否则操作界面无效
    * 布局取响应式值 -表达式: `@{ }
    *
    * */
    var binding: ActivityMainBinding? = null // 操作布局实例
    private var login: Login? = null  //声明一个响应式对象,用于ui
    var clickListener: ButtonClickListener? = null // 布局点击对象封装
    var vm: OrderViewModel = OrderViewModel()


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) //        setContentView(R.layout.activity_main)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }
        initView()
        initData()
        corGlobalTest()
    }

    private fun initData() {
        LogUtils.init(this)
    }

    /* *
     * 在Controller层,将我们的data与model相关联
     * */
    data class Login(var name: String = "LLL", var msg: String) //意向绑定响应式

    data class Student(var name: String = "LLL", var score: Int) : BaseObservable() //双击绑定响应式

    private fun initView() {
        binding?.msg?.setText("我是谁")
        login = Login("LANI", "我是谁")
        //  binding?.login = Login("LANI", "我是谁")
        //  这一步必须要,否则点击没反应,否则界面不显示对应的名字与信息
        binding?.setLogin(Login("LANI", "我是谁"))

        binding?.setStudent(Student("LEE", 199))
        MainActivityBindingAdapter.loadStudentDetails(
            binding?.picture1 as ImageView,
            "http://192.168.1.207:8080/download/kn.png"
        )
        binding?.picture1?.setOnClickListener {
            println("图片点击")
            LogUtils.d("图片点击")
            MainActivityBindingAdapter.loadStudentDetails(
                it as ImageView,
                "http://192.168.1.207:8080/download/88.jpg"
            )
        }
        clickListener = ButtonClickListener()
        binding?.btnHandler = clickListener //给控件添加响应事件 :点击事件
        binding?.order = OrderViewModel() // 绑定双向响应实体


    }

    /*
    * 协程创建
    * */
    fun corGlobalTest() {
        GlobalScope.launch {
            println("|--开始global${Date()}")
            delay(1000)
            println("|--END global${Date()}")
        }

        println("|--END ${Date()}")

    }
}
OrderViewModel.kt 源码

实现双向绑定 viewmodel,BaseObservable :普通的数据对象包装成一个可观察的数据对象

package com.example.lanidemokt.viewmodel

import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import com.catchpig.utils.LogUtils
import com.example.lanidemokt.BR


class OrderViewModel : BaseObservable() {
    /*
    * 实现双向绑定 viewmodel,
    * BaseObservable :普通的数据对象包装成一个可观察的数据对象
    * 当使用name字段发生变更后,若想UI自动刷新,
    * 要求方法名必须以get开头并且标记Bindable注解
    * 注解才会自动在build目录BR类中生成entry
    * 数据模型继承 BaseObservable;
   * 要求获取数据方法名必须以 get 开头并且标记 @Bindable 注解;
   * 设置数据方法必须以 set 开头然后调用 notify() 函数既可以刷新视图。
      * BR 类是 BaseObservable 子类中由 @Bindable 注解修饰的函数生成;
     * BR 类生成位置在 //app\build\generated\source\kapt\debug\com\example\lanidemokt
     *
    * */


    @get:Bindable
    var orderCount: String? = "100"
        set(orderCount) {
            LogUtils.d("当前orderCount=${orderCount}")
            field = orderCount
            notifyPropertyChanged(BR.orderCount)
        }

}
activity_main.xml源码

引入viewmodel:

<variable name="order"  type="com.example.lanidemokt.viewmodel.OrderViewModel" />

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <import type="android.view.View" />

        <variable
            name="order"
            type="com.example.lanidemokt.viewmodel.OrderViewModel" />

        <variable
            name="btnHandler"
            type="com.example.lanidemokt.viewmodel.ButtonClickListener" />

        <variable
            name="login"
            type="com.example.lanidemokt.MainActivity.Login" />

        <variable
            name="student"
            type="com.example.lanidemokt.MainActivity.Student" />

    </data>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:onClick="@{btnHandler::msgTextClickListener}"
            android:text="杀生丸哥哥"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/msg4"
            tools:text="杀生丸哥哥" />

        <TextView
            android:id="@+id/msg5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text="@{ ` 当前订单数量:`+order.orderCount}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/login"
            tools:text="消息2" />

        <!-- 双向响应数据,赋值语法  @={xx.xx}-->
        <EditText
            android:id="@+id/username"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="@={order.orderCount}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/msg5" />

        <!--  XXXBindingAdapter方式设置app:url   -->
        <ImageView
            android:id="@+id/picture1"
            android:layout_width="300dp"
            android:layout_height="200dp"
            android:layout_marginTop="20dp"
            android:layout_marginBottom="20dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/login"
            app:url="@{`http://192.168.1.207:8080/download/kn.png`}" />


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

双向响应完结。

-- 设置网络图片在ImageView 打开网络权限

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:networkSecurityConfig="@xml/network_security_config"
        android:theme="@style/Theme.LaniDemoKt"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:usesCleartextTraffic="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

app\src\main\res\xml\network_securit_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1682476.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

常见算法(1)

1.基本查找/顺序查找 核心&#xff1a;从0索引之后挨个查找 实现代码&#xff1a; public class test {public static void main(String [] arg) throws ParseException {int[] arr {121,85,46,15,55,77,63,49};int number55;System.out.println(bashi(arr,number));}publi…

el-upload上传图片,视频可获取视频时长。

对element-ui组件的upload组件再一次封装&#xff0c;简单记录。下面是效果图。 注意点&#xff1a;该组件现在仅支持单图和单个视频上传。 <template><div :style"myStyle"><divclass"uploads":style"{width: upWith px,height: up…

多商户消费券系统源码(ThinkPHP+FastAdmin+微信公众号)

打造智能促销新体验 一、引言&#xff1a;消费券系统的时代意义 在当今这个数字化高速发展的时代&#xff0c;电子商务和移动支付已经成为人们日常生活的重要组成部分。随着市场竞争的加剧&#xff0c;多商户消费券系统作为一种创新的促销手段&#xff0c;正逐渐受到商家和消…

容器监控方案

1、docker部署prometheus Prometheus是一套开源的系统监控报警框架&#xff0c;它基于时序数据库&#xff0c;并通过HTTP协议周期性地从被监控的组件中抓取指标数据。以下是一些关于Prometheus的详细介绍&#xff1a; 基本概念&#xff1a;Prometheus所有采集的监控数据均以指…

Swift使用JSONDecoder处理json数据,实现json序列化和反序列化

Json数据处理是开发中不可获取的一项技能&#xff0c;如果你不会处理json数据&#xff0c;那你离失业就不远了&#xff0c;所以学完了swift基础教程&#xff0c;还是先老老实实学习一下json处理吧&#xff0c;有了这项技能&#xff0c;你才可以继续下一个网络请求阶段的开发&am…

深度学习之基于YoloV5车型识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景与意义 在智能交通、安全监控等领域&#xff0c;车型识别技术具有重要的应用价值。传统的车型识别方法…

栈(基于动态顺序表实现的栈)

栈的简单介绍 关于栈的性质咳咳 栈&#xff1a;栈是一种特殊的线性表,其中只让在一端插入和删除元素。 后进先出 进行插入删除的那一端叫栈顶&#xff0c;另一端叫栈底 我们实现的栈是基于一个动态顺序表的的栈&#xff0c;会实现栈的 入栈&#xff0c;出栈&#xff0c;获取…

【NumPy】关于numpy.eye()函数,看这一篇文章就够了

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

15:00面试,15:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

【重学C++】02 脱离指针陷阱:深入浅出 C++ 智能指针

前言 大家好&#xff0c;今天是【重学C】系列的第二讲&#xff0c;我们来聊聊C的智能指针。 为什么需要智能指针 在上一讲《01 C如何进行内存资源管理》中&#xff0c;提到了对于堆上的内存资源&#xff0c;需要我们手动分配和释放。管理这些资源是个技术活&#xff0c;一不…

Thinkphp内核开发盲盒商城源码v2.0 对接易支付/阿里云短信/七牛云存储

源码简介 这套系统是我从以前客户手里拿到的,100完整可用,今天测试防红链接失效了,需要修改防红API即可!前端页面展示我就不放了,懂的都懂 优点是Thinkphp开发的&#xff0c;二开容易。 源码图片 资源获取&#xff1a;Thinkphp内核开发盲盒商城源码v2.0 对接易支付/阿里云短…

浅谈hook下的免杀研究

文章目录 前记实现过程后记reference 前记 原理 CS在高版本中推出了sleep mask功能&#xff0c;即在beacon sleep时对堆进行加密混淆&#xff0c;绕过内存扫描&#xff0c;在恢复运行前还原&#xff0c;防止进程崩溃。beacon每次运行的时间远短于sleep时间&#xff0c;内存扫描…

python实现520表白图案

今天是520哦&#xff0c;作为程序员有必要通过自己的专业知识来向你的爱人表达下你的爱意。那么python中怎么实现绘制520表白图案呢&#xff1f;这里给出方法&#xff1a; 1、使用图形库&#xff08;如turtle&#xff09; 使用turtle模块&#xff0c;你可以绘制各种形状和图案…

GPT‑4o普通账户也可以免费用

网址 https://chatgpt.com/ 试了一下&#xff0c;免费的确实显示GPT‑4o的模型&#xff0c;问了一下可以联网&#xff0c;不知道能不能通过插件出图 有兴趣的可以试试

海山数据库(He3DB)代理ProxySQL使用详解:(一)架构说明与安装

一、ProxySQL介绍 1.1 简介 业界比较知名的MySQL代理&#xff0c;由ProxySQL LLC公司开发并提供专业的服务支持&#xff0c;基于GPLv3开源协议进行发布,大部分配置项可动态变更。后端的MySQL实例可根据用途配置到不同的hostgroup中&#xff0c;由ProxySQL基于7层网络协议,将来…

第二证券股市资讯:突传重磅!高盛最新发声,事关中国股票!

外资猛买我国财物。 高盛在最新发布的陈述中称&#xff0c;海外对冲基金已连续第四周增持我国股票。另据彭博社的数据显现&#xff0c;上星期&#xff0c;我国是新式商场国家中录得最大资金流入的商场&#xff0c;达4.88亿美元&#xff08;约合人民币35亿元&#xff09;。 北…

滴滴三面 | Go后端研发

狠狠的被鞭打了快两个小时… 注意我写的题解不一定是对的&#xff0c;如果你认为有其他答案欢迎评论区留言 bg&#xff1a;23届 211本 社招 1. 自我介绍 2. 讲一个项目的点&#xff0c;因为用到了中间件平台的数据同步&#xff0c;于是开始鞭打数据同步。。 3. 如果同步的时候…

OpenFeign高级用法:缓存、QueryMap、MatrixVariable、CollectionFormat优雅地远程调用

码到三十五 &#xff1a; 个人主页 微服务架构中&#xff0c;服务之间的通信变得尤为关键。OpenFeign&#xff0c;一个声明式的Web服务客户端&#xff0c;使得REST API的调用变得更加简单和优雅。OpenFeign集成了Ribbon和Hystrix&#xff0c;具有负载均衡和容错的能力&#xff…

LInux实验二--进程间通信--信号

一、实验原理&#xff1a; 信号类似 windows 下的消息,用于通知进程有某种事件发生。只要知道进程的进 程号,就可以向进程发送信号。而进程可以自行定义对信号的处理方法。 二、实验内容&#xff1a; 实例一&#xff1a;编写实例&#xff0c;让子进程在启动2s后杀死父进程 /…

Docker Portainer使用

Portainer是什么 Docker Portainer是一个轻量级的 Web UI 管理界面,可以用来管理Docker环境。它提供了一个直观的控制台,用户可以通过它来管理Docker主机、容器、网络、卷等Docker资源。 Portainer的主要功能和特点包括: 容器管理:可以查看、启动、停止、删除容器,以及查看容器…