Android组件化搭建分享

1.组件化开发

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

我总结了好几点:android

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

如下为基础模块包:github

package.png

整个项目结构:api

Android框架模块分布图.png

Android studio:bash

图片2.png

在gradle.properties中,咱们能够设置一个变量,控制是否使用模块化来开发网络

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

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

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

业务模块gradle进行模块判断app

图片3.png

//经过以前设定的变量进行设置是application仍是library
if (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:

图片4.png

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具体的使用方法: github.com/alibaba/ARo…

2.Retrofit+RxJava+MVP模式

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

1.基于Retrofit的Api工厂

ApiFactory以下图:

api工厂.png

图中的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> T create(Class<T> 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能够解耦,结构清晰,对于业务复杂的场景来讲,能够提升代码可读性,结构清晰,下降后期维护成本。以下图登陆模块所示:

mvp.png

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<T extends IPresenterView> 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>        t
     * @return return
     */
    protected <T> Observable<T> createObservable(Observable<T> 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<String, List<Disposable>> sObservableDisposableList = new WeakHashMap();

    public RxObservable() {
    }

    /**
     * 建立被观察者,如retrofit集合rxJava返回的网络请求,
     * 此方法用于事件在初始化时进行处理,把此事件保存到sObservableDisposableList集合中,
     * 以tag为key,觉得List<Disposable>为值,订阅被观察者时能够获其Disposable
     */
    public static <T> Observable<T> create(Observable<T> 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<LoginContract.View> 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<UserInfo>() {
            @Override
            protected void onError(String msg) {
                //登陆失败
                mView.onLoginFailed(msg);
            }

            @Override
            protected void accept(UserInfo userInfo) {
                //登陆成功等操做
            }
        });
    }

    
}
复制代码
相关文章
相关标签/搜索