当前位置:网站首页>Android kotlin uses arouter componentized routing and datastore to save data instead of SharedPreferences
Android kotlin uses arouter componentized routing and datastore to save data instead of SharedPreferences
2022-07-21 08:38:00 【Vaccae】
Learn better people ,
Be a better person .
——《 Smart enjoyment of wechat 》
The length of this paper is 6237 word , Expected reading 11 minute
Preface
Now? Android Development App Basically, we have started to use component-based architecture , To achieve cross component Activity Jump , You need to use routing , In this article, I would like to introduce Kotlin Use in ARouter Implement componentized architecture , Then I joined in DataStore To replace SharePreference Save local data , complete Demo Will also post the address at the end of the article , Combine the front 《Android MVI A preliminary study of the structure 》 And 《Android Use LiveEventBus Messages enable communication between components 》 You can try to make a simple Android Of App framework .
Smart enjoyment of wechat
About ARouter Use
In fact, about ARouter Use , There should be many introductions on the Internet , I'm just talking about a few in Kotlin Precautions for use in , And the functions I personally think will be used .
01
Project framework
02
gradle Add dependencies and configuration
First, in the gradle.properties Add android.enableJetifier=true
Next is the project build.gradle Add arouter-register, Notice the image in the following figure buildscripts To be added plugins front ,apply plugin: 'com.alibaba.arouter' Also in plugins Back , If it is not set like this, an error will occur .
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
dependencies {
classpath "com.alibaba:arouter-register:1.0.2"
}
}
plugins {
id 'com.android.application' version '7.2.1' apply false
id 'com.android.library' version '7.2.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
}
apply plugin: 'com.alibaba.arouter'
task clean(type: Delete) {
delete rootProject.buildDir
}
Then each Module Of build.gradle All of them join in arouter References to
libbase As a general component , So I joined in datastore Dependence , other Module Because they all need to be introduced libbase So don't add datastore Dependence
Complete code , It's used here libbase Of
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
android {
compileSdk 32
defaultConfig {
minSdk 22
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
// Add dependency
implementation 'com.alibaba:arouter-api:1.5.2'
kapt 'com.alibaba:arouter-compiler:1.5.2'
//JepPack DataStore
implementation 'androidx.datastore:datastore-preferences:1.0.0'
implementation 'androidx.datastore:datastore-preferences-core:1.0.0'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
03
stay BaseApp In the initialization ARouter
stay libbase Create a Application, The name for BaseApp
BaseApp Add initialization ARouter, Remember to release when exiting .
And then in app Of manifest Medium application Add android:name, Point to libbase Of BaseApp
BaseApp Complete code
package pers.vaccae.libbase
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import com.alibaba.android.arouter.BuildConfig
import com.alibaba.android.arouter.launcher.ARouter
/**
* author :Vaccae
* mailbox :[email protected]
* Creation time :15:26
* Function module description :
*/
class BaseApp : Application() {
companion object {
@SuppressLint("StaticFieldLeak")
lateinit var mContext: Context
@SuppressLint("StaticFieldLeak")
lateinit var instance: BaseApp
}
override fun onCreate() {
super.onCreate()
instance = this
mContext = this
InitARouter(instance)
}
private fun InitARouter(baseApp: Application) {
// These two lines must be written in init Before , Otherwise, these configurations are in init The process will not work
// Pay attention to yourself here ARouter Also have BuildConfig, It's used here Android Of BuildConfig, If the reference is wrong, it is invalid
if (pers.vaccae.libbase.BuildConfig.DEBUG) {
ARouter.openLog(); // Print log
ARouter.openDebug(); // Turn on debugging mode ( If in InstantRun Run in mode , Debug mode must be turned on ! Online version needs to be closed , Otherwise, there is a safety risk )
}
ARouter.init(baseApp); // As early as possible , Recommended in the Application In the initialization
}
override fun onTerminate() {
super.onTerminate()
ARouter.getInstance().destroy()
}
}
04
Basic use
The above steps are to complete the basic configuration , The next step is how to realize ARouter The jump
As can be seen from the above figures , stay Activity Add... To the front of @Route The label of , Jump position that can be specified , The path here needs to be noted that there are at least two levels ,/xx/xx, And different Module The tag names of the middle level cannot be the same , But the same Module There can be more than one middle level tag name , Suggest that each Module It is best to use the same primary label .
Initiate routing operations
// 1. Simple jump in application ( adopt URL The jump is in ' Advanced usage ' in )
ARouter.getInstance().build("/test/activity").navigation();
// 2. Jump and carry parameters
ARouter.getInstance().build("/test/1")
.withLong("key1", 666L)
.withString("key3", "888")
.withObject("key4", new Test("Jack", "Rose"))
.navigation();
The above code is simple to initiate routing operation , The first one has no parameters , therefore build The path and Activity If the label path in is consistent, you can jump .
Focus on the second kind with parameters
The two pictures above are Module2 and Module1 Jump to... Respectively Module3 Of Activity, The parameter passed in is String Type own current Activity name , Then take a look Module3 in Actvity How to handle the receiving parameters .
@Route(path = "/M3/activity")
class M3Activity : AppCompatActivity() {
private lateinit var binding: ActivityM3Binding
@JvmField
@Autowired(name = "parent")
var parent: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityM3Binding.inflate(layoutInflater)
setContentView(binding.root)
ARouter.getInstance().inject(this)
binding.btnm2.setOnClickListener {
ARouter.getInstance().build("/M2/activity").navigation();
}
binding.textView.append("\r\nparent:${parent}")
}
}
From different Module Open in Module3 Of Activity According to the effect
Module1 Open in Module3 Show
Module2 Open in Module3 Show
05
Use of interceptors
ARouter In addition to the above basic jump , Interceptors may also be used , The most commonly used interceptor is mainly to judge whether the login status , If you log in, you can open Activity, So this Demo Let's put Module2 Set to open only after login , At this point, the interceptor will be used , Next, let's talk about the use of interceptors .
// The classic application is to handle the landing event in the jump process , So you don't need to repeat the login check on the target page
// The interceptor will execute between jumps , Multiple interceptors execute in order of priority
@Interceptor(priority = 8, name = " Test interceptors ")
public class TestInterceptor implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
...
callback.onContinue(postcard); // Processing is complete , Return control
// callback.onInterrupt(new RuntimeException(" I think it's a little unusual ")); // I think there is a problem , Interrupt the routing process
// At least one of the above two needs to be called , Otherwise, the route will not continue
}
@Override
public void init(Context context) {
// Initialization of interceptor , Will be in sdk Call this method when initializing , Will only be called once
}
}
It's an official introduction , It can be seen that , The key is to declare a IInterceptor, stay process Function to implement the interception process , First, in the libbase Create a new interceptor in
complete ARouterInterceptor Code
package pers.vaccae.libbase
import android.content.Context
import com.alibaba.android.arouter.facade.Postcard
import com.alibaba.android.arouter.facade.annotation.Interceptor
import com.alibaba.android.arouter.facade.callback.InterceptorCallback
import com.alibaba.android.arouter.facade.template.IInterceptor
/**
* author :Vaccae
* mailbox :[email protected]
* Creation time : 13:10
* Function module description :
*/
@Interceptor(priority = 1, name = " Landing interception ")
class ARouterInterceptor : IInterceptor {
override fun init(context: Context?) {
}
override fun process(postcard: Postcard?, callback: InterceptorCallback?) {
callback?.let {
postcard?.let { post ->
if(post.group.equals("M2")) {
val isLogin = DataStoreHelper.getData("login", false)
if (!isLogin) {
val msg = " The user is not logged in , Unable to enter the current page !"
post.tag = msg
it.onInterrupt(RuntimeException(msg))
}
}
it.onContinue(post)
}
}
}
}
The code is also quite simple , Mainly two points :
Class with comments @Interceptor, among priority Is the priority ,name Is the name. .
postcard Medium judgement Module Is your group M2( If only judge a single Activity, You can change it post.group Change it to post.name To achieve ), If it is M2 To judge whether it is login status , My login status in the code is used DataStore preservation , So here directly through the acquisition DataStore To determine whether to log in , When the login is successful, execute directly onContinue that will do , Unsuccessful use onInterrupt Throw one Exception come out .
Activity Launch the route judgment callback method
binding.btnm2.setOnClickListener {
ARouter.getInstance().build("/M2/activity").navigation(this,
object : NavigationCallback {
override fun onFound(postcard: Postcard?) {
}
override fun onLost(postcard: Postcard?) {
}
override fun onArrival(postcard: Postcard?) {
}
override fun onInterrupt(postcard: Postcard?) {
postcard?.let {
showMsg(it.tag.toString())
}
}
});
}
stay NavigationCallback Directly rewrite... In onInterrupt, As mentioned above, throw Exception here postcard Of tag You can get , The implementation effect is as follows :
Click to display Module2 An exception prompt is displayed after , If you have logged in, jump directly to Module2 Of Activity in
Smart enjoyment of wechat
About DataStore Use
The singleton defined here mentioned above BaseApp in , Just to give dataStore Use
DataStore Need to be in Application In the initialization DataStore<Preferences>, With BaseApp Single case , In the new Helper Class to get DataStore<Preferences>
adopt BassApp.dataStore Asynchronous write
private suspend fun putString(key: String, value: String) = ds.edit {
it[stringPreferencesKey(key)] = value
}
adopt BassApp.dataStore.map To read
private fun getString(key: String, default: String = ""): String =
runBlocking {
[email protected] ds.data.map {
it[stringPreferencesKey(key)] ?: default
}.first()
}
DataStore Wrapper class :
package pers.vaccae.libbase
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
/**
* author :Vaccae
* mailbox :[email protected]
* Creation time :11:28
* Function module description :
*/
object DataStoreHelper {
private val BaseApp.dataStore: DataStore<Preferences>
by preferencesDataStore(name = BaseApp.mContext.packageName)
private val ds = BaseApp.instance.dataStore
// Store the data
fun <T> putData(key: String, value: T) {
runBlocking {
when (value) {
is String -> putString(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Float -> putFloat(key, value)
is Double -> putDouble(key, value)
is Boolean -> putBoolean(key, value)
else -> throw Exception("DataStore Unknown data type was entered when writing ")
}
}
}
// Reading data
fun <T> getData(key: String, default: T): T {
val resdata = when (default) {
is String -> getString(key, default)
is Int -> getInt(key, default)
is Long -> getLong(key, default)
is Float -> getFloat(key, default)
is Double -> getData(key, default)
is Boolean -> getBoolean(key, default)
else -> throw Exception("DataStore Unknown data type entered while reading ")
}
return resdata as T
}
//region write in DataStore data
// write in String Type data
private suspend fun putString(key: String, value: String) = ds.edit {
it[stringPreferencesKey(key)] = value
}
// write in Int Type data
private suspend fun putInt(key: String, value: Int) = ds.edit {
it[intPreferencesKey(key)] = value
}
// write in Long Type data
private suspend fun putLong(key: String, value: Long) = ds.edit {
it[longPreferencesKey(key)] = value
}
// write in Float Type data
private suspend fun putFloat(key: String, value: Float) = ds.edit {
it[floatPreferencesKey(key)] = value
}
// write in Double Type data
private suspend fun putDouble(key: String, value: Double) = ds.edit {
it[doublePreferencesKey(key)] = value
}
// write in Boolean Type data
private suspend fun putBoolean(key: String, value: Boolean) = ds.edit {
it[booleanPreferencesKey(key)] = value
}
//endregion
//region Read DataStore data
// Read String Type data
private fun getString(key: String, default: String = ""): String =
runBlocking {
[email protected] ds.data.map {
it[stringPreferencesKey(key)] ?: default
}.first()
}
// Read Int Type data
private fun getInt(key: String, default: Int = -1): Int =
runBlocking {
[email protected] ds.data.map {
it[intPreferencesKey(key)] ?: default
}.first()
}
// Read Long Type data
private fun getLong(key: String, default: Long = -1): Long =
runBlocking {
[email protected] ds.data.map {
it[longPreferencesKey(key)] ?: default
}.first()
}
// Read Float Type data
private fun getFloat(key: String, default: Float = 0.0f): Float =
runBlocking {
[email protected] ds.data.map {
it[floatPreferencesKey(key)] ?: default
}.first()
}
// Read Double Type data
private fun getFloat(key: String, default: Double = 0.00): Double =
runBlocking {
[email protected] ds.data.map {
it[doublePreferencesKey(key)] ?: default
}.first()
}
// Read Boolean Type data
private fun getBoolean(key: String, default: Boolean = false): Boolean =
runBlocking {
[email protected] ds.data.map {
it[booleanPreferencesKey(key)] ?: default
}.first()
}
//endregion
}
Source code address
https://github.com/Vaccae/ARouterDemo.git
Click to read the original text and you can see “ Code cloud ” The address of
End
Past highlights
Android Local Sqlite Database backup and restore
边栏推荐
猜你喜欢
业务出海,灵感乍现前要先「把手弄脏」
How many months did you write your first SCI?
Examples illustrate the division basis of code segment (.Text), data segment (.Data), BSS segment, read-only data segment (.Rodata) and stack
懒到骨子里了,我在CSDN写文章都懒得自己写了,基于selenium模拟写文章
Yu Meimei, Ji Gongdu
罗敏成不了董宇辉
Double shutter Technology
IOT standard system framework
十年烧光1300亿,垂直电商终将走进历史尘埃
If you can't understand the character code, hit me! (ASCII,Unicode,Utf-8,GB2312…)
随机推荐
The replay block of canoe still needs to be combined with CAPL script to make it clear
SAP Spartacus - Progressive Web Applications,渐进式 Web 应用程序
从零开始C语言精讲篇1:初识C语言
自定义类型
你第一篇SCI写了几个月?
Yu Meimei, Ji Gongdu
在CANoe中通过Panel面板控制Test Module 运行(高级)
Leetcode 322 coin change, 找零钱的最小张数
CAPL script printing functions write, writeex, writelineex, writetolog, writetologex, writedbglevel do you really know which one to use under what circumstances?
MySQL基礎(多錶查詢、事務)
Learning canoe from scratch (16) -- graphics
How to write the docker cleaning cache script
Matlab基本语法(一)
行业现状令人失望,工作之后我又回到UC伯克利读博了
CANoe下載地址以及CAN Demo 16的下載與激活,並附錄所有CANoe軟件版本
CANoe仿真功能之自动化序列(Automation Sequences )
Android Kotlin使用ARouter组件化路由及DataStore替代SharedPreferences保存数据
CANoe下载地址以及CAN Demo 16的下载与激活,并附录所有CANoe软件版本
阿里面试Redis常考问题,你略知多少?
你怎么看CV-Transformer【秋招面经分享】