关于Chrome Extensions 消息通讯

有些功能需要用到消息通讯,比如我们需要获取某个网站的cookie发送到后台服务器,那么我们手动点击之后,需要获取cookie,然后发送。

一、设置服务脚本

定义接收通讯消息的处理脚本。

{ 
 "background": {
    "service_worker": "background/service-worker.js",
    "type": "module"
  },
  "host_permissions": [
    "https://*/*",
    "http://*/*"
  ],
  "permissions": [
    "cookies", 
    "declarativeNetRequestFeedback",
    "declarativeNetRequestWithHostAccess",
    "declarativeNetRequest",
    "webRequest"
  ]
}

二、在前台发送消息给服务脚本

通过chrome的api发送“chrome.runtime.sendMessage”

<template>
 <button type="primary" style="width: 100%;" @click="onSendMessage">发送消息</el-button>
</template>

<script>
const onSendMessage= async () => {
  const res = await chrome.runtime.sendMessage({
    diyData: dataBody
  });
}
<script>

三、接收

chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        console.log(request)
        console.log(sender)
        console.log(sendResponse)
        const data = request.diyData;

   chrome.cookies.getAll({
            domain: "taobao.com"
        }, function (cookies) {
            let cookieString = ""
            cookies.forEach(function (cookie) {
                 cookieString += cookie.name + "=" + cookie.value + ";"
            }
            fetch( "http://www.xx.com/api/data",
            {
                method: "post",
                headers: {
                   'Content-Type': 'application/json'
                },
                body: JSON.stringify(data),
            }
            ).then(
                res => {
                    console.log(res);
                    let reader = res.body.pipeThrough(new TextDecoderStream())
                        .getReader();
                    reader.read().then(rs => {
                        const data = JSON.parse(rs.value)
                        sendResponse(data); //发送给前端。
                    })
                }
            ).catch(
                err => {
                    console.log(err)
                }
            )
        });
    }
)

如果出现问题了,记得看看自己在清单中是否申请了权限。

Hilt依赖注入DataStore代码段

DataStore 代码段

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject

class DataStoreManager @Inject constructor(@ApplicationContext private val context: Context) {

    val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

    private val nameKey = stringPreferencesKey("name")
    private val emailKey = stringPreferencesKey("email")

    suspend fun saveName(userModel: UserModel) {
      context.dataStore.edit { preferences ->
            preferences[nameKey] = userModel.name
            preferences[emailKey] = userModel.email
      }
    }
}

Activity中使用

@AndroidEntryPoint
class MainActivity : ComponentActivity() {


    @Inject
    lateinit var dataStoreManager: DataStoreManager

    //其他代码......

}

Android下载网络图片到本地

第一步,需要添加授权

 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="32" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="32"
        tools:ignore="ScopedStorage" />

第二步:网络请求网络图片地址

   val imageUrl = "https://static.jikecrm.com/2018-06-10/152862233429525.jpg"
               val client = OkHttpClient()
               val request = Request.Builder()
                   .url(imageUrl)
                   .build()
               val call = client.newCall(request)

               call.enqueue(object: Callback {
                   override fun onFailure(call: Call, e: IOException) {
                       Log.e("TAG", "onFailure: 下载失败:${e.message}", )
                   }

                   override fun onResponse(call: Call, response: Response) {
                       if(response.isSuccessful){
                           val inputStream = response.body?.byteStream() ?: return
                           DownLoadImage().saveImage(inputStream,context)
                       }else{
                           Log.e("TAG", "onResponse: 下载失败~~~~~~~", )
                       }
                   }
               })

在协程中去请求,不要在UI主线程请求。

第三步保存图片到本地

    fun saveImage(inputStream: InputStream, context: Context) {

        val values = ContentValues().apply {
            put(MediaStore.Images.Media.DISPLAY_NAME, "tj_${System.currentTimeMillis()}.jpg") // 图片文件名
            put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg") // 图片MIME类型
            put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES) // 存储目录,此处为Pictures
        }

        val uri = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
        if (uri == null) {
            // 创建文件失败,处理错误
            Log.e("DownLoad", "saveImage: 创建文件失败")
            return
        }

        try {
            val fos = context.contentResolver.openOutputStream(uri, "w") ?: return
            val buffer = ByteArray(4 * 1024)
            var read: Int
            while (inputStream.read(buffer).also { read = it } != -1) {
                fos.write(buffer, 0, read)
            }
            fos.close()
            inputStream.close()

            // 更新媒体数据库,使图片在系统相册中可见
            MediaScannerConnection.scanFile(
                context,
                arrayOf(Environment.DIRECTORY_PICTURES),
                null,
                null
            )
            Log.i("DownLoad", "saveImage: 保存成功~~~")
        } catch (e: Exception) {
            context.contentResolver.delete(uri, null, null)
            Log.e("DownLoad", "saveImage: 保存失败:${e.message}")
        }
    }

