//网络请求
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")
作者: jishuge2099@gmail.com
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>
Retrofit框架的简单使用(上)
一,导入依赖
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")
在AndroidManifest.xml中记得申请网络权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
二,定义个请求接口
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Query
interface ApiService {
companion object {
const val BASE_URL = "https://api.sunofbeaches.com/shop/"
}
//最后组合成完整地址:https://api.sunofbeaches.com/shop/discovery/categories
@GET("discovery/categories")
//@Query注解是查询参数,如果多个参数就写多个注解,用逗号分开
fun test(@Query("test") str: String): Call<String>
}
三,开始使用
var TAG: String = "Xiang"
/**
* 创建拦截器
*/
var intercept = HttpLoggingInterceptor {
Log.e(TAG, "onHttpRetrofit: $it")
}.setLevel(HttpLoggingInterceptor.Level.BODY)
/**
* 添加拦截器
*/
var okHttpClient = OkHttpClient.Builder().addInterceptor(intercept).build()
var retrofit = Retrofit.Builder()
.baseUrl(ApiService.BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.client(okHttpClient)
.build()
/**
* 获取动态代理实例
*/
var apiService = retrofit.create(ApiService::class.java)
/**
* 异步请求,不用协程或者子线程。
*/
// apiService.test("100").enqueue(object : retrofit2.Callback<String> {
// override fun onResponse(
// call: retrofit2.Call<String>,
// response: retrofit2.Response<String>
// ) {
// /**
// * 请求成功
// */
// if (response.isSuccessful) {
// Log.e(TAG, "onResponse: ${response.body()}")
// }
// }
//
// override fun onFailure(call: retrofit2.Call<String>, t: Throwable) {
// //请求是吧
// Log.e(TAG, "onFailure: ${t.message}")
// }
//
// })
/**
* 同步请求,必须用协程或者线程去执行,不然会阻塞UI(主)线程
*/
GlobalScope.async {
var response = apiService.test("100").execute();
if (response.isSuccessful) {
Log.e(TAG, "onResponse: ${response.body()}")
} else {
Log.e(TAG, "onFailure: ${response.message()}")
}
}
关于Post请求,请跳转 Retrofit框架的简单使用(下)
HttpOk3通过Kotlin 请求简单Demo
开启网络权限
需要在AndroidManifest.xml 的manifest下添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
第一步:引入依赖
implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
添加过滤器
import android.util.Log
import okhttp3.Interceptor
import okhttp3.Response
class LogIntercept : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
var url = request.url
Log.e("Xiang", "intercept: 这里是请求过滤器,请求地址:${url}")
return chain.proceed(request)
}
}
二:GET请求
GlobalScope.async {
var okHttpClient = OkHttpClient.Builder().addInterceptor(LogIntercept()).build()
var request: Request = Request.Builder()
.url("https://api.admin.com/shop/discovery/categories")
.build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
response.body?.let {
val dataBody = it.string()
Log.e("Xiang", dataBody)
}
}
}
})
}
三:form表单提交
var okHttpClient = OkHttpClient.Builder().addInterceptor(LogIntercept()).build()
var formBody = FormBody.Builder()
.add("AD_user","admin")
.add("AD_pass","888888")
.build()
var request: Request = Request.Builder()
.url("https://api.admin.com/admin/Chk_login.asp")
.post(formBody)
.build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.message?.let {
Log.e("Xiang", it.toString())
}
}
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
response.body?.let {
val dataBody = it.string()
Log.e("Xiang", dataBody)
}
}
}
})
四:JSON格式提交
var okHttpClient = OkHttpClient.Builder().addInterceptor(LogIntercept()).build()
var stringBody = "{\"username\":\"123456\",\"password\":\"123456789\"}"
.toRequestBody("application/json;charset=utf-8".toMediaType())
var request: Request = Request.Builder()
.url("https://auth.admin.com/login")
.post(stringBody)
.build()
LiveData和ViewModel联合使用,简单入门
我们得首先明白ViewModel只是一个记录业务逻辑和屏幕状态的容器,LiveData是一个可以被观察的数据集合。
第一步,创建容器
我们新建一个ViewModel容器类。比如MyViewModel
class MyViewModel : ViewModel() {}
第二步,在容器中创建LiveData可被观察的数据,比如一个String类型的数据。
class MyViewModel : ViewModel() {
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
}
第三步,在界面组件(Activity或Fragment)中去使用。
首先添加一个观察者,这样就可以实时的去关键currentName的变动。
//我们先在Activity中添加ViewModel
private val model: MyViewModel by viewModels()
//在组件中使用。
@Composable
fun GetMessage(model: MyViewModel, cotnent: ComponentActivity) {
var message by remember {
mutableStateOf("")
}
//建立一个观察者
var observer = Observer<String> {
message = it
}
//添加观察者,cotnent:Activity对象,observer观察者对象
model.currentName.observe(cotnent, observer)
Text(text = "内容:$message")
}
第四步,设置改变LiveData数据
model.currentName.setValue("这是更新的内容")
Lifecycle简单使用方法。
Lifecycle的组件作用就是解耦Activity的生命周期,没当Activity的生命周期改变之后通知Lifecycle观察者。
第一,导入Lifecycle依赖
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.1")
第二,建立一个观察者,实现DefaultLifecycleObserver接口
重写DefaultLifecycleObserver的生命周期函数,比如我们要观察Activity的onStart、onStop周期函数,那么就重写这两个函数。
import android.util.Log
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
class TestModel(var names:String):DefaultLifecycleObserver {
lateinit var name:String
init{
this.name = names
}
override fun onStart(owner: LifecycleOwner) {
Log.e("Xiang","执行了start $name")
}
override fun onStop(owner: LifecycleOwner) {
Log.e("Xiang","执行了stop $name")
}
}
第三,绑定到Activity去。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//添加观察者
lifecycle.addObserver(TestModel("kt"))
setContent {
Text(text = "hi Android")
}
}
}
- 当MainActivity 这个Activity的生命周期发生变化之后,我们可以在观察者TestModel中同步对应的方法。
Android开发在layout添加子目录方法(Kotlin版本)
如果把所有的布局文件和fragment文件都放在layout这一个目录下面,随着文件增多,越发越乱,那么我们在layout下再见几个文件对布局文件进行分组分类。
第一步 显示Android目录为Project状态,在 build.gradle.kts 文件中添加一下代码
android {
.......
sourceSets {
getByName("main") {
res {
srcDirs(
"src/main/res/layout/activity",
"src/main/res/layout/fragment",
"src/main/res"
)
}
}
}
}
那么对应的文件布局分为active和fragment,对应的文件目录格式如下
--res/layout
----res/layout/activity/layout
----res/layout/fragment/layout
xml布局文件可以放在res/layout、res/layout/activity/layout、res/layout/fragment/layout目录下,如果自己有其他文件分类,可以自行添加。
Vue子组件每次加载都执行Mounted周期函数
Vue框架为了提高性能,对组件进行了缓存,所以第二次加载相同的子组件的时候,是不会创建新的dom,而是使用缓存。
但是有时候我们需要每次加载子组件都执行一次Mounted函数来做一些数据的初始化或者请求等等,所以这是个麻烦事了。
vue组件通过key来识别组件是否缓存,那么我们可以在组件上面添加一个key值,每次请求的时候都不一样,这样就可以解决上面的问题(不考虑性能问题)。
父组件
<template> <div> <Com :key="randdomKey"></Com> </div> </template>
在每次要加载显示Com子组件的时候,就改变randdomKey的值即可。
WIN32创建模态和非模态弹窗框
第一步,在资源文件中创建弹出框资源。
新建两个弹出框资源,菜单分别为IDD_DIALOG1 和IDD_DIALOG2

