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…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...
【HarmonyOS 5】鸿蒙中Stage模型与FA模型详解
一、前言 在HarmonyOS 5的应用开发模型中,featureAbility是旧版FA模型(Feature Ability)的用法,Stage模型已采用全新的应用架构,推荐使用组件化的上下文获取方式,而非依赖featureAbility。 FA大概是API7之…...