如果是同一张网络图片,系统默认是覆盖的。

Android Jetpack Room使用的小结。

添加依赖:

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt' // 如果使用Kotlin,需要kapt编译器插件
    id 'dagger.hilt.android.plugin' // Hilt插件
}

dependencies {
    implementation 'com.google.dagger:hilt-android:2.40.5' // Hilt核心库
    kapt 'com.google.dagger:hilt-compiler:2.40.5' // 如果使用Kotlin,需要kapt编译器依赖
    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0' // Hilt与ViewModel的集成库
    implementation 'androidx.room:room-runtime:2.4.3' // Room运行时库
    kapt 'androidx.room:room-compiler:2.4.3' // 如果使用Kotlin,需要kapt编译器依赖
}

使用Room的三个步骤:

第一,定义数据库(AppDatabase是自定义的数据库类名称)

定义一个Room数据库实体(例如UserEntity)以及对应的Room数据库抽象类(例如AppDatabase),并使用@Database注解指定包含的实体类与版本信息。

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [UserEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

第二,创建数据的实体类Entity

@Entity:注解必须存在,这是实体类的标志,tableName 表名
@PrimaryKey:注解表示这个字段是主键,autoGenerate表示是自动增加的
@ColumnInfo:注解这个字段的别名,我们在其他地方就用这个别名。

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val name: String,
    val email: String
)

第三,定义一个Data Access Object (DAO) 接口(例如UserDao),包含用于更新数据的方法等

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update

@Dao
interface UserDao {
    @Update
    suspend fun updateUser(user: UserEntity)


    @Insert
    suspend fun insertUser(user: UserEntity)


    @Query("SELECT * FROM users WHERE id=:id ")
    suspend fun getUserById(id: Int): UserEntity?

}

第四创建Repository

创建一个Repository类(例如UserRepository),它将作为ViewModel与Room数据库之间的中介。在这个类中,注入UserDao并通过其方法执行数据更新操作。

class UserRepository @Inject constructor(private val userDao: UserDao) {

    suspend fun updateUser(user: UserEntity) {
        val data = userDao.getUserById(user.id!!)
        if (data == null) {
            userDao.insertUser(user)
        } else {
            userDao.updateUser(user)
        }

    }
}

第五:配置Hilt Modules

创建一个Hilt模块(例如DatabaseModule),用于提供AppDatabase实例。另外,可以创建另一个模块(例如RepositoryModule)来提供UserRepository实例。

// DatabaseModule.kt
@Module
@InstallIn(SingletonComponent::class)
object DatabaseModule {

    @Singleton
    @Provides
    fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
        return Room.databaseBuilder(context, AppDatabase::class.java, "app_database.db")
            .build()
    }

    @Singleton
    @Provides
    fun provideUserDao(database: AppDatabase): UserDao {
        return database.userDao()
    }
}

// RepositoryModule.kt
@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {

    @Singleton
    @Provides
    fun provideUserRepository(userDao: UserDao): UserRepository {
        return UserRepository(userDao)
    }
}

六:Hilt化ViewModel

使用@HiltViewModel注解标记ViewModel类(例如UserViewModel),并在其中注入所需的UserRepository

// UserViewModel.kt
@HiltViewModel
class UserViewModel @Inject constructor(private val userRepository: UserRepository) : ViewModel() {

    fun updateUser(user: UserEntity) {
        viewModelScope.launch {
            userRepository.updateUser(user)
            // 更新成功后,可以在此处触发LiveData或Flow等通知UI更新
        }
    }
}

七:在Activity/Fragment/Compose中使用ViewModel

在使用Hilt的Activity或Fragment或者Compse中,通过by viewModels()委托属性获取注入的UserViewModel实例,并调用其updateUser方法来更新Room中的数据。

class MainActivity : ComponentActivity() {

