博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android组件化搭建分享
阅读量:6721 次
发布时间:2019-06-25

本文共 10472 字,大约阅读时间需要 34 分钟。

1.组件化开发

组件化开发这个名词并不陌生,但真正什么才是组件化开发,大家在网上搜可以查看很多相应的文章,我概念中,模块化的开发,就是把很多模块独立出来,基础模块,业务模块等。什么是基础模块,基础模块就是公司常用的一些sdk,一些封装好的基类,业务模块就是基于基础模块进行开发的。在以往的开发中,我并未真正的去使用组件化开发,直到加入新的团队可以说是开启新世界的大门,给我的感觉,组件化开发,贼爽,为什么爽?

我总结了好几点:

1.各自负责业务模块独立开发,以application进行开发,后期再以library引入项目 2.因为每个模块独立出来,以最小模块的进行开发,编译速度快 3.有利于单元测试,对业务模块进行单元测试 4.降低耦合,提高模块的复用

以下为基础模块包:

整个项目结构:

Android studio:

在gradle.properties中,我们可以设置一个变量,控制是否使用模块化来开发

#是否使用模块化开发isModule=false复制代码

然后在settings.gradle中设置项目引入包

//默认都打开基础模块include ':sdk', ':model', ':widget', ':module-basic'//根据自己负责的模块分别进行相应的引入include ':module-user'include ':module-business'//根据是否模块开发,是否引入app 模块if (!isModule.toBoolean()) {    include ':app'}复制代码

业务模块gradle进行模块判断

//通过之前设定的变量进行设置是application还是libraryif (isModule.toBoolean()) {    apply plugin: 'com.android.application'} else {    apply plugin: 'com.android.library'}复制代码

根据结构图,我们基础模块的依赖,默认引入sdk、model、widget、module-baisc 然后根据自己负责的业务模块,分别引入不同的业务,如果我是负责用户模块,我在开发就只需要引入用户模块即可,这样开发每个模块的时候可以提高每个模块的编译效率。

最后模块合并的时候,在gradle.properties中关闭模块开发,在settings.gradle引入项目相应的模块包,并设置app的build-gradle:

build-gradle:

dependencies {    compile fileTree(dir: 'libs', include: ['*.jar'])    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {        exclude group: 'com.android.support', module: 'support-annotations'    })    compile 'com.android.support:appcompat-v7:26.+'    compile 'com.android.support.constraint:constraint-layout:1.0.2'    compile 'com.android.support:design:26.+'    testCompile 'junit:junit:4.12'    //如果不使用模块化开发,就引入所有的业务模块    if (!isModule.toBoolean()) {        compile project(':module-business')        compile project(':module-user')    }}复制代码

现在的问题,不同模块的activity怎么跳转,以前我的做法都会在每个activity中写一个静态方法,把入参设定好.

/** * 跳转 * * @param context 上下文 * @param param   参数 */public static void toAcitivty(Context context, String param) {    Intent intent = new Intent(context, MainActivity.class);    intent.putExtra("param", param);    context.startActivity(intent);}复制代码

因为使用模块化开发的话,不同业务模块是不能调用其activity,因此我们使用阿里的Arouter, 在每个activity头部使用注解进行跳转,就像Spring mvc 的controller一样,使用路由进行设置跳转,在模块化的开发中,这个很关键,一方面使用arouter可以降低activity之间的耦合,另一方面可以对模块进行单元测试。

Arouter具体的使用方法:

2.Retrofit+RxJava+MVP模式

关于Retrofit跟RxJava,具体详细的用法就不在这里介绍,网上有很多现有的文章,为什么使用Retrofit跟RxJava,Retrofit是基于Okhttp封装一层的客户端,配合RxJava线程调度,很好的控制网络请求,使用RxJava可以提高代码的可读性,这里我分享一下retrofit+Rxjava封装。

1.基于Retrofit的Api工厂

ApiFactory如下图:

图中的ApiFactory的职责是提供所有业务Api接口,具体提供的Api是通过接口ApiProvider提供每个业务接口,如果用户接口,交易接口,令牌接口等,ApiFactory通过单例,获取api提供者,ApiProvider具体的实现类ApiProvideImpl继承于网络引擎RetrofitApi,RetrofitApi用于初始化一些网络引擎。ApiProvideImpl中使用retrofit来初始化各种Api接口。

ApiProviderImpl.java:

