Potato
Potato copied to clipboard
Android: Develop Tips
Android: Develop Tips
[TOC]
逐渐更新一些关于 Android 开发设计的一下小技巧
Multi-Module
多模块设计参考:模块分层设计
Design
屏幕适配:建议使用今日头条屏幕适配方案
Develop
-
借助 Lifecycle,使得有需要的 java 类或者自定义控件,自动感知生命周期,解决内存泄漏的问题。
(参考:https://developer.android.com/topic/libraries/architecture/lifecycle)
-
所有引入的第三方开源库,强烈建议 进行单独封装, 方便后续替换或者升级。
(比如图片记载,网络请求等等)
ViewModel 用于多Activity 共享数据
一般来说,一个 Activity 对应于一个 ViewModel。但有一些全局情况,比如用户退出登录,多个页面需要被通知,可以借助 ViewModelProvider.Factory 创建共享的 ViewModel 来通信和同步数据。
object SharedViewModelFactory : ViewModelProvider.Factory {
val viewModelMap = hashMapOf<String, ViewModel>()
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName ?: ""
if (modelClass == UserViewModel::class.java) {
return if (viewModelMap.containsKey(canonicalName)) {
viewModelMap[canonicalName] as T
} else {
val viewModel = modelClass.newInstance()
viewModelMap[canonicalName] = viewModel
viewModel as T
}
}
throw IllegalArgumentException("Unknown ViewModel class")
}
fun clearViewModel() {
viewModelMap.clear()
}
}
Gson 和 kotlin 处理服务器返回数据的几个问题
遵循两端相互不信任的原则,客户端定义的服务器返回数据接口要 Any? (可空)的。 基本数据类型可以不为空,但 String,对象类型必须要可以空,防止出现空指针错误。 对于 String,但服务器返回空时,可以使用下列方法来返回 “”。
private static class StringTypeAdapter implements JsonDeserializer<String> {
@Override
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
if (json.isJsonPrimitive()) {
// 基本类型
String str = json.getAsString();
if (str != null) {
return str;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
Retrofit2 + Coroutine 使用的几个问题
-
一般服务器返回的数据都有固定的格式。在使用中异步执行可以处理错误。但在协程下使用,只会得到返回结果,没有 rxjava2 或则异步的错误分支处理。 解决方法:可定义 okhttp 的拦截器,对 chain.process() 进行处理,将异常结果转换为 code = 200 的满足需要的服务器数据。
-
测试 使用 runBlocking{} 进行测试,但不可用在 Android 程序中。
class Retrofit_Test {
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(ErrorHandlerInterceptor())
.addInterceptor { throw RuntimeException("error") }
.build()
val retrofit = Retrofit.Builder().
client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://api.github.com/").build()
@Test
fun listRepos_test() {
val githubService = retrofit.create(GitHubService::class.java)
// val response = githubService
// .listRepos("octocat")
// .execute()
// println("code: ${response.code()}, body: ${GsonUtils.toJson(response.body())}")
// GlobalScope.launch(Dispatchers.Default) {
// val res = githubService.listRepos2("123")
// println("res: 123")
// }
runBlocking {
val response = githubService.listRepos2("octocat")
println("res, body: ${GsonUtils.toJson(response)}")
}
}
}
class ErrorHandlerInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
val request = chain.request()
var response: Response
try {
response = chain.proceed(request)
} catch (e: Exception) {
val result = BaseResponse<List<Repo>>().apply {
result = -9999
message = "internal error"
data = null
}
response = Response.Builder()
.body(ResponseBody.create(null, GsonUtils.toJson(result)))
.code(200)
.message("internal error ${e.message}")
.protocol(Protocol.HTTP_2)
.request(request).build()
}
return response
}
}
interface GitHubService {
@GET("users/{user}/repos")
fun listRepos(@Path("user") user: String): Call<List<Repo>>
@GET("users/{user}/repos")
suspend fun listRepos2(@Path("user") user: String): BaseResponse<String>
}
class BaseResponse<T> {
var result = 0
var message = ""
var data: T? = null
}
class Repo(var id: Int = 0) {
}
解决LiveData 使用的问题
多次消费事件问题
LiveData 会持有当前的值。 可能会多次重复执行,可以参考: LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)
TextView 不同状态下改变背景和字体颜色
改变背景
除了使用 code 去改变, 推荐定义 xml 文件,设置 view background .
android:background="@drawable/relax_tab_background"
res/drawable
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/relax_tab_selected"/>
<item android:state_selected="false" android:drawable="@drawable/relax_tab_unselected" />
</selector>
改变字体颜色
与改变背景相似,定义 xml 文件
android:textColor="@color/relax_tab_text_color"
res/color/
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/relax_white"
android:state_selected="true"/>
<item android:color="#000000" android:state_selected="false"/>
</selector>