    private val userViewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                Index(userViewModel)
            }
        }
    }
}
@Composable
fun Index(userViewModel: UserViewModel) {
    var username: String by remember { mutableStateOf("") }
    var email: String by remember { mutableStateOf("") }

    Column {
        OutlinedTextField(value = username, onValueChange = {
            username = it
            userViewModel.updateUser(UserEntity(1, username, email))
        })
        OutlinedTextField(value = email, onValueChange = {
            email = it
            userViewModel.updateUser(UserEntity(1, username, email))
        })
    }
}

至此,您已经完成了在Android应用中使用Hilt依赖注入来更新Room数据库中数据的所有步骤。当updateUser方法在ViewModel中被调用时,Hilt会确保所有依赖(如UserRepositoryUserDao)已正确注入,并通过它们与Room数据库进行交互。

Jetpack Compose 上传相册图片到服务器

一、定义组件

定义一个Composable组件,当点击Composable组件中的按钮时候,就跳转到相册选择,然后选中图片上传。

import android.net.Uri
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import coil.compose.rememberAsyncImagePainter
import com.google.gson.JsonObject
import com.xiangzhenming.taojin.network.ImageUploadNetwork
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.Call
import retrofit2.Response
	
	
	
	@Composable
    fun UploadImageView() {
		val context = LocalContext.current
        val contentResolver = context.contentResolver
		var imageUri by remember { mutableStateOf<Uri?>(null) }
		val launcher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
		  if (uri != null) {
            imageUri = uri
			contentResolver.openInputStream(uri).use { inputStream ->
			
			
				// 创建RequestBody对象
				val requestBody = inputStream?.let {
					it.readBytes().toRequestBody("image/jpeg".toMediaTypeOrNull())
				}
				
				 // 创建MultipartBody.Part对象
				val part = requestBody?.let {
					MultipartBody.Part.createFormData(
						"file", // API接口要求的参数名
						uri.lastPathSegment ?: "${System.currentTimeMillis()}.jpg", // 文件名,默认使用URI的最后一段作为文件名
						it
					)
				}
				
				
				// 假设apiService是你的Retrofit服务实例
				if (part != null) {
					ImageUploadNetwork("").apiService.uploadImage(part)
						.enqueue(object : retrofit2.Callback<JsonObject> {

							override fun onResponse(call: Call<JsonObject>, response: Response<JsonObject>) {
								Log.e("UploadImage", "onResponse: ${response.body().toString()}:")
								if (response.isSuccessful) {
									Log.e("UploadImage", "onResponse: success:")
								} else {
									Log.e("UploadImage", "onResponse: fail${response.code()}")
								}
							}

							override fun onFailure(call: Call<JsonObject>, t: Throwable) {
								Log.e("UploadImage", "onFailure: ${t.message}")
							}
						}
					)
				}
					
				
			}
		  }
		}
		
		
		
		Column(
            Modifier
                .fillMaxSize()
                .background(Color.White),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        )
        {
            Button(onClick = {
                launcher.launch("image/*")
            }) {
                Text(text = "选择照片")
            }
            Image(painter = rememberAsyncImagePainter(model = imageUri), contentDescription = null)
        }
		
		
}

上传接口

interface ImageUploadService {

    companion object {
        const val BASE_URL = "https://www.jishuge.cn/"
        const val PREFIX_URL = "/upload/android"
    }

    @Multipart
    @POST("${PREFIX_URL}/reward/upload/image")
    fun uploadImage( @Part file: MultipartBody.Part): Call<JsonObject>


}
class ImageUploadNetwork {


    val apiService: ImageUploadService by lazy {
        initRetrofit()
    }

    private val gson: Gson = GsonBuilder().setLenient().create()

    private fun initRetrofit(): ImageUploadService {

        val okHttpClient = OkHttpClient.Builder()
            .addInterceptor(LoggingInterceptor())
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .retryOnConnectionFailure(false)
            .build()

        val retrofit = Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create(gson))
            .baseUrl(ImageUploadService.BASE_URL)
            .client(okHttpClient)
            .build()

        return retrofit.create(ImageUploadService::class.java)
    }
}
class LoggingInterceptor : Interceptor {
    @Volatile
    var level: Level = Level.BASIC // 设置默认的日志级别

    enum class Level {
        BASIC, // 只打印基本信息
        HEADERS, // 打印基本信息以及请求/响应头信息
        BODY // 打印所有信息(基本信息、请求/响应头及请求/响应体)
    }

    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()

        when (level) {
            Level.BASIC -> logBasic(request)
            Level.HEADERS -> logHeaders(request)
            Level.BODY -> logBodyAndHeaders(request, chain)
        }

