引言:千里之行,始于足下。
如果你从事Android开发,请认真看完本篇文章,因为可能会颠覆你对Android开发的认识。
当夜空中繁星点点,一颗璀璨的流星划过,其辉光洒在古老的山谷之中,照亮了一个隐藏在山石之间的洞穴。
洞穴内的空气弥漫着神秘的气息,流星划过的光芒越来越亮,洞穴内的温度也开始升高。你能感受到一股无法言喻的力量,这似乎是一个新的时代的开始。
突然,洞穴内的石壁开始发出耀眼的光芒,似乎有某种能量觉醒了。你不禁一愣,然后意识到这是传说中的创世神器正在诞生。
在光芒中,一块巨大的水晶开始从石壁上升起,悬浮在空中,它就是https://github.com/dora4/dora 。这块水晶透射出五彩斑斓的光芒,你能感觉到其中蕴含的无限力量。你走近一看,石壁上镌刻着一行行整齐的铭文,原来是https://github.com/dora4/dora_samples 。你突然意识到,这石壁上的铭文似乎跟眼前这块巨大的水晶存在着某种密不可分的联系。莫非上面镌刻着这块水晶的使用方法?你思索着。
石壁开头写着这么几行文字。
这块水晶散发着微弱但持续的吸引力,它可以用来吸引其他美丽的宝石,并使其绕着自己旋转,并释放出清脆动人的歌声。以某种方式,可以让其吸附到水晶上,并获取其能力。
比如:
implementation("com.github.dora4:dora:1.1.37")
implementation("com.github.dora4:dora-firebase-support:1.2")
implementation("com.github.dora4:dora-glide-support:1.2")
implementation("com.github.dora4:dora-arouter-support:1.6")
你似乎对眼前的事物十分感兴趣,然后读起了正文。
正文
项目从模块化、组件化开始
在Android开发中,模块化、组件化是一个重要的话题。因为,它可以从业务产品层面解耦合,并不仅仅是单一模块的代码。这样做有什么好处呢?它可以从认知层面杜绝核心代码泄露给潜在的竞争对手。非核心模块单独也能运行,同时也可以运行在宿主程序中,作为主程序的一个模块进行打包。这样的项目结构大概是app模块、基础库模块、公共代码库模块以及一个个的业务模块。
那么app模块里面只放一个Application,清单文件里面只配置一个入口activity和公共的权限以及引用到的app名称和logo资源等。然后将该入口activity从其所在模块的清单文件中注释掉。注意,这个入口也是可以换的。
基础库,就是dora全家桶框架所要做的事情。比如dora核心架构库https://github.com/dora4/dora 及其扩展包,比如https://github.com/dora4/dora-glide-support glide图片加载扩展包、https://github.com/dora4/dora-arouter-support arouter模块化、组件化扩展包以及https://github.com/dora4/dcache-android dcache网络请求与数据持久化库等。这些基础库是所有模块都可能用到的。所以我们在公共代码库里面去implementation这些基础库。最后其他所有业务相关的模块都直接compileOnly这个公共代码库和其需要的功能型开源库。最后app模块implementation你所有要打包进来的模块,当然也包括公共代码库。因为dora的这些support扩展包大多是使用api依赖的这些第三方SDK,而不是使用的implementation,所以你可以直接穿透使用第三方SDK的API方法。这样相当于间接获取了第三方SDK的功能,同时也能使用support包的一些优化API。
公共代码库,里面放所有业务模块的model、api接口、以及自定义View等,也包含所有通用的资源文件,包括但不仅限于strings.xml、colors.xml以及drawable等。重要的事情就是ARouter的所有路径一定要放在公共代码库,这样你所有依赖公共代码库的模块都可以通过这个路径花名册调用到其他任何一个模块的任意一个功能。当然这个是使用了ARouter的IProvider接口配合@Autowired注解进行依赖注入使用的。
更高效和舒服的开发方式,MVVM
MVVM,即Model-View-ViewModel开发模式。model指你的数据,你的模型或实体类,view则表示所有承载界面的容器activity、fragment、dialog等以及其上面的contentView,viewmodel是model跟view的中间层,因为model跟view不直接建立联系。
Android中的MVVM主要使用的是Jetpack中的一些开发套件。比如DataBinding、BindingAdapter、LiveData、ViewModel、ObservableField、Lifecycle等。
举个例子:我们可以在xml中定义一些变量,在view层去绑定这些变量。
<layout>
<data>
<variable
name="v"
type="com.example.dora.MenuListActivity" />
<variable
name="vm"
type="com.example.dora.vm.VMMenu" />
</data>
<!-- 以下是你的contentView布局 -->
<RelativeLayout>...</RelativeLayout>
</layout>
然后在view层的kotlin或java代码给这些xml中定义的变量赋值。
override fun initData(savedInstanceState: Bundle?, binding: ActivityMenuListBinding) {
binding.v = this
binding.vm = ViewModelProvider(this)[VMMenu::class.java]
}
使用BindingAdapter扩展控件属性实现点击事件
class BindingAdapters {
companion object {
@JvmStatic
@BindingAdapter("android:click")
fun bindClick(view: View, listener: OnClickListener) {
view.setOnClickListener(listener)
}
}
}
这样你就可以在xml中的控件中使用android:click属性,并指定一个lambda表达式来快捷绑定点击事件了。
<Button
android:id="@+id/btn_next_page"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:layout_gravity="bottom|center_horizontal"
android:text="下一页"
android:click="@{()->v.nextPage()}"/>
这个nextPage()方法定义在view层,如activity中。由于nextPage()方法是dora框架中dora.BaseActivity的API,所以无需定义,直接使用。
这里需要注意的是配置了@BindingAdapter的方法必须是static方法,如果是kotlin,则务必加上@JvmStatic注解,否则无法绑定成功。
使用BindingAdapter绑定RecyclerView的BaseQuickAdapter适配器
在BindingAdapters.kt中增加以下方法。
@JvmStatic
@BindingAdapter("android:adapter")
fun <T, B : ViewDataBinding> bindAdapter(recyclerView: RecyclerView, adapter: BaseAdapter<T, B>) {
recyclerView.adapter = adapter
}
@JvmStatic
@BindingAdapter("android:itemDecoration")
fun bindItemDecoration(recyclerView: RecyclerView, decoration: ItemDecoration) {
recyclerView.addItemDecoration(decoration)
}
然后就可以在xml中的RecyclerView控件中指定这些属性了。
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_menu_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:itemDecoration="@{vm.listDecorationObservable}"
android:adapter="@{vm.adapterObservable}"/>
诶,你有没有发现,这里指定的是ViewModel里面的ObservableField系列属性。
package com.example.dora.vm
import androidx.databinding.ObservableField
import androidx.lifecycle.ViewModel
import androidx.recyclerview.widget.DividerItemDecoration
import com.example.dora.BaseAdapter
import dora.util.GlobalContext
open class BaseViewModel : ViewModel() {
var listDecorationObservable = ObservableField<DividerItemDecoration>()
var adapterObservable = ObservableField<BaseAdapter<*,*>>()
init {
listDecorationObservable.set(DividerItemDecoration(GlobalContext.get(), DividerItemDecoration.VERTICAL))
}
}
这就是viewmodel+observablefield的一种开发方式,observablefield里面的值发生变化,UI会自动更新。还有另外一个开发方式则是viewmodel+livedata。
ViewModel的作用
使用viewmodel最大的好处就是方便复用,viewmodel里面的数据由于它源码中是保存在hashmap中的,所以你可以理解成内存缓存,你可以在横竖屏切换以及多个界面中复用这部分数据,而无需重新请求。甚至一些通用的数据你可以直接放在BaseViewModel中,这样你只要继承BaseViewModel,就可以直接在xml中使用这些数据了。使用viewmodel和databinding还有一个好处就是方便在xml和kotlin/java代码中进行切换阅读代码,直接点过去就可以找到具体的某个地方了。是不是瞬间就减少了我们找代码的时间?
Dora SDK的全局配置GlobalConfig原理解析
下面重点讲解一下GlobalConfig这个东西。很多第三方SDK都是要初始化的,所以这也是Dora SDK的一个强大用途。通过配置文件注入第三方SDK的生命周期实现。
<application>
<meta-data
android:name="dora.lifecycle.config.TaskStackGlobalConfig"
android:value="GlobalConfig" />
<meta-data
android:name="dora.lifecycle.config.ARouterGlobalConfig"
android:value="GlobalConfig" />
<meta-data
android:name="dora.lifecycle.config.EventBusGlobalConfig"
android:value="GlobalConfig" />
</application>
你通过在AndroidManifest.xml清单文件的application节点下配置meta-data元数据,来直接注入全局的生命周期。android:name指定config的实现类的全类名,android:value指定GlobalConfig。这样Dora SDK就可以通过解析xml来读取这些配置,并直接在全局的生命周期中注入这些配置了。
在AppDelegate里面有一个DefaultGlobalConfig,这里给你提供了一个实现GlobalConfig类的模板。框架层最终会逐个解析这些GlobalConfig并在生命周期中对这些代码进行叠加。
private static class DefaultGlobalConfig implements GlobalConfig {
@Override
public void injectApplicationLifecycle(Context context, List<ApplicationLifecycleCallbacks> lifecycles) {
// All methods in AppLifecycle will be called during the corresponding lifecycle of
// the base Application class,
// so you can extend your own logic in the respective methods.
// Multiple implementation classes can be added based on different logic requirements.
// 简体中文:AppLifecycle 中的所有方法都会在基类 Application 的对应生命周期中被调用, 所以在对应的方
// 法中可以扩展一些自己需要的逻辑
// 可以根据不同的逻辑添加多个实现类
lifecycles.add(new AppLifecycle());
}
@Override
public void injectActivityLifecycle(Context context, List<Application.ActivityLifecycleCallbacks> lifecycles) {
// All methods in ActivityLifecycleCallbacks will be called during the corresponding
// lifecycle of the Activity (including third-party libraries),
// so you can extend your own logic in the respective methods.
// Multiple implementation classes can be added based on different logic requirements.
// 简体中文:ActivityLifecycleCallbacks 中的所有方法都会在 Activity (包括三方库) 的对应生命周期中
// 被调用,所以在对应的方法中可以扩展一些自己需要的逻辑
// 可以根据不同的逻辑添加多个实现类
lifecycles.add(new ActivityLifecycle());
}
@Override
public void injectFragmentLifecycle(Context context, List<FragmentManager.FragmentLifecycleCallbacks> lifecycles) {
// All methods in FragmentLifecycleCallbacks will be called during the corresponding
// lifecycle of the Fragment (including third-party libraries),
// so you can extend your own logic in the respective methods.
// Multiple implementation classes can be added based on different logic requirements.
// 简体中文:FragmentLifecycleCallbacks 中的所有方法都会在 Fragment (包括三方库) 的对应生命周期中
// 被调用,所以在对应的方法中可以扩展一些自己需要的逻辑
// 可以根据不同的逻辑添加多个实现类
lifecycles.add(new FragmentLifecycle());
}
}
大多数dora的扩展包support库都会给你提供推荐的默认配置,你可以直接配置到你的清单文件中。暂时是手动控制配置到开关,并非implementation库即配置。这样做的目的就是你可以在调试的时候,仅注释掉配置,而无需重新索引和更新所有依赖。
配置解析的关键代码如下,默认配置自动被添加进去,同时解析其他所有GlobalConfig的生命周期注入配置,如application的生命周期、activity的生命周期。
public AppDelegate(Context context) {
this.mConfigs = ManifestParser.parse(context);
this.mConfigs.add(0, new DefaultGlobalConfig());
for (GlobalConfig config : mConfigs) {
config.injectApplicationLifecycle(context, mApplicationLifecycles);
config.injectActivityLifecycle(context, mActivityLifecycles);
}
}
另外fragment的生命周期则是作为activity的子项注入。
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
registerFragmentCallbacks(activity);
}
使用Android Studio IDE插件来提升开发效率
你还可以使用IDE插件来快速创建BaseAcitivity
和BaseFragment
。
插件项目:https://github.com/dora4/dora-studio-plugin
插件包:https://dorachat.oss-cn-hongkong.aliyuncs.com/dora-studio-plugin-1.1.jar
后记
你看完了铭文,顿时茅塞顿开,发现这么好的东西决不能落到坏人手里。决定拿着这颗神奇的水晶去除恶扬善,拯救天下苍生,将这种能量传播出去。从此你获得了令人羡慕的成就、美好的爱情、无尽的愉悦、享不尽的荣华富贵,过上了幸福的生活。