说说我自己常用的 Android 架构

作者: cpacm | 来源:发表于2017-03-01 15:20 被阅读1682次

    先列出一些常用的依赖,想必看到下面的依赖大家也能明白接下来要讲的是什么?

    //所需依赖
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'io.reactivex.rxjava2:rxjava:2.0.5'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.google.code.gson:gson:2.8.0'
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
    

    没错,就是老生常谈的 MVP 模式。

    retrofit2作为网络请求,gson作为json解析器。注意这里是用的最新的 rxjava2 和 jakewharton 大大开源的 retrofit2-rxjava2-adapter 作为桥接器。最新的版本可以前往各自的 Github 上查看。

    mvp

    基础架构

    我个人比较喜欢在项目下新建一个 Android library 的模块,取名为core,主要作用是负责网络层和数据层。像数据实体类,数据库操作,SharedPreferences缓存,网络请求都可以放在 core 模块下,主要目的就是彻底将UI和数据层完全分开(物理层面上)。

    网络模块

    需要一个 RetrofitHelper 单例模块支持,主要是为 OkHttp 设置请求参数属性和初始化 Api 接口服务。

    OkHttp的参数设置

    OkHttp上可以设置的参数很多,像缓存,头部信息,超时时间,重连等信息都可以在 OkHttpClient 初始化设置时统一设置。

    private void initOkHttp() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        
        //设置统一的请求头部参数
        builder.addInterceptor(apikey);
        //设置缓存
        builder.addNetworkInterceptor(cacheInterceptor);
        builder.addInterceptor(cacheInterceptor);
        builder.cache(cache);
        //设置超时
        builder.connectTimeout(10, TimeUnit.SECONDS);
        builder.readTimeout(20, TimeUnit.SECONDS);
        builder.writeTimeout(20, TimeUnit.SECONDS);
        //错误重连
        builder.retryOnConnectionFailure(true);
        okHttpClient = builder.build();
    }
    

    初始化项目 api 接口

    一般来说,一个项目的网络返回数据都有统一的返回数据,比如有一个定义好的返回码 resultCode,数据返回信息 resultInfo,以及最重要的数据对象 returnObject。所以我们需要一个类来进行网络数据套接。

    public class ApiResponse<T> {
        private int resultCode;
        private T returnObject;
        private Object ruturnInfo;
    
        // get 和 set 方法
        //...
    }
    

    定义好数据类型,就轮到 Retrofit 与网络接口进行联动,首先需要一个能够定义 api 接口的地方 Apis

    public interface Apis {
    
        /**
         * 获取启动页图片
         *
         * @return
         */
        @FormUrlEncoded
        @POST("getSplashImg")
        Observable<ApiResponse<SplashImageBean>> getStartImg(@Field("uid") String uid,@Field("size") String size);
    
        //其他的api
        ...
    }
    

    之后回到 RetrofitHelper 中初始化接口服务。

    // 接口服务
    apis = getApiService(HttpUtils.BASEURL, Apis.class);
    
    // 接口服务初始化方法
    private <T> T getApiService(String baseUrl, Class<T> clz) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        return retrofit.create(clz);
    }
    

    搭建 MVP 架构

    此方法是从 Google 的开源项目android-architecture 上的 todo‑mvp‑rxjava 篇演变而来。一样需要定义 View 和 Presenter 接口以及Presenter的实现,只不过加了一层 RxPresenter 对Rxjava的优化,防止内存的泄露。

    MVP的基础类

    首先要为mvp模式定下基础接口 BaseViewBasePresenter
    Presenter 需要绑定 View 才能回调 View 里面的各种方法,所以直接在类声明的时候将 View 绑定。
    同理,View 里面需要一个 Presenter 去处理数据,故定义一个 setPresenter() 方法来提醒(所以不设置也行)。

    public interface BaseView {
        void setPresenter();
    }
    
    public interface BasePresenter<T extends BaseView> {
        void attachView(T view);
        void detachView();
    }
    

    Rxjava 在 MVP 上的优化

    之前在写 MoeMusic开源项目 的时候完全没有考虑到 Rxjava 在与 Retrofit 结合请求网络请求的时候会存在内存泄露的问题,所以在这个模块上利用 Rxjava 的订阅和取消订阅功能消除内存泄露的问题。

    /**
     * @author cpacm
     * @date 2017/2/26
     * @desciption 可取消订阅的 rxpresenter,防止rxjava引起的内存泄露
     */
    
    public abstract class RxPresenter<T extends BaseView> implements BasePresenter<T> {
        protected T view;
        protected CompositeDisposable compositeDisposable;
    
        protected void unDisposable() {
            if (compositeDisposable != null) {
                compositeDisposable.clear();
            }
        }
    
        protected void addDisposable(Disposable disposable) {
            if (compositeDisposable == null) {
                compositeDisposable = new CompositeDisposable();
            }
            compositeDisposable.add(disposable);
        }
    
        @Override
        public void attachView(T view) {
            this.view = view;
        }
    
        @Override
        public void detachView() {
            this.view = null;
            unDisposable();
        }
    }
    

    原理很简单,就是使用 CompositeDisposable 来订阅 rxjava 发射的事件,之后在 detachView() 解绑的时候取消订阅。

    MVP 的简单使用

    在使用前,我们先建一个 BaseActivity 作为所有的Activity的基类,并将其生命周期与 MVP 模块关联起来。

    public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements BaseView {
    
        protected T presenter;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setPresenter();
            if (presenter != null) {
                presenter.attachView(this);
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (presenter != null) {
                presenter.detachView();
            }
        }
    }
    

    使用示例

    需求:一个 app 的启动页,启动页图片来自服务器。

    SplashContract 中定义各个接口要回调的方法。

    public interface SplashContract {
    
        interface View extends BaseView {
            void showSplash(SplashImageBean bean);
        }
    
        interface Presenter extends BasePresenter<View> {
            void getSplashData();
        }
    }
    

    SplashPresenter 中实现网络的请求和view的回调

    public class SplashPresenter extends RxPresenter<SplashContract.View> implements SplashContract.Presenter {
    
        private Apis zqswApis;
    
        public SplashPresenter() {
            zqswApis = RetrofitHelper.getInstance().getApis();
        }
    
        @Override
        public void getSplashData() {
            Disposable disposable = zqswApis.getStartImg("","")
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribeOn(Schedulers.io())
                    .subscribe(new Consumer<ApiResponse<SplashImageBean>>() {
                        @Override
                        public void accept(ApiResponse<SplashImageBean> splashImageBean) throws Exception {
                            view.showSplash(splashImageBean.getReturnObject());
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            Log.e("cpacm", throwable.toString());
                        }
                    });
            addDisposable(disposable);
        }
    }
    

    SplashActivity 实现UI完成整个需求

    /**
     * @author cpacm
     * @date 2017/2/16
     * @desciption 启动界面
     */
    
    public class SplashActivity extends BaseActivity<SplashPresenter> implements SplashContract.View {
    
        private ImageView bgView;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_splash);
            bgView = (ImageView) findViewById(R.id.background);
            presenter.getSplashData();
        }
    
        @Override
        public void setPresenter() {
            presenter = new SplashPresenter();
        }
    
        @Override
        public void showSplash(SplashImageBean bean) {
            Glide.with(this)
                    .load(bean.getImageUrl())
                    .into(bgView);
            Toast.makeText(this, bean.toString(), Toast.LENGTH_SHORT).show();
        }
    
    }
    

    项目源码见下方链接

    结论

    整个框架小巧而精致,而且看起来也不怎么复杂,个人项目或者小型的团队项目应付起来应该是绰绰有余了,不过大型的项目应该还需要扩展或者采用其他的架构来应付繁琐的需求。


    MvpDemo源码

    相关文章

      网友评论

      • sundy_5f80:获取网络数据应该放在model中,而非p中
      • TechMix:这个架构现在很多人用吗
      • d5bd132e85f5:感觉很好用
      • 相互交流:楼主对于MVP模式接口很多,有什么看法,或许有什么好的解决方案。。忘解答啊!
        cpacm:现在已经把V和P的接口放在了 Contract 中,减少了一倍的文件量。如果再减少只能进行再次封装,比如说定义个接口父类,设个 onReponse(T result) 方法,如果业务只是获取网络数据可以复用这个接口类可以不用再定义新的接口出来。但不是很推荐这种再封装的做法,代码的耦合度又上去了。
      • wwzlp:架构还是比较清晰的
      • 2060c4a8b92d:我们一个多年老App准备重新架构,参考一下,谢谢
      • aa943d52bbd9:实用,厉害了~
      • 贾亦真亦贾:既然分库了就没必要引入MVP了
        cpacm:@贾亦真亦贾 不不不,网络请求的封装只是在m层调用起来比较方便,p层才是主要写业务逻辑的地方
        贾亦真亦贾:@cpacm 感觉MVP除了封装了一下网络请求就没干别的事了 逻辑都写在库里了吧?
        cpacm:分库与使不使用MVP没什么关系,分库只是让代码层次再清晰一点。

      本文标题:说说我自己常用的 Android 架构

      本文链接:https://www.haomeiwen.com/subject/umzqgttx.html