        val response = chain.proceed(request)

        when (level) {
            Level.BASIC -> logBasic(response)
            Level.HEADERS -> logHeaders(response)
            Level.BODY -> logBodyAndHeaders(response)
        }

        return response
    }

    private fun logBasic(request: Request) {
        Log.d("LoggingInterceptor", "Sending request ${request.url}")
    }

    private fun logHeaders(request: Request) {
        Log.d("LoggingInterceptor", "Sending request ${request.url}")
        Log.d("LoggingInterceptor", "Request Headers: ${request.headers}")
    }

    private fun logBodyAndHeaders(request: Request, chain: Interceptor.Chain) {
        logHeaders(request)
        val requestBody = request.body ?: return
        val bodyString = requestBody.toString()
        Log.d("LoggingInterceptor", "Request Body: $bodyString")
    }

    private fun logBasic(response: Response) {
        Log.d("LoggingInterceptor", "Received response for ${response.request.url} with code ${response.code}")
    }

    private fun logHeaders(response: Response) {
        Log.d("LoggingInterceptor", "Received response for ${response.request.url} with code ${response.code}")
        Log.d("LoggingInterceptor", "Response Headers: ${response.headers}")
    }

    private fun logBodyAndHeaders(response: Response) {
        logHeaders(response)
        if (response.body != null) {
            val bodyString = response.body?.string()
            Log.d("LoggingInterceptor", "Response Body: $bodyString")
        }
    }
}

服务器端返回的值格式:

{
    "code": 0,
    "msg": "ok",
    "data": "https://www.jishuge.cn/image/2024-03-13/b69935ce-912e-4d31-b1e5-a15a3da85f82-1710318431942.jpg",
    "count": 0,
    "version": "1.0.0",
    "timestamp": 1710318433
}

监听ViewModel中的LiveData数据提示Live data observe in compose。

原因是因为我们的依赖没有引入,只要引入依赖就可以解决该问题:

implementation("androidx.compose.runtime:runtime-livedata:1.5.4")

然后在Activity或者Compose中使用。

var titleValue: String by remember {
	mutableStateOf(myModel.titleValue.value ?: "")
}

myModel.titleValue.observeAsState().value?.let { newData ->
	titleValue= newData
}

AndroidView网页加载

    @Composable
    fun SecondOnePage(): Unit {
        var progression by remember {
            mutableFloatStateOf(0f)
        }
        Surface {
            Column {
                //进度组件
                LinearProgressIndicator(
                    progress = progression / 100,
                    modifier = Modifier.fillMaxWidth(),
                    color = Color.Red
                )
                //利用AndroidView加载网页
                AndroidView(
                    factory = { context ->
                        var webView = WebView(context)
                        webView.apply {
                            settings.javaScriptEnabled = true
                            webViewClient = object : WebViewClient() {}
                            loadUrl("https://m.jd.com/")
                        }

                    },
                    modifier = Modifier.fillMaxSize(),
                    update = { webview ->
                        //监听加载进度。
                        webview.webChromeClient = object :WebChromeClient(){
                            override fun onProgressChanged(view: WebView?, newProgress: Int) {
                                progression = newProgress.toFloat()
                                super.onProgressChanged(view, newProgress)
                            }
                        }
                    }
                )
            }
        }
    }

Android Jetpack Compose开发中经常导入的依赖(可能持续更新)

    //网络请求
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("com.squareup.retrofit2:converter-gson:2.9.0")
    implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
    implementation("com.squareup.okhttp3:okhttp:4.9.0")
    implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")

    //fastjson2解析json格式数据。
    implementation("com.alibaba.fastjson2:fastjson2:2.0.42")

    //ViewModel依赖
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
    //liveData依赖
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
    //lifecycle依赖
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")

    //添加协程
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")


    //room,如果kapt,需要在上面插件处导入    id("kotlin-kapt")
    implementation("androidx.room:room-runtime:2.6.1")
    annotationProcessor("androidx.room:room-compiler:2.6.1")
    implementation("androidx.room:room-ktx:2.6.1")
    kapt("androidx.room:room-compiler:2.6.1")


    //相机
    implementation("androidx.camera:camera-core:1.2.2")
    implementation("androidx.camera:camera-camera2:1.2.2")
    implementation("androidx.camera:camera-lifecycle:1.2.2")
    implementation("androidx.camera:camera-video:1.2.2")
    implementation("androidx.camera:camera-view:1.2.2")
    implementation("androidx.camera:camera-extensions:1.2.2")

    //navigation-kotlin
    implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
    implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
    //navigation-compose
    implementation("androidx.navigation:navigation-compose:2.5.3")

    //workManager
    implementation("androidx.work:work-runtime-ktx:2.9.0")

