布景
先说一下布景,当接触了比较多的项目之后,其实会发现每一个项目都会封装BaseActivity、BaseFragment等等。其实初衷其实是好的。每一个Activity和Fragment都是许多模板代码的,为了削减模板代码,封装进Base类其实是一种比较便利且可行的挑选。
Base类涵盖了笼统、承继等面向对象特性,用得好会削减许多样板代码,可是一旦乱用,会对项目有许多坏处。
举个比如
当项目大了,需求封装进Base类的逻辑会非常多,比如说打印生命周期、ViewBinding 或许DataBinding封装、埋点、监听播送、监听EventBus、展现加载界面、弹Dialog等等其他事务逻辑,更有甚者把需求Context的函数都封装进Base类中。
以下举一个BaseActivity的比如,里面封装了上面所说的大部分状况,实际状况或许更多。
abstract class BaseActivity<T: ViewBinding, VM: ViewModel>: AppCompatActivity {
protected lateinit var viewBinding: T
protected lateinit var viewModel: VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 打印日志!!
ELog.debugLifeCycle("${this.localClassName} - onCreate")
// 初始化viewModel
viewModel = initViewModel()
// 初始化视图!!
initView()
// 初始化数据!!
initData()
// 注册播送监听!!
registerReceiver()
// 注册EventBus工作监听!!
registerEventBus()
// 省略一堆事务逻辑!
// 设置导航栏颜色!!
window.navigationBarColor = ContextCompat.getColor(this, R.color.primary_color)
}
protected fun initViewModel(): VM {
// 初始化viewModel
}
private fun initViewbinding() {
// 初始化viewBinding
}
// 让子类必须完成
abstract fun initView()
abstract fun initData()
private fun registerReceiver() {
// 注册播送监听
}
private fun unregisterReceiver() {
// 刊出播送监听
}
private fun registerEventBus() {
// 注册EventBus工作监听
}
protected fun showDialog() {
// 需求用到Context,因而也封装进来了
}
override fun onResume() {
super.onResume()
ELog.debugLifeCycle("${this.localClassName} - onResume")
}
override fun onPause() {
super.onPause()
ELog.debugLifeCycle("${this.localClassName} - onPause")
}
override fun onDestroy() {
super.onDestroy()
ELog.debugLifeCycle("${this.localClassName} - onDestroy")
unregisterReceiver()
}
}
其实看起来还好,可是在运用的时分难免会遇到一些问题,关于半途接手项意图人来说问题更加明显。咱们从半途接手项意图心路历程看看Base类的缺陷。
心路历程
-
当创立新的Activity或许Fragment的时分需求想想有没有逻辑能够复用,就去找Base类,或许写Base类的人不同,发现一个项目中或许会存在多个Base类,乃至Base类依然有多个Base子类完成不同逻辑。这个时分就需求去查看分析每个Base类别离完成了什么功用,决议承继哪个。
-
假如一个项目中只要一个Base类的话,仍需求看看Base类完成了什么逻辑,没有完成什么逻辑,避免重复写样板代码。
-
当呈现Base类完成了的,而自己本身并不想需求,例如不想监听播送或许不想用ViewModel,关于不想监听播送的状况就要特别做适配,例如往Base类加标志位。关于不想用ViewModel可是由于泛型限制,仍是只能传进去,不然没法承继。
-
当发现自己集成Base类出BUG了,就要考虑改子类仍是改Base类,由于大量的类都集成了Base类,显然改Base类比较麻烦,于是改自己比较便利。
-
假如一个Activity中展现了多个Fragment,或许会有事务逻辑的重复,其实只需求一个就好了。
其实榜首第二点还好,时间成本其实没有重复写样板代码那么高。可是第三点的话其实用标志位来决议Base类的功用哪个需求完成哪个不需求完成并不是一种高雅的办法,反而需求重写的东西多了几个。第四点归根结底便是Base类其实并不好保护。
爬坑
那么关于Base类怎样实践才比较高雅呢?在我看来组合代替承继其实是一种不错的思路。关于Kotlin first的Android项目来说,组合代替承继其实是比较容易的。以下仅代表个人想法,有不同定见能够交流一下。
成员变量托付
关于ViewModel、Handler、ViewBinding这些Base变量运用托付的办法是比较便利的。
关于ViewBinding
托付能够看看我之前的文章,运用起来其实是非常简单的,只需求一行代码即可。
// Activity
private val binding by viewBinding(ActivityMainBinding::inflate)
// Fragment
private val binding by viewBinding(FragmentMainBinding::bind)
关于ViewModel
托付,官方库则供给了一个viewBindings
托付函数。
private val viewModel:HomeViewModel by viewModels()
需求在Gradle中引进ktx库
implementation 'androidx.fragment:fragment-ktx:1.5.1'
implementation 'androidx.activity:activity-ktx:1.5.1'
而关于Base变量则尽量少封装在Base类中,需求运用能够运用托付,由于假如实例了没有运用其实是比较浪费内存资源的,尽量按需实例。
扩展办法
关于需求用到Context上下文的逻辑封装到Base类中其实是没有必要的,在Kotlin还没有盛行的时分,假如说需求运用到Context的工具办法,运用起来其实是不太高雅的。
例如展现一个Dialog:
class DialogUtils {
public static void showDialog(Activity activity, String title, String content) {
// 逻辑
}
}
运用起来便是这样:
class MyActivity : AppCompatActivity() {
...
fun initButton() {
button.setOnClickListener {
DialogUtils.showDialog(this, "title", "content")
}
}
}
运用起来或许就会有一些利诱,榜首个参数把自己传进去了,这关于展现Dialog的语义上是有些奇怪的。按理来说只需求传title和content就好了。
这个时分就会有人想着把这个封装到Base类中。
public abstract class BaseActivity extends AppCompatActivity {
protected void showDialog(String title, String content) {
// 这儿就能够用Context了
}
}
运用起来便是这样:
class MyActivity : AppCompatActivity() {
...
fun initButton() {
button.setOnClickListener {
showDialog("title", "content")
}
}
}
是不是感觉好许多了。可是写在Base类中在Java中比较好用,关于Kotlin则完全能够运用扩展函数语法糖来代替了,在运用的时分和界说在Base类是相同的。
fun Activity.showDialog(title: String, content: String) {
// this就能获取到Activity实例
}
class MyActivity : AppCompatActivity() {
...
fun initButton() {
button.setOnClickListener {
// 运用起来和界说在Base类其实是相同的
showDialog("title", "content")
}
}
}
这也说明了,需求运用到Context上下文的函数其实不用在Base类中界说,直接界说在顶层就好了,能够削减Base类的逻辑。
注册监听器
关于注册监听器这种状况则需求分状况,监听器是需求根据生命周期来注册和取消注册的,避免内存泄漏。关于不是每个子类都需求的状况,有的人或许觉得供给一个标志位就好了,假如不需求的话让子类重写。假如界说成笼统办规律每个子类都要重写,假如不是笼统办法的话,子类或许就会忘掉重写。在我看来获取生命周期其实是比较简单的工作。按需添加代码监听就好了。
那么什么状况需求封装在Base类中呢?
-
怕之后接手项意图人忘掉写这部分代码,则能够写到Base类中,例如打印日志或许埋点。
-
而关于界面太多难以测试的功用,例如收到被服务器踢下线的音讯跳到登录页面,这个能够写进Base类中,由于基本上每个类都需求监听这种音讯。
总结
没有最优秀的架构,只要最适合的架构!关于Base类大家的看法都不相同,追求更少的工作量完成更多工作这个意图是统一的。而Base类一旦臃肿起来了会形成整个项目难以保护,因而关于Base类应该辩证看待,养成只要必要的逻辑才写在Base类中的习气,feature类应该运用组合的办法来运用,这关于项意图可保护性和代码的可调试性是有好处的。
参考
/post/707789…