Android compose 的基本环境搭建
1.创建项目
导入版本
1.gradle/libs.versions.toml
[versions]
accompanistPermissions = "0.36.0"
agp = "8.5.0-beta01"
coilCompose = "2.7.0"
constraintlayoutComposeVersion = "1.0.1"
hiltAndroid = "2.51.1"
hiltNavigationCompose = "1.2.0"
kotlin = "1.9.25"
coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
activityCompose = "1.9.2"
composeBom = "2024.09.01"
kotlinxSerializationJson = "1.6.3"
lifecycleViewmodelKtx = "2.8.5"
composeRuntime = "1.7.1"
lingver = "1.3.0"
loggingInterceptorVersion = "4.12.0"
navigationCompose = "2.8.0"
retrofit = "2.11.0"
roomRuntime = "2.6.1"
rxandroid = "2.1.1"
rxandroidVersion = "3.0.1"
rxjava = "2.2.21"
rxjava3 = "3.1.9"
rxlifecycle = "3.1.0"[libraries]androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
#region viewmodel livedata compose
androidx-lifecycle-extensions = { module = "androidx.lifecycle:lifecycle-extensions", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleViewmodelKtx" }
androidx-lifecycle-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "lifecycleViewmodelKtx" }
constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "constraintlayoutComposeVersion" }
runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata", version.ref = "composeRuntime" }
androidx-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "composeRuntime" }
androidx-runtime-rxjava2 = { module = "androidx.compose.runtime:runtime-rxjava2" }
#endregion compose#region kotlin serialization
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
#endregion#region hilt
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroid" }
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
#endregion#region navigation
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
#endregion#region retrofit2 and okhttp3
okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptorVersion" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
converter-scalars = { module = "com.squareup.retrofit2:converter-scalars", version.ref = "retrofit" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }#endregion okhttp3#region rxjava 2
adapter-rxjava2 = { module = "com.squareup.retrofit2:adapter-rxjava2", version.ref = "retrofit" }
rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroid" }
rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" }
#endregion#region rxjava 3
rxjava3-rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroidVersion" }
rxjava3-rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava3" }
adapter-rxjava3 = { module = "com.squareup.retrofit2:adapter-rxjava3", version.ref = "retrofit" }
rxlifecycle = { module = "com.trello.rxlifecycle3:rxlifecycle", version.ref = "rxlifecycle" }
rxlifecycle-android-lifecycle-kotlin = { module = "com.trello.rxlifecycle3:rxlifecycle-android-lifecycle-kotlin", version.ref = "rxlifecycle" }
rxlifecycle-components = { module = "com.trello.rxlifecycle3:rxlifecycle-components", version.ref = "rxlifecycle" }#endregion#region room
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }#endregion#region coil
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
#endregion#region accompanist
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
#endregion#region 国际化
lingver = { module = "com.github.YarikSOffice:lingver", version.ref = "lingver" }
#endregionjunit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
我整理好的版本,都用过了适配
2.模块目录下的 build.gradle.kts
plugins {alias(libs.plugins.android.application)alias(libs.plugins.jetbrains.kotlin.android)id("kotlin-kapt")id("com.google.dagger.hilt.android")kotlin("plugin.serialization")id("androidx.room")
}android {namespace = "com.composeapp"compileSdk = 34defaultConfig {applicationId = "com.composeapp"minSdk = 24targetSdk = 34versionCode = 1versionName = "1.0"testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"vectorDrawables {useSupportLibrary = true}}room {schemaDirectory("$projectDir/schemas")}buildTypes {release {isMinifyEnabled = falseproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),"proguard-rules.pro")}}compileOptions {sourceCompatibility = JavaVersion.VERSION_1_8targetCompatibility = JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = "1.8"}buildFeatures {compose = true}composeOptions {kotlinCompilerExtensionVersion = "1.5.15"}packaging {resources {excludes += "/META-INF/{AL2.0,LGPL2.1}"}}}dependencies {implementation(libs.androidx.core.ktx)implementation(libs.androidx.activity.compose)implementation(platform(libs.androidx.compose.bom))implementation(libs.androidx.ui)implementation(libs.androidx.ui.graphics)implementation(libs.androidx.ui.tooling.preview)implementation(libs.androidx.material3)testImplementation(libs.junit)androidTestImplementation(libs.androidx.junit)androidTestImplementation(libs.androidx.espresso.core)androidTestImplementation(platform(libs.androidx.compose.bom))androidTestImplementation(libs.androidx.ui.test.junit4)debugImplementation(libs.androidx.ui.tooling)debugImplementation(libs.androidx.ui.test.manifest)implementation(libs.kotlinx.serialization.json)//region ViewModel Livedata compose runtimeimplementation(libs.androidx.lifecycle.viewmodel.ktx)implementation(libs.androidx.lifecycle.viewmodel.compose)implementation(libs.androidx.lifecycle.livedata.ktx)implementation(libs.androidx.lifecycle.runtime.ktx)implementation(libs.androidx.lifecycle.lifecycle.runtime.compose)implementation(libs.androidx.lifecycle.viewmodel.savedstate)implementation(libs.androidx.runtime)implementation(libs.runtime.livedata)//endregion// region hiltimplementation(libs.hilt.android)kapt(libs.hilt.android.compiler)implementation(libs.androidx.hilt.navigation.compose)// endregion//region navigationimplementation(libs.androidx.navigation.compose)//endregion navigation//region retrofit2 okhttp//Retrofit 核心库implementation(libs.retrofit)//响应数据自动序列化//JSONimplementation(libs.converter.gson)//String类型implementation(libs.converter.scalars)//拦截器 loggingimplementation(libs.okhttp3.logging.interceptor)//endregion//region rxjava2
// implementation(libs.androidx.runtime.rxjava2)
// implementation(libs.adapter.rxjava2)
// implementation(libs.rxjava)
// implementation(libs.rxandroid)//endregion//region rxjava3implementation(libs.rxjava3.rxjava)implementation(libs.adapter.rxjava3)implementation(libs.rxlifecycle)implementation(libs.rxlifecycle.android.lifecycle.kotlin)implementation(libs.rxlifecycle.components)implementation(libs.rxjava3.rxandroid)//endregion//region roomimplementation(libs.androidx.room.runtime)annotationProcessor(libs.androidx.room.compiler)kapt(libs.androidx.room.compiler)//endregion//region coil 异步网络图片加载implementation(libs.coil.compose)//endregion//region accompanistimplementation(libs.accompanist.permissions)//endregion//region 国际化implementation(libs.lingver)// endregion//region compose 约束布局implementation (libs.constraintlayout.compose)//endregion}
3.工程目录下的 build.gradle.kts
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {alias(libs.plugins.android.application) apply falsealias(libs.plugins.jetbrains.kotlin.android) apply falseid("com.google.dagger.hilt.android") version "2.51.1" apply falsekotlin("kapt") version "1.9.25" apply falsekotlin("jvm") version "1.9.25" apply false // or kotlin("multiplatform") or any other kotlin pluginkotlin("plugin.serialization") version "1.9.25" apply falseval room_version = "2.6.1"id("androidx.room") version room_version apply false
}
4.工程目录下setting.gradle.kts
pluginManagement {repositories {google {content {includeGroupByRegex("com\\.android.*")includeGroupByRegex("com\\.google.*")includeGroupByRegex("androidx.*")}}mavenCentral()gradlePluginPortal()}
}
dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()mavenCentral()maven("https://jitpack.io")}
}rootProject.name = "ComposeApp"
include(":app")
2.搭建Hilt环境
1.创建Application
@HiltAndroidApp
class MyApplication : Application() {companion object {const val SharedPreferencesFileName = "MyApplication";private lateinit var application: Application;lateinit var sharedPreferences: SharedPreferencesfun getApplication(): Application {return application;}}@Overrideoverride fun onCreate() {super.onCreate()
// Log.i("测试","app启动了 child="+child)application = thissharedPreferences =getSharedPreferences(SharedPreferencesFileName, Context.MODE_PRIVATE);/*** 国际化绑定 已经帮存入 sharedPreferences里了*/Lingver.init(this)}}
加上注解 @HiltAndroidApp
2.在用到注入的Activity上加上注解
@AndroidEntryPoint
class MainActivity : ComponentActivity() {/*** 将依赖注入好的viewModel放入这里*/private val viewModel: XianPageViewModel by viewModels()@Injectlateinit var child: Child;override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)enableEdgeToEdge()setContent {val sharedPreferences = MyApplication.sharedPreferencesLog.i("share", "这是shared 全局 $sharedPreferences")// sharedPreferences.edit().putString("token", "123456").apply()//关闭 导航状态栏// 隐藏状态栏if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {window.insetsController?.hide(WindowInsets.Type.statusBars())} else {@Suppress("DEPRECATION")window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN}this.lifecycleScopeval string = sharedPreferences.getString("token", "")Log.i("share", "获取token 全局 $string")ComposeAppTheme {
// ScaffoldExample()
// MyApp()
// NamePageRxjava3()
// NamePage()
// RoomTestPage()
// NfcPage()
// NfcMain()
// ImageIconPage()
// PermissionPage()// LiveDataTest()
// LanguageSelector()LanguageSelectorKuangjia()}}// Log.i("测试","activity启动了 this="+this)
// Log.i("测试","activity启动了 child="+child.activity)
// Log.i("测试","activity启动了 child="+child.application)Log.i("测试", "activity启动了 viewModel=" + viewModel)}}
3.测试注入
/*** 第一种: 相当于spring @Component*/
@ActivityScoped
class Child @Inject constructor(@ActivityContext val activity: Context,@ApplicationContext val application: Context){val name = "你好"
// val applicationContext = application}
3.注入网络模块
1.单例类
/*** 全局唯一网络模块*/
@Module
@InstallIn(SingletonComponent::class)
object NetModel {private const val Tag = "Retrofit:";private const val URL = "http://192.168.202.57:8080";@Singleton@Providesfun provideOkHttpClient(tokenInterceptor: TokenInterceptor): OkHttpClient {//构建日志拦截器val httpLoggingInterceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {override fun log(message: String) {Log.i(Tag, message)}}).setLevel(HttpLoggingInterceptor.Level.BODY)//构建return OkHttpClient.Builder().connectTimeout(5, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).addInterceptor(tokenInterceptor).addInterceptor(httpLoggingInterceptor).build()}@RxJava2Inject@Singleton@Providesfun provideRetrofitRxJava2(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(URL).client(okHttpClient)// .addConverterFactory(ScalarsConverterFactory.create())//添加Gson转换器.addConverterFactory(GsonConverterFactory.create())//添加Gson转换器//添加Rxjava适配
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()}@RxJava3Inject@Singleton@Providesfun provideRetrofitRxJava3(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(URL).client(okHttpClient).addCallAdapterFactory(RxJava3CallAdapterFactory.create())
// .addConverterFactory(ScalarsConverterFactory.create())//添加Gson转换器.addConverterFactory(GsonConverterFactory.create())//添加Gson转换器//添加Rxjava适配.build()}@Singleton@Providesfun provideMainTestService(@RxJava3Inject retrofit: Retrofit): MainTestService {return retrofit.create(MainTestService::class.java)}}
2.Token拦截器
@Singleton
class TokenInterceptor @Inject constructor() : Interceptor {override fun intercept(chain: Interceptor.Chain): Response {val request = chain.request()val token: String = MyApplication.sharedPreferences.getString("token", "2001").toString()val url = request.url.newBuilder().addQueryParameter("token1", token).build()val header = request.headers.newBuilder().add("token2", token).build()val requestNew = request.newBuilder().url(url).headers(header).build()return chain.proceed(requestNew);}
}
3.要是注入两个相同类型的

创建自定义注解
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class RxJava2Inject
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class RxJava3Inject
在构造的时候声明
@RxJava2Inject@Singleton@Providesfun provideRetrofitRxJava2(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(URL).client(okHttpClient)// .addConverterFactory(ScalarsConverterFactory.create())//添加Gson转换器.addConverterFactory(GsonConverterFactory.create())//添加Gson转换器//添加Rxjava适配
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()}@RxJava3Inject@Singleton@Providesfun provideRetrofitRxJava3(okHttpClient: OkHttpClient): Retrofit {return Retrofit.Builder().baseUrl(URL).client(okHttpClient).addCallAdapterFactory(RxJava3CallAdapterFactory.create())
// .addConverterFactory(ScalarsConverterFactory.create())//添加Gson转换器.addConverterFactory(GsonConverterFactory.create())//添加Gson转换器//添加Rxjava适配.build()}
注入的时候声明注入哪个
@Singleton@Providesfun provideMainTestService(@RxJava3Inject retrofit: Retrofit): MainTestService {return retrofit.create(MainTestService::class.java)}
4.声明接口
interface MainTestService {// @GET("/phone/gis/test")
// suspend fun getTestData(): Response<String>@GET("/phone/gis/test")suspend fun getTestDataCall(): Call<TestDto>@GET("/phone/gis/test")suspend fun getTestData(): Response<TestDto>@GET("/phone/gis/test/{name}")suspend fun getTestDataTruth(@Path("name") name: String, @Query("age") age: Int): TestDto/*** 使用Rxjava 不允许加 suspend 关键字*/@GET("/phone/gis/test/{name}")fun getTestDataRx(@Path("name") name: String, @Query("age") age: Int): Observable<TestDto>}
RxJava写 接口,千万不要声明suspend ,要不然序列化JSON 会报错,无法实例化 Observable
5.要是发送http请求还需要权限

network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config><base-config cleartextTrafficPermitted="true" />
</network-security-config>
6.网络权限
<uses-permission android:name="android.permission.INTERNET"/>
4.ViewModel
/*** viewmodel不能设置作用域,否则会报错** ViewModel 部分中提到的 viewModel() 函数会自动使用 Hilt 通过 @HiltViewModel 注解构建的 ViewModel*/
@HiltViewModel
class ScopePageViewModel @Inject constructor():ViewModel() {init {val job: Job = viewModelScope.launch { }//可以单独取消}}
然后注入就可以了
5.LiveData
@HiltViewModel
class LiveDataTestViewModel @Inject constructor() : ViewModel() {val items:MutableLiveData<MutableList<Item>> = MutableLiveData(mutableListOf())init {val item1 = Item("测试模块1",true)items.value?.add(item1)val item2 = Item("测试模块2",true)items.value?.add(item2)}fun updateItem(item:Item){item.updateIsShow(!item.isShow.value!!)}}
用法
@Composable
fun LiveDataTest(viewModel: LiveDataTestViewModel = hiltViewModel()) {val items = viewModel.items.observeAsState(mutableListOf())LazyColumn(modifier = Modifier.fillMaxSize().padding(30.dp)) {item {Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround) {val width = Modifier.width(150.dp).height(50.dp)Button(onClick = {viewModel.updateItem(items.value[0])}, modifier = width) {Text(text = stringResource(R.string._1))}Button(onClick = { viewModel.updateItem(items.value[1]) }, modifier = width) {Text(text = "隐藏或打开测试模块2")}}}items(items.value) {ItemComponent(item = it)}}}
6.整合navigation
@Serializable
data class Profile(val name: String)@Serializable
object FriendsList@Serializable
object ProfileScreenChild2@Serializable
data class ProfileScreenChild1(val param: String)@Serializable
data class Aaa(val param: String)// Define the ProfileScreen composable.
@Composable
fun ProfileScreen(profile: Profile,parentViewModel: XianPageViewModel?,viewModel: XianPageViewModel = hiltViewModel(),onNavigateToFriendsList: () -> Unit) {// ProfileScreen启动了com.composeapp.ui.view.XianPageViewModel@841f9de这是viewmodelLog.i("测试", "ProfileScreen启动了" + viewModel.toString() + "这是viewmodel")Log.i("测试","ProfileScreen启动了 这是父级的viewModel实例" + parentViewModel?.toString() + "这是父类viewmodel")Column {Text("导航参数: ${profile.name}")ProfileScreenChild()Button(onClick = { onNavigateToFriendsList() }) {Text("Go to Friends List")}}
}@Composable
fun ProfileScreenChild(viewModel: XianPageViewModel = hiltViewModel()) {
// ProfileScreenChild启动了com.composeapp.ui.view.XianPageViewModel@841f9de这是viewmodelLog.i("测试", "ProfileScreenChild启动了" + viewModel.toString() + "这是viewmodel") //在同一个导航中是一致的
// Text("子组件的值: ${viewModel.data.value}")val childNavController = rememberNavController()NavHost(childNavController, startDestination = ProfileScreenChild1("你好测试子组件Pchild1")) {composable<ProfileScreenChild2> { backStackEntry ->//专门用来拿到当前导航的堆栈里的ViewModelval parentEntry = remember(backStackEntry) {//必须类型和值都一样才能找到,否则抛异常childNavController.getBackStackEntry(ProfileScreenChild1("你好测试子组件Pchild1"))//必须值要一样才能找到,值不一致就找不到,跟序列化JSON比}
// viewmodel 当组件移除组件树的时候,将会被销毁val parentViewModel = hiltViewModel<XianPageViewModel>(parentEntry)ProfileScreenChild2(parentViewModel)}composable<ProfileScreenChild1> { backStackEntry ->val child1 = backStackEntry.toRoute<ProfileScreenChild1>()ProfileScreenChild1(profileScreenChild1 = child1) {childNavController.navigate(ProfileScreenChild2)}}}}@Composable
fun ProfileScreenChild2(parentViewModel: XianPageViewModel,viewModel: XianPageViewModel = hiltViewModel()
) {Log.i("测试","ProfileScreenChild2启动了" + parentViewModel.toString() + "这是parent viewmodel") //在同一个导航中是一致的Log.i("测试","ProfileScreenChild2启动了" + viewModel.toString() + "这是viewmodel") //在同一个导航中是一致的Log.i("测试","ProfileScreenChild2启动了" + (viewModel === parentViewModel) + "这是viewmodel") //在同一个导航中是一致的Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {Button(onClick = { /*TODO*/ }) {Text(text = "ProfileScreenChild2")}}
}@Composable
fun ProfileScreenChild1(viewModel: XianPageViewModel = hiltViewModel(),profileScreenChild1: ProfileScreenChild1,toProChild2: () -> Unit
) {Log.i("测试","ProfileScreenChild1启动了" + viewModel.toString() + "这是viewmodel 这是param${profileScreenChild1.param}") //在同一个导航中是一致的Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {Button(onClick = { toProChild2() }) {Text(text = "ProfileScreenChild1")}}}// Define the FriendsListScreen composable.
@Composable
fun FriendsListScreen(onNavigateToProfile: (String) -> Unit,viewModel: XianPageViewModel = hiltViewModel()
) {Log.i("测试", "FriendsListScreen启动了" + viewModel.toString() + "这是viewmodel")val (text, setText) = remember { mutableStateOf("") }Column {Text("Friends List")TextField(value = text, onValueChange = setText)Button(onClick = { onNavigateToProfile(text) }) {Text("Go to Profile")}}
}// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp(viewModel: XianPageViewModel = viewModel()) {Log.i("测试", "MyApp启动了 viewmodel=$viewModel")//是Activity同一个val navController = rememberNavController()NavHost(navController, startDestination = FriendsList) {composable<Profile> { backStackEntry ->
// 获取路由对象实例val profile: Profile = backStackEntry.toRoute()Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {ProfileScreen(profile = profile,parentViewModel = null,onNavigateToFriendsList = {navController.navigate(route = FriendsList)})}}composable<FriendsList> {Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {FriendsListScreen(onNavigateToProfile = { it ->navController.navigate(route = Profile(name = "传入了新的参数:${it}"))})}}}
}
7.整合room
1.实体类
@Entity
@Serializable
data class User(@PrimaryKey val uid:String,@ColumnInfo(name = "name")val name:String,val time:Long,val date:String?=null,val age :Int
) {
}
2.dao
@Dao
interface UserDao {@Query("SELECT * FROM user")fun getAll(): List<User>@Query("SELECT * FROM user WHERE uid = :userId")fun loadAllByIds(userId: String): List<User>@Query("SELECT * FROM user WHERE name LIKE :name")fun findByName(name: String): List<User>@Insertfun insertAll(vararg users: User)@Deletefun delete(user: User)}
3.数据库
@Database(entities = [User::class], version = 3, exportSchema = true)
abstract class AppDataBase: RoomDatabase() {companion object{private const val DB_NAME = "my_app.db"fun getInstance(context: Context): AppDataBase{val db = Room.databaseBuilder(context,AppDataBase::class.java, DB_NAME).addMigrations(MIGRATION_1_2,MIGRATION_2_3).build()return db;}val MIGRATION_1_2 = object : Migration(1, 2) {override fun migrate(db: SupportSQLiteDatabase) {// 执行 SQL 语句来迁移数据库db.execSQL("ALTER TABLE user ADD COLUMN date TEXT")}}val MIGRATION_2_3 = object : Migration(2, 3) {override fun migrate(db: SupportSQLiteDatabase) {// 执行 SQL 语句来迁移数据库db.execSQL("ALTER TABLE user ADD COLUMN age INTEGER NOT NULL default 1")}}}/*** 数据库当中的一张表*/abstract fun UserDao():UserDao}
4.Dao实例注入
/*** 全局唯一数据库模块*/
@Module
@InstallIn(SingletonComponent::class)
object DataBaseModel {@Singleton@Providesfun provideAppDataBase(@ApplicationContext context:Context): AppDataBase {return AppDataBase.getInstance(context)}@Singleton@Providesfun provideUserDao(appDataBase: AppDataBase): UserDao {return appDataBase.UserDao()}}
8.Permission
@SuppressLint("CheckResult")
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun PermissionPage(){// Camera permission stateval cameraPermissionState = rememberPermissionState(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)Observable.fromCallable {Log.i("执行","执行在"+Thread.currentThread().name)"true"}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe({Log.i("执行","执行成功$it "+Thread.currentThread().name)},{Log.i("执行","执行失败"+it.message)})Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){if (cameraPermissionState.status.isGranted) {Text("相机权限授予成功")} else {Column {val textToShow = if (cameraPermissionState.status.shouldShowRationale) {// If the user has denied the permission but the rationale can be shown,// then gently explain why the app requires this permission"相机权限是核心. Please grant the permission."} else {// If it's the first time the user lands on this feature, or the user// doesn't want to be asked again for this permission, explain that the// permission is required"必须给予相机权限. " +"Please grant the permission"}Text(textToShow)Button(onClick = { cameraPermissionState.launchPermissionRequest() }) {Text("Request permission")}}}}}
9.Kotlin json 序列化
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json@Serializable
data class Project(val name: String, val language: String)fun main() {// Serializing objectsval data = Project("kotlinx.serialization", "Kotlin")val string = Json.encodeToString(data)println(string) // {"name":"kotlinx.serialization","language":"Kotlin"}// Deserializing back into objectsval obj = Json.decodeFromString<Project>(string)println(obj) // Project(name=kotlinx.serialization, language=Kotlin)
}
10.kotlin 冷流
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlockingfun main() {runBlocking {val coldFlow = flow {for (i in 1..5){delay(3000)emit(i)}}// 订阅者 1launch {coldFlow.collect { value ->println("Subscriber 1 received: $value")}}// 订阅者 2launch {delay(2000) // 延迟订阅,确保第二个订阅者在第一个订阅者之后coldFlow.collect { value ->println("Subscriber 2 received${Thread.currentThread().name}: $value")}}println("执行")}}
11.kotlin 热流
import androidx.compose.runtime.collectAsState
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlockingfun main() {runBlocking {// 创建一个 StateFlow 实例,初始状态为 0val stateFlow = MutableStateFlow(0)// 启动一个协程来更新 StateFlow 的状态launch {repeat(5) {delay(500) // 模拟数据生成stateFlow.value += 1}}// 订阅 StateFlow 并打印最新状态launch {stateFlow.collect { value ->println("Subscriber received: $value")}}
}}
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlockingfun main() {runBlocking {// 创建一个 SharedFlowval sharedFlow = MutableSharedFlow<Int>()// 启动一个协程来发射数据launch {repeat(10) {delay(100) // 模拟异步操作sharedFlow.emit(it) // 发射数据}}// 订阅 SharedFlowlaunch {sharedFlow.collect { value ->println("Subscriber 1 received: $value")}}// 另一个订阅者launch {sharedFlow.collect { value ->println("Subscriber 2 received: $value")}}}
}
12.全局语言切换
在 Application created 方法
/*** 国际化绑定 已经帮存入 sharedPreferences里了*/Lingver.init(this)
@Composable
fun LanguageSelectorKuangjia(localeViewmodel: LocaleViewmodel= hiltViewModel()) {// val locale = MyApplication.sharedPreferences.getString("locale",null)
//
// val localeState = localeViewmodel.locale.observeAsState(if (locale == null) Locale.getDefault() else Locale(locale)
// locale?.let { Locale(it) }?:Locale.getDefault()
// )val current = LocalContext.currentColumn(modifier = Modifier.fillMaxSize().padding(30.dp)) {// Example buttons to switch languagesRow {Button(onClick = {Lingver.getInstance().setLocale(current,Locale.SIMPLIFIED_CHINESE)(current as Activity).recreate()}) {Text("中文")}Button(onClick = {Lingver.getInstance().setLocale(current,Locale.ENGLISH)(current as Activity).recreate()}) {Text("英文")}}// Apply the selected language
// LanguageSwitcherKuangjia(localeState.value) {// Your UI content here, which will reflect the selected languageText(text = stringResource(id = R.string._1))
// }}}
自己写的扩展函数
/*** 转变语言扩展函数 会保留在当前的Navigation 导航里*/
fun Context.changeLocale(locale: Locale) {Lingver.getInstance().setLocale(this,locale)(this as Activity).recreate()
}
在 导航中 重创建,也会跟随导航目的地,不会重置
13.使用 阿里矢量图
iconfont-阿里巴巴矢量图标库

1.存一个地方

2.下载这个插件
3.右键一个空文件夹

4.操作
14.文件分享
1.文件提供者
<providerandroid:authorities="${applicationId}.fileprovider"android:name="androidx.core.content.FileProvider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /></provider>

2. 外部存储空间权限文件
file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths><external-path name="external_files" path="."/>
</paths>

3. MediaStore
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;public class ExportMedia {public static Uri saveExcelFileToMediaStoreInExternalDownloadExport(Context context, String fileName) {// 创建 ContentValues 对象ContentValues values = new ContentValues();values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);values.put(MediaStore.MediaColumns.MIME_TYPE, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS+"/EXPORT"); // 下载目录// 插入文件到 MediaStorereturn context.getContentResolver().insert(MediaStore.Files.getContentUri("external"), values);}public Uri queryExcelFiles(Context context, String fileName) {String[] projection = {MediaStore.Files.FileColumns._ID,MediaStore.Files.FileColumns.DISPLAY_NAME,MediaStore.Files.FileColumns.MIME_TYPE};String selection = MediaStore.Files.FileColumns.MIME_TYPE + "=?";String[] selectionArgs = {"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}; // .xlsx 的 MIME 类型Cursor cursor = context.getContentResolver().query(MediaStore.Files.getContentUri("external"),projection,selection,selectionArgs,null);try {while (cursor.moveToNext()) {long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID));String displayName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME));if (displayName.equals(fileName)){// 处理查询结果,例如分享文件return ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id);}}} catch (IllegalArgumentException e) {e.printStackTrace();}finally {if (cursor != null){cursor.close();}}return null;}
}
public class ShareUtils {public static void shareExcel(Context context,String path){// 文件路径,确保文件存在File fileToShare = new File(path);// 创建分享意图Intent shareIntent = new Intent(Intent.ACTION_SEND);shareIntent.setType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // MIME 类型// 使用 FileProvider 获取 content URIUri fileUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileprovider", fileToShare);shareIntent.putExtra(Intent.EXTRA_STREAM, fileUri);// 允许临时读取 URI 权限shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 启动分享对话框context.startActivity(Intent.createChooser(shareIntent, "分享一个Excel"));}public static void shareExcel(Context context,Uri uri){// 创建分享意图Intent shareIntent = new Intent(Intent.ACTION_SEND);shareIntent.setType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // MIME 类型// 使用 FileProvider 获取 content URIshareIntent.putExtra(Intent.EXTRA_STREAM, uri);// 允许临时读取 URI 权限shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// 启动分享对话框context.startActivity(Intent.createChooser(shareIntent, "分享一个Excel"));}
}
<!--在sdcard中创建/删除文件的权限 --><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" tools:ignore="ProtectedPermissions" /><uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"tools:ignore="ScopedStorage" />
15. NFC
1.开启权限
<uses-permission android:name="android.permission.NFC" />
2.创建工具类
import android.nfc.tech.NfcA
import java.security.MessageDigestobject NfcHelper {private fun readUid(nfcA: NfcA): ByteArray {val command = byteArrayOf(xxxx.toByte(), xxxxx.toByte())val uid = nfcA.transceive(command)return byteArrayOf(uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7])}/*** 处理密码** @param nfcA* @return*/private fun handlePwd(nfcA: NfcA): ByteArray {val uidByte = readUid(nfcA).copyOf(7)val secret = byteArrayOf(xxxxxxxxxxxxxxxxxx)//两个数组重载运算符 拼接在一起val byteUnique = uidByte + secret//加密SHA -256val digest = MessageDigest.getInstance("SHA-256")// 更新 MessageDigest 实例,传入要哈希的数据digest.update(byteUnique)// 计算哈希值并返回 取前四位val pwdBytes = digest.digest().copyOf(4)return pwdBytes}/*** 新nfc芯片验证密码** @param nfcA* @return*/fun authNew(nfcA: NfcA) {val pwd = handlePwd(nfcA)//拼接两个数组val command = byteArrayOf(0x1B.toByte()) + pwdnfcA.transceive(command)}/*** 新nfc 芯片读方法** @param nfcA* @param startPage* @param endPage* @return*/fun readTag(nfcA: NfcA, startPage: Int, endPage: Int): List<Byte> {val list = arrayListOf<Byte>()for (i in startPage..endPage) {val data = nfcA.transceive(byteArrayOf(0x30, i.toByte()))list.addAll(data.asList().subList(0,4))}return list;}/*** 新nfc 芯片写方法** @param nfcA* @param writeByte* @param block 扇区,也就是页*/fun writeTag(nfcA: NfcA, writeByte: ByteArray, page: Int) {val cmd = byteArrayOf(0xA2.toByte(), page.toByte()) + writeBytenfcA.transceive(cmd)}}
3.Model
/*** 全局唯一NFC模块*/
@Module
@InstallIn(SingletonComponent::class)
object NfcModel {@Singleton@Providesfun provideNfcAdapter(@ApplicationContext context: Context): NfcAdapter {return NfcAdapter.getDefaultAdapter(context)}}
4.viewModel
import android.nfc.NfcAdapter
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.scopes.ActivityScoped
import javax.inject.Inject/*** viewmodel不能设置作用域,否则会报错*/
@HiltViewModel
class NfcViewModel @Inject constructor(val nfcAdapter: NfcAdapter):ViewModel() {// 使用 MutableLiveData 来持有数据}
5.页面
@Composable
fun NfcMain(){var open by remember { mutableStateOf(false) }Column(Modifier.fillMaxSize()) {Box(modifier = Modifier.fillMaxWidth().height(100.dp), contentAlignment = Alignment.Center){Button(onClick = { open = !open }) {Text(text = if (open) "点击关闭NFC" else "点击开启NFC")}}if (open){NfcPage()}else{Box(modifier = Modifier.fillMaxSize().background(Color.Blue), contentAlignment = Alignment.Center){Text(text = "NFC未开启")}}}}@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NfcPage(modifier: Modifier = Modifier, nfcViewModel: NfcViewModel = hiltViewModel()) {var presses by remember { mutableIntStateOf(0) }val readOrWrite = remember {mutableStateOf(false)}val (text, setText) = remember { mutableStateOf("") }val nfcAdapter = nfcViewModel.nfcAdapterval list = remember {mutableStateListOf<String>()}val context = LocalContext.currentLaunchedEffect(key1 = Unit) {val pendingIntent = PendingIntent.getActivity(context, 0, Intent(MyApplication.getApplication(), context::class.java).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_IMMUTABLE)val filters = arrayOf(IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED))val arrayOf = arrayOf(arrayOf<String>(NfcA::class.java.name))nfcAdapter.enableReaderMode(context as Activity?, { tag ->val nfcA = NfcA.get(tag)Log.i("NfcA", "NfcA: $nfcA")nfcA.timeout = 3000try {nfcA.connect()Log.i("测试","nfc超时时间 ${nfcA.timeout} ")Log.i("测试","nfc最大长度 ${nfcA.maxTransceiveLength} ")NfcHelper.authNew(nfcA)// NewNfcHelper.authNew(nfcA)if (readOrWrite.value){val readTag = NfcHelper.writeTag(nfcA, byteArrayOf(2,3,4,1), 4)Log.i("测试","写入成功 $readTag")}else{val readTag = NfcHelper.readTag(nfcA, 4, 4)Log.i("测试","读取成功 $readTag")}} catch (e: Exception) {e.printStackTrace()} finally {nfcA.close()}}, NfcAdapter.FLAG_READER_NFC_A, null);nfcAdapter.enableForegroundDispatch(context, pendingIntent, filters, arrayOf)}DisposableEffect(key1 = Unit) {this.onDispose {nfcAdapter.disableForegroundDispatch(context as Activity)nfcAdapter.disableReaderMode(context as Activity)}}Scaffold(topBar = {TopAppBar(colors = topAppBarColors(containerColor = MaterialTheme.colorScheme.primaryContainer,titleContentColor = MaterialTheme.colorScheme.primary,),title = {Box(modifier = Modifier.fillMaxWidth(),contentAlignment = Alignment.Center // 设置 Box 的内容居中) {TextField(value = text, onValueChange = setText,modifier = Modifier.width(200.dp).height(50.dp),// 调整宽度// 调整高度textStyle = MaterialTheme.typography.bodyLarge.copy(fontSize = 16.sp,lineHeight = TextUnit(200f, TextUnitType.Sp)), // 调整字体大小) // 调整内边距}})},bottomBar = {BottomAppBar(containerColor = MaterialTheme.colorScheme.primaryContainer,contentColor = MaterialTheme.colorScheme.primary,) {Row(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),horizontalArrangement = Arrangement.spacedBy(space = 12.dp),verticalAlignment = Alignment.CenterVertically) {Button(modifier = Modifier.weight(1f), onClick = {}) {Text(text = "添加")}Button(modifier = Modifier.weight(1f), onClick = {readOrWrite.value = !readOrWrite.value}) {Text(text = if (readOrWrite.value) "写入" else "读取")}Button(modifier = Modifier.weight(1f), onClick = {list.clear()}) {Text(text = "清空")}}}},floatingActionButton = {FloatingActionButton(onClick = { presses++ }) {Icon(Icons.Default.Build, contentDescription = "Add")}}) { innerPadding ->LazyColumn(modifier = Modifier.padding(innerPadding)) {items(items = list, key = { it }) {Text(text = it,modifier = Modifier.fillMaxWidth().height(40.dp),textAlign = TextAlign.Center)}}}
}
相关文章:
Android compose 的基本环境搭建
1.创建项目 导入版本 1.gradle/libs.versions.toml [versions] accompanistPermissions "0.36.0" agp "8.5.0-beta01" coilCompose "2.7.0" constraintlayoutComposeVersion "1.0.1" hiltAndroid "2.51.1" hiltNavi…...
git | 合并 commit 的两种方法
比如你最近的 3 次提交分别为 A B C,你想将它们合并成 X。 方案一 使用 git rebase -i HEAD~3 进入编辑: pick 0148079 A pick 29cae72 B pick bf8572a C修改: r 0148079 A f 29cae72 B f bf8572a C:wq 保存进入 commit 编辑页面,输入 X …...
Grafana链接iframe嵌入Web前端一直跳登录页面的问题记录
概述 公司有个项目使用到Grafana作为监控界面,因为项目方的环境极其复杂,仅物理隔离的环境就有三四个,而且每个都得部署项目,今天在某个环境测试,查看界面遇到一个比较奇怪的Grafana问题,后面针对该问题进行跟踪分析并解决,故而博文记录,用于备忘。 问题 登录项目We…...
后端Java-SpringBoot整合MyBatisPlus步骤(超详细)
1.新建项目。 2.点击完上一步的next之后,选择pom.xml文件中的依赖。 3.点击pom文件进行项目初始化。 按照下面的俩步骤刷新一下maven ,让文件生效 4.新建一个application.yml文件 5. 新建一个数据库mp,在数据库中新建一张user表 6.连接数据…...
8609 哈夫曼树
### 思路 1. **选择最小权值节点**:在哈夫曼树构建过程中,选择两个权值最小且父节点为0的节点。 2. **构建哈夫曼树**:根据权值构建哈夫曼树,确保左子树权值小于右子树权值。 3. **生成哈夫曼编码**:从叶子节点到根节点…...
docker的harbor仓库登录问题
目录 一、问题描述 二、证书信任问题 三、DNS解析问题 四、解决 参考链接:Docker login Harbor报错解决:Error response from daemon: Get https:..-阿里云开发者社区 一、问题描述 问题: 挂机或者挂机重启之后harbor登录不上 查看日…...
ENV | docker 安装使用(简单实操版)
1. 详细步骤 1.1 安装 sudo apt update sudo apt install docker.io1.2 验证(可跳过) docker -v1.3 使用 1.3.1 拉取镜像 # 镜像源,如使用腾讯云服务器,可使用 https://mirror.ccs.tencentyun.com docker pull xxx1.3.2 运行…...
【Golang】深入解读Go语言中的错误(error)与异常(panic)
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
DMDSC更换DCR和VOTE磁盘
DMDSC更换DCR和VOTE磁盘 为了提高DMDSC集群运行速度和节点之间通信协调的效率,需要将运行在机械盘上的dcr和vote磁盘替换到SSD高效磁盘上。将原来200M的dcr和vote机械磁盘,换成500M的SSD高效磁盘。 磁盘替换规划信息如下所示: 信息说明 替…...
国产化框架PaddleYOLO结合Swanlab进行作物检测
1. 项目介绍 粮食安全,作为人类生存与发展的基石,始终是全球关注的焦点。它不仅仅关乎粮食的充足供应,更涉及粮食的质量安全、营养健康以及可持续生产等多个维度。在全球化、气候变化和资源环境约束日益加剧的背景下,如何确保粮食…...
Linux编译部署PHP环境
1.准备工作 安装前我们需要设置防护墙,开放端口,更新yum源 # 1.防火墙 systemctl status firewalld 看到active(running)就意味着防火墙打开了 systemctl stop firewalld 看到inactive(dead)就意味着防火墙关闭了 systemctl start fire…...
Win11禁止搜索栏查找互联网内容
禁止任务栏和开始菜单的搜索栏查找互联网内容的方法如下: 使用组策略:WinR键,或菜单框,输入gpedit.msc回车,启动本地组策略编辑器。使用左侧的边栏导航到“计算机配置”>“管理模板”>“Windows组件”>“搜索…...
dig和nmap的区别
dig和nmap是两种在网络管理和安全领域广泛使用的工具,它们在功能、用途和原理上存在显著差异。 dig 定义与功能: dig(Domain Information Groper)是一个用于查询DNS(域名系统)信息的命令行工具。它允许用…...
无人机飞手入伍当兵技术优势分析
随着现代战争形态的不断演变,无人机技术在军事领域的应用日益广泛,成为提升军队作战能力的重要手段。对于无人机飞手而言,其专业技能和实战经验在入伍当兵后能够转化为显著的技术优势,为国防事业贡献重要力量。以下是从专业技能优…...
[Everything] 文件搜索工具的下载及详细安装使用过程(附有下载文件)
快速搜索文件名及其所在路径 下载链接在文末 下载压缩包后解压 !!安装路径不要有中文 解压后得到文件 双击exe文件得到 选择简体中文,点击OK 点击“我接受” 更改安装目录,最好不要放在C盘,点击下一步 点击下一步 点…...
HIRI-ViT:使用高分辨率输入的视觉Transformer扩展
摘要 https://arxiv.org/pdf/2403.11999 视觉Transformer( V i T \mathrm{ViT} ViT)与卷积神经网络(CNN)的混合深度模型已成为视觉任务中一类强大的骨干网络。自然地,提高此类混合骨干网络的输入分辨率会增强模型容量…...
TI DSP TMS320F280025 Note15:串口SCI的使用
TMS320F280025 串口SCI的使用 ` 文章目录 TMS320F280025 串口SCI的使用框图分析串口特点可编程数据格式SCI端口中断非FIFO/FIFO模式下SCI中断的操作/配置UartDriver.cUartDriver.h串口时钟由PCLKCR7控制使能,默认位系统时钟4分频 串口接收与发送都可以触发中断 串口使用的引脚…...
[Bandzip] 文件解压工具的下载及详细安装使用过程(附有下载文件)
文件解压工具,避免解压出错,双击即可解压文件 下载链接在文末 下载压缩包后解压 !!安装路径不要有中文 解压得到文件 双击exe文件 同意并安装 安装完成后,点击关闭, 右键点击需要解压的压缩包࿰…...
微服务MongoDB解析部署使用全流程
目录 1、什么是MongoDB 1、非关系型数据库 2、非关系型数据库分类 3、MongoDB?bson格式什么样? 2、MongoDB的优势 3、MongoDB应用场景 4、术语 5、操作 1、安装MongoDB 1、查询镜像文件【不操作】 2、拉取镜像文件 3、创建数据挂载目录 4、启…...
string为什么存储在堆里
在 Java 中,字符串对象存储在堆内存中而不是栈内存中,这是由于 Java 的内存管理和对象生命周期的特性决定的。以下是详细解释: 1. Java 内存模型 Java 的内存模型主要分为以下几个部分: 堆(Heap)&#x…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...
解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...
2025-05-08-deepseek本地化部署
title: 2025-05-08-deepseek 本地化部署 tags: 深度学习 程序开发 2025-05-08-deepseek 本地化部署 参考博客 本地部署 DeepSeek:小白也能轻松搞定! 如何给本地部署的 DeepSeek 投喂数据,让他更懂你 [实验目的]:理解系统架构与原…...