二、建立一个顶级菜单,方便操作。
ID分别设置为 ID_MODEL(模态) 和 ID_NO_MODEL(非模态)

三、在.cpp中的窗口处理函数的添加WM_COMMAND的后续处理函数
.......
case WM_COMMAND: {
OnCommand(hwnd, wParam);
break;
.......
void OnCommand(HWND hwnd, WPARAM wParam) {
//点击不同菜单之后做不同的处理。
switch (LOWORD(wParam))
{
case ID_MODEL:{
DialogBoxA( g_hInstance, (char*)IDD_DIALOG1, hwnd, DlgProc); //创建模态
break;
}
case ID_NO_MODEL: {
//创建非模态
HWND hNoModelWnd = CreateDialog(g_hInstance, (char*)IDD_DIALOG2, hwnd, DlgNoModeProc);
//显示窗口(模态不需要)
ShowWindow(hNoModelWnd, SW_SHOW);
break;
}
default:
break;
}
}
INT_PTR DlgProc(HWND hwnd, UINT msgId, WPARAM wParam, LPARAM lParam) {
switch (msgId)
{
case WM_SYSCOMMAND:
{
//点击关闭弹出窗口的关闭按钮
if (wParam == SC_CLOSE) {
EndDialog(hwnd, TRUE); //模态必须要用EndDialog,因为这个函数既可以销毁窗口又能解除阻塞
}
break;
}
default:
break;
}
return FALSE;
}
//非模态
INT_PTR DlgNoModeProc(HWND hwnd, UINT msgId, WPARAM wParam, LPARAM lParam) {
switch (msgId)
{
case WM_SYSCOMMAND:
{
//点击关闭弹出窗口的关闭按钮
if (wParam == SC_CLOSE) {
DestroyWindow(hwnd); //只要销毁窗口,不需要解除窗口,所以不需要用EndDialog
}
break;
}
default:
break;
}
return FALSE;
}
当我们创建主窗口的时候系统会发送WM_CREATE消息,但是创建弹窗窗口的时候发送的不是WM_CREATE而是发送的WM_INITDIALOG,这是弹窗窗口和其他窗口不同之处,其他的和普通窗口处理没有差异。