从0到1打造一款安卓app之6-安卓技术选型(将会持续更新)
以kotlin为基础
1.jetpack全家桶
1.AppStartup
用于应用初始化,初始化时可以访问到context: Context
自定义一个类,继承Initializer
class StartupInitializer :Initializer<String>{
override fun create(context: Context): String {
Log.d(StartupInitializer::class.java.simpleName,"create")
return "test"
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
Log.d(StartupInitializer::class.java.simpleName,"dependencies")
//被依赖的先执行,再执行自定义的StartupInitializer create
return mutableListOf()
}
}
复制代码
AndroidManifest.xml里配置StartupInitializer,tools:node="remove"禁用自定义的Initializer自动初始化
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge"
tools:replace="android:authorities" />
<meta-data
android:name="base.module.android.StartupInitializer"
android:value="androidx.startup"
tools:node="remove"/>
复制代码
手动调用自定义的Initializer初始化
val result = AppInitializer.getInstance(this).initializeComponent(StartupInitializer::class.java)
复制代码
2.WorkManager
work组件定义了一个WorkManagerInitializer,WorkManager 会默认初始化
public final class WorkManagerInitializer implements Initializer<WorkManager> {
private static final String TAG = Logger.tagWithPrefix("WrkMgrInitializer");
@NonNull
@Override
public WorkManager create(@NonNull Context context) {
// Initialize WorkManager with the default configuration.
Logger.get().debug(TAG, "Initializing WorkManager with default configuration.");
WorkManager.initialize(context, new Configuration.Builder().build());
return WorkManager.getInstance(context);
}
@NonNull
@Override
public List<Class<? extends androidx.startup.Initializer<?>>> dependencies() {
return Collections.emptyList();
}
}
复制代码
Application 实现 Configuration.Provider
import androidx.work.Configuration
class MyApplication() : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setMinimumLoggingLevel(android.util.Log.INFO)
.build()
}
复制代码
如果不想要work组件自带的WorkManagerInitializer
移除默认的初始化
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge"
tools:replace="android:authorities">
...其他自定义的初始化器
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
复制代码
手动添加到自定义的初始化器依赖下
class StartupInitializer : Initializer<String> {
override fun create(context: Context): String {
return "test"
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
return mutableListOf(
xxyyzz::class.java,
WorkManagerInitializer::class.java,
xxx::class.java,
)
}
}
复制代码
3.lifecycle生命周期感知
感知整个application生命周期示例
class ProcessLifecycleObserver :DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
}
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
}
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
}
}
复制代码
class BaseApplication : Application() {
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get().lifecycle.addObserver(ProcessLifecycleObserver())
}
}
复制代码
2.Square公司系列
1. Okhttpt
Okhttp初始化与AppStartUp,因为要访问context: Context
//后面有需要时,直接globalOkHttpClient.newBuilder()创建一个新的build来自定义新的配置
lateinit var globalOkHttpClient: OkHttpClient
class OkHttpClientInitializer : Initializer<OkHttpClient> {
override fun create(context: Context): OkHttpClient {
Log.d(
OkHttpClientInitializer::class.java.simpleName,
"context.externalCacheDir:${context.externalCacheDir}"
)
Log.d(
OkHttpClientInitializer::class.java.simpleName,
"context.externalCacheDir exists:${context.externalCacheDir?.exists()}"
)
Log.d(
OkHttpClientInitializer::class.java.simpleName,
"context.filesDir:${context.filesDir}"
)
globalOkHttpClient = OkHttpClient.Builder()
//配置响应内容缓存
.cache(
Cache(
directory = context.externalCacheDir ?: context.filesDir,
maxSize = 100 * 1024L * 1024L
)
)
//配置超时
.callTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
//添加默认请求拦截器
.addInterceptor(HttpLoggingInterceptor())
.build()
return globalOkHttpClient
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
return mutableListOf()
}
}
复制代码
2. retrofit
//后面使用时,.newBuilder()创建新的Builder,重新自定义一个配置
// globalRetrofit.newBuilder()
// .baseUrl("https://www.baidu.com/")
// .build()
// .create(BaiduService::class.java)
lateinit var globalRetrofit: Retrofit
class RetrofitInitializer : Initializer<Unit> {
override fun create(context: Context):Unit {
val moshi = Moshi.Builder().build()
val gson: Gson = GsonBuilder().create()
val moshiConverterFactory: MoshiConverterFactory = MoshiConverterFactory.create(moshi)
globalRetrofit = Retrofit.Builder()
.baseUrl("https://x.y.z")
.client(globalOkHttpClient)
.addConverterFactory(moshiConverterFactory)
.addConverterFactory(ScalarsConverterFactory.create())
.build()
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
return mutableListOf()
}
}
复制代码
3.1 retrofit与kotlin协程配合使用
retrofit新版本自带支持kotlin协程,只需要在定义请求时,用suspend
修饰fun,即不是普通的fun,而是 suspend fun
@POST("{keyword}")
@Headers("Content-Type: application/json")
suspend fun addData(@Path("keyword") keyword: String, @Body data: String): Any
复制代码
3.2定义一个全局可以使用的协程作用域
val handler = CoroutineExceptionHandler { _, exception ->
LogUtils.e("CoroutineExceptionHandler", "CoroutineExceptionHandler get: $exception")
}
val mainScope = MainScope()
fun runOnAsyncScope(block: suspend CoroutineScope.() -> Unit) = mainScope.launch(Dispatchers.Default + handler) {
block.invoke(this)
}
复制代码
3. moshi json解释库
简单使用示使
定义适配器工厂类和JsonAdapter类
import com.squareup.moshi.*
import java.lang.reflect.Type
import java.util.*
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class DateLong
class DateLongJsonAdapterFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
if (type != Date::class.java) return null
Types.nextAnnotations(annotations, DateLong::class.java) ?: return null
return DateLongAdapter().nullSafe()
}
private class DateLongAdapter : JsonAdapter<Date>() {
override fun fromJson(reader: JsonReader): Date {
return Date(reader.nextLong())
}
override fun toJson(writer: JsonWriter, value: Date?) {
writer.value(value?.time ?: 0)
}
}
}
复制代码
定义数据类,并添加注解
@JsonClass(generateAdapter = true)
open class BaseObject(
open var createdAt: String = TimeUtils.date2String(Date()),
open var updatedAt: String = TimeUtils.date2String(Date()),
)
@JsonClass(generateAdapter = true)
data class FinalObject(
@DateLong var createdAtTime: Date = Date(),
@DateLong var updatedAtTime: Date = Date(),
) : BaseObject()
复制代码
简单使用
val globalMoshi = Moshi.Builder()
.add(DateLongJsonAdapterFactory())
.build()
val adapter = globalMoshi.adapter(FinalObject::class.java)
val str = adapter.toJson(FinalObject())
复制代码
4. leakcanary内存泄漏检测库
引入依赖
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
}
复制代码
查看应用日志LeakCanary是否正常工作
D LeakCanary: LeakCanary is running and ready to detect leaks
如果显示 ,则需要处理一下,
[Can't use LeakCanary because of org.junit.Test class dependency in external libraries]
复制代码
//在app主模块下,添加下面三行配置
configurations {
debugImplementation.exclude group: "junit", module: "junit"
}
dependencies {
}
复制代码
3.腾讯公司
bugly
Android SDK 使用指南 - Bugly 文档 (qq.com)
添加依赖
dependencies {
implementation 'com.tencent.bugly:crashreport:4.0.4'
}
复制代码
初始化
class CrashReportInitialzer : Initializer<Unit> {
override fun create(context: Context) {
CrashReport.initCrashReport(context, "AppId", false)
}
override fun dependencies(): MutableList<Class<out Initializer<*>>> {
return mutableListOf()
}
}
复制代码
测试
CrashReport.testJavaCrash()
腾讯浏览服务
4.日志记录与工具类
AndroidUtilCode 集成了各种各样的工具类和日志功能,这一个类库可以打日志打印到控制台,也可以把日志/崩溃记录写入到文件,但唯一不足的是,写入到文件的日志,只能是使用类里提供的LogUtils
类打印的,系统或者第三方库的Log日志则会记录不到,所以可以使用 (Gradle + ASM)修改字节码的方案。