class ApiProviderImpl extends RetrofitApi implements ApiProvider {    private OkHttpClient httpClient;    ApiProviderImpl(Context applicationContext) {        super(applicationContext);    }    private 
T create(Class
cls) { return mRetrofit.create(cls); } @Override public ITokenApi getTokenApi() { return create(ITokenApi.class); } @Override public IUserApi getUserApi() { return create(IUserApi.class); } @Override public IProductApi getProductApi() { return create(IProductApi.class); } @Override public IPushApi getPushApi() { return create(IPushApi.class); } @Override public IQuotationApi getQuotationApi() { return create(IQuotationApi.class); } @Override public ITradeApi getTradeApi() { return create(ITradeApi.class); } .....}复制代码

2.MVP

使用mvp可以解耦,结构清晰,对于业务复杂的场景来说,可以提高代码可读性,结构清晰,降低后期维护成本。如下图登录模块所示:

View跟presenter都抽象成接口,这样相互不依赖于细节,有易于做单元测试,降低耦合。这里有两个基础接口,LoginView跟LoginPresenter分别继承于IView跟IPresenter,LoginViewImpl以及LoginPresenterImpl分别实现LoginView跟LoginPresenter,其依赖于抽象不依赖于实现的细节。

/** * 登录契约类 */public interface LoginContract {    /**     * 表现层接口     */    interface Presenter extends IPresenter {        /**         * 登录操作         */        void login();    }    /**     * 视图层接口     */    interface View extends IPresenterView {        /**         * 获取密码         *         * @return return         */        String getPassword();        /**         * 获取用户信息         *         * @return return         */        String getUsername();        /**         * 登录成功         */        void loginSuccess();        /**         * 登录失败         *         * @param msg msg         */        void loginFailed(String msg);    }}复制代码

我们通过定义一个Contract契约类,来制定接口,在定Presenter跟view接口的同时,我们可以很清晰的知道,表现层需要什么东西,view层需要提供什么东西,包括网络请求后相应的响应,这样在我们做一个业务逻辑的时候思路可以更清晰,同事在进行presenter复用以及单元测试会更方便。

3.结合Retrofit+RxJava+Mvp

结合之前谈到的Api跟mvp,在这个基础上进行封装Presenter的实现基础类。