JetPack Room 入门使用完整示例

具体数据可以参考官方文档:https://developer.android.com/codelabs/kotlin-android-training-room-database?hl=zh-cn#0

第一步:导入依赖

1、插件

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("kotlin-kapt")
}

2、依赖

implementation("androidx.room:room-runtime:2.6.1")
annotationProcessor("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
kapt("androidx.room:room-compiler:2.6.1")
版本问题(可能存在)
    compileOptions {
        //JavaVersion.VERSION_1_8这个版本可能不兼容,根据环境的版本来更换
        sourceCompatibility = JavaVersion.VERSION_17 
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = "17"
    }

二、定义实体类

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(

    @PrimaryKey(autoGenerate = true)
    var nightId: Long = 0L,

    @ColumnInfo(name = "start_time_milli")
    val startTimeMilli: Long = System.currentTimeMillis(),

    @ColumnInfo(name = "end_time_milli")
    var endTimeMilli: Long = startTimeMilli,

    @ColumnInfo(name = "quality_rating")
    var sleepQuality: Int = -1
)

三、定义Dao访问类

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update

@Dao
interface SleepDatabaseDao {

    @Insert
    fun insert(night: SleepNight)

    @Update
    fun update(night: SleepNight)

    @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
    fun get(key: Long): SleepNight?


    @Query("DELETE FROM daily_sleep_quality_table")
    fun clear()

    @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
    fun getTonight(): SleepNight?

    @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
    fun getAllNights(): LiveData<List<SleepNight>>

四、定义DataBase操作类

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

    abstract val sleepDatabaseDao: SleepDatabaseDao

    companion object {

        @Volatile
        private var INSTANCE: SleepDatabase? = null

        fun getInstance(context: Context): SleepDatabase {
            synchronized(this) {
                var instance = INSTANCE

                if (instance == null) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        SleepDatabase::class.java,
                        "sleep_history_database"
                    )
                        .fallbackToDestructiveMigration()
                        .build()
                    INSTANCE = instance
                }
                return instance
            }
        }
    }
}

五、使用(必须在子线程中或者协程中使用)

在项目中我们一般是结合LiveData和ViewModel一起使用。

class MainActivity2 : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            MyApplicationTheme {
                GetMessage(this)
            }
        }
    }
}

@Composable
fun GetMessage(mainActivity2: MainActivity2) {
    Column {
        Button(onClick = {
            GlobalScope.launch{
                try {
                    SleepDatabase.getInstance(mainActivity2)
                        .sleepDatabaseDao
                        .insert(SleepNight())
                }catch (e:Exception){
                    Log.e("Xiang","出现了错误:${e.message}")
                }
            }
        }) {
            Text(text = "添加数据")
        }
    }
}

六、利用Android Studio工具查看

打开菜单【view】->【Tool Windows】->【App Inspection】

Retrofit框架的简单使用(下)

一、Post提交

在ApiService接口中定义Post请求,其他代码书接上回 Retrofit框架的简单使用(上)

 /**
     * Post提交方式,请求参数goodsId=xxx
     * @FormUrlEncoded :表单形式提交
     */
    @POST("goods/relative")
    @FormUrlEncoded //表单形式提交
    fun testPost2(@Field("goodsId") goodsId: String): Call<String>


    /**
     * 参数为map形式。请求参数: password=xxx&username=xxxxx
     */
    @POST("goods/relative")
    @FormUrlEncoded //表单形式提交
    fun testPost3(@FieldMap mapData: Map<String,String>): Call<String>


    /**
     * (@Body data:HashMap<String,String>):这里不一定是String ,你可以传入具体类型,比如数据库表的实体类。
     * ,要想传入的参数是Json格式的话,需要修改请求格式,
     * 把ScalarsConverterFactory.create() 替换成 GsonConverterFactory.create()
     *     var retrofit = Retrofit.Builder()
     *         .baseUrl(ApiService.BASE_URL)
     *         .addConverterFactory(GsonConverterFactory.create()) //替换处
     *         .client(okHttpClient)
     *         .build()
     */
    @POST("goods/relative")
    fun testPost(@Body data:HashMap<String,String>): Call<String>