/** * presenter基础实现类的封装 * 1.跟视图view进行绑定与解绑 * 2.对rx事件进行加工处理与释放资源 */public class BasicPresenterImpl
implements IPresenter { /** * 视图 */ protected T mView; /** * 上下文 */ protected Context mContext; /** * 记录标识,用于此presenter所有的任务进行标识 */ private String mTag = this.getClass().getName(); public BasicPresenterImpl(Context context, T view) { this.mView = view; this.mContext = context; } public void start() { } /** * 销毁资源,一般用于与view解绑操作 * 如activity作为view中,activity 销毁的时候调用 * 避免错误引用,避免内存泄露 */ public void destroy() { this.mView = null; this.mContext = null; this.cancelRequest(); } /** * 根据tag清掉任务,如清掉未完成的网路请求 */ protected void cancelRequest() { RxObservable.dispose(this.mTag); RxObservable.dispose("PageDataObservable"); } /** * rxJava 多数用于创建网络请求 * 如createObservable(mUser.login()) * retorfit结合rxJava * * @param observable observable * @param
t * @return return */ protected
Observable
createObservable(Observable
observable) { //创建任务 return RxObservable.create(observable, this.mTag); }}复制代码

基础Presenter封装了绑定与解绑的操作,presenter跟view解绑时调用destory释放资源,并把此presenter中使用rxJava处理得事件全部清掉,释放资源,例如一些网络请求,当view跟presenter解绑后网络请求未来得及返回处理,容易出现view空指针的操作。

接着介绍一下RxObservable的封装:

/** * 用于封装rx事件,通过键值对的方式保存任务 * 对任务进行初始化,释放等操作所 */public final class RxObservable {    /**     * 全局变量,使用tag标识保存Disposable集合     * Disposable?Observer(观察者)与Observable(被观察者)切断链接     */    private static final Map
> sObservableDisposableList = new WeakHashMap(); public RxObservable() { } /** * 创建被观察者,如retrofit集合rxJava返回的网络请求, * 此方法用于事件在初始化时进行处理,把此事件保存到sObservableDisposableList集合中, * 以tag为key,以为List
为值,订阅被观察者时可以获其Disposable */ public static
Observable
create(Observable
observable, final String tag) { return observable.doOnSubscribe(new Consumer() { public void accept(@NonNull Disposable disposable) throws Exception { //在集合中判断是否存在集合 //没有则创建,并以key-tag保存到sObservableDisposableList中 List disposables = (List) RxObservable.sObservableDisposableList.get(tag); if (disposables == null) { ArrayList disposables1 = new ArrayList(); RxObservable.sObservableDisposableList.put(tag, disposables1); } //把此事件的Disposable添加到对应的tag的集合中 ((List) RxObservable.sObservableDisposableList.get(tag)).add(disposable); } //订阅过程在Io线程处理,发送在主线程处理 }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); } /** * 释放所有资源 */ public static void dispose() { try { Iterator e = sObservableDisposableList.values().iterator(); while (e.hasNext()) { List disposables = (List) e.next(); Iterator var2 = disposables.iterator(); while (var2.hasNext()) { Disposable disposable = (Disposable) var2.next(); if (disposable != null && !disposable.isDisposed()) { disposable.dispose(); } } disposables.clear(); } } catch (Exception var7) { Log.e("rae", "释放HTTP请求失败!", var7); } finally { sObservableDisposableList.clear(); } } /** * 根据tag标识进行释放资源 * * @param tag tag */ public static void dispose(String tag) { try { if (!TextUtils.isEmpty(tag) && sObservableDisposableList.containsKey(tag)) { List e = (List) sObservableDisposableList.get(tag); Iterator var2 = e.iterator(); while (var2.hasNext()) { Disposable disposable = (Disposable) var2.next(); if (disposable != null && !disposable.isDisposed()) { disposable.dispose(); } } e.clear(); sObservableDisposableList.remove(tag); return; } } catch (Exception var7) { Log.e("rae", "释放HTTP请求失败!", var7); return; } finally { sObservableDisposableList.remove(tag); } }}复制代码

在RxObservable中,创建一个sObservableDisposableList用于保存每个presenter中处理的事件,通过tag作为标识创建,每个presenter中会通过tag找到对应的Disposable集合,Disposable集合中保存了此presenter中的所有任务,如网络请求、io操作等,通过此方法可以统一管理tag的任务,在presenter解绑的时候可以及时的销毁资源,避免内存泄露。

登录的一个小例子:

public class LoginPresenterImpl extends BasicPresenterImpl
implements LoginContract.Presenter { IUserApi mUserApi; public LoginPresenterImpl(Context context, LoginContract.View view) { super(context, view); //初始化变量.... } @Override public void login() { //在view层获取手机号跟密码 final String mobile = mView.getMobile(); final String password = mView.getPassword(); if (TextUtils.isEmpty(mobile)) { mView.onLoginFailed("请输入手机号码"); return; } if (TextUtils.isEmpty(password)) { mView.onLoginFailed("请输入密码"); return; } if (!mPhoneValidator.isMobile(mobile)) { mView.onLoginFailed("请输入正确的手机号码"); return; } createObservable(mUserApi.login(mobile, password)).subscribe(new ApiDefaultObserver
() { @Override protected void onError(String msg) { //登录失败 mView.onLoginFailed(msg); } @Override protected void accept(UserInfo userInfo) { //登录成功等操作 } }); } }复制代码

转载地址:http://rujmo.baihongyu.com/

你可能感兴趣的文章
定制更友好的iptables防火墙
查看>>
用sql语句对access数据库进行多条件查询
查看>>
php操作ini配置文件
查看>>
dataguard主备延迟多长时间的查询方法
查看>>
[Array]628. Maximum Product of Three Numbers
查看>>
C++函数模板&类模板
查看>>
spring事件广播
查看>>
javascript事件委托和jquery事件委托
查看>>
使用ReaderWriterLock类实现多用户读/单用户写同步
查看>>
MySQL--Basic(一)
查看>>
(转)CSS字体大小: em与px、pt、百分比之间的对比
查看>>
C语言的关键字
查看>>
喷水装置(一)NYOJ6
查看>>
填充与步幅
查看>>
bzoj 1911 特别行动队
查看>>
关于PHPExcel类占用内存问题
查看>>
hadoop分布式存储(1)-hadoop基础概念
查看>>
Mac svn使用学习-1-简介
查看>>
浅谈IT技术选型和未来技术发展趋势
查看>>
JS怎么创建一个类?
查看>>