组件化开发+MVP框架+Rxjava2+Rxlifecycle2+retrofit2网络框架封装

前言

应公司需求,重构项目,特搭建一套适合自己公司项目的框架


一.所需掌握的技能点

A.组件化开发:

你需要了解以下知识点

1.gradle基础    
2.注解生成器(apt)
3.注解的使用
3.路由器(本项目使用的是阿里开源的arouter)
4.思维导图工具的使用(目的:是用来分离项目的业务)

B.MVP框架:

你需要了解以下知识点

1.注解的使用
2.泛型的使用    
3.动态代理设计模式
4.抽象工厂设计模式
5.单列设计模式
6.mvp设计思想,简单框架的搭建

C.网络框架:

你需要了解以下知识点

1.rxjava,rxandroid,rxlifecycle,rxpermissions(rx系列基本使用)
2.rxjava的操作符compose,等等。。
2.okhttp使用,栏截器,加密等
3.retrofit 基本使用,与rxjava配合的使用

ok,如果以上知识点差不多掌握那么这套框架你分分钟搞定。写这篇文章主要目的,一是为了记录自己所学知识,二是为了巩固自己。如有写的不好的地方,请各位朋友多多海涵。

附上githubdemo https://github.com/yangyoupeng/Componentized


二.搭建框架时遇到的问题:

A组件化遇到的问题

1.组件开发时,我需要什么环境
2.组件之间AndroidManifest合并问题
3.各module如何通信(如:activity,fragment)
4.retrofit,api接口怎么定义?

B MVP框架搭建遇到的问题

1.presenter层如何拿到view的引用 ,当view层销毁时产生内存泄漏,如何解决
2.view 为空时,p层怎么处理
3.使用动态代理生成P层,在view层如何用注解拿到p层

C 网络框架搭建遇到的问题

1.封装retrofit时,okhttp,如果封装公共参数
2.如果动态生成api接口类
3.如果封装rxjava的Observable
4.rxlifecycle绑定



进入正题


三.组件化

参考文章:https://blog.csdn.net/guiying712/article/details/55213884#1%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E9%A1%B9%E7%9B%AE%E7%BB%84%E4%BB%B6%E5%8C%96

我们再来回顾下问题

1.组件开发时,我需要什么环境
2.组件之间AndroidManifest合并问题
3.各module如何通信(如:activity,fragment)
4.retrofit,api接口怎么定义?

1.组件开发时,我需要什么环境

组件开发环境主要是利用:组件模式和集成模式的转换

apply plugin: ‘com.android.application’这个是application属性,可以独立运行的Android程序,也就是我们的APP

apply plugin: ‘com.android.library’library属性,不可以独立运行,一般是Android程序依赖的库文件;

module的属性是在每个组件的 build.gradle 文件中配置的,当我们在组件模式开发时,业务组件应处于application属性,这时的业务组件就是一个 Android App,可以独立开发和调试;而当我们转换到集成模式开发时,业务组件应该处于 library 属性,这样才能被我们的“app壳工程”所依赖,组成一个具有完整功能的APP;

但是我们如何让组件在这两种模式之间自动转换呢?总不能每次需要转换模式的时候去每个业务组件的 Gralde 文件中去手动把 Application 改成 library 吧?如果我们的项目只有两三个组件那么这个办法肯定是可行的,手动去改一遍也用不了多久,但是在大型项目中我们可能会有十几个业务组件,再去手动改一遍必定费时费力,这时候就需要程序员发挥下懒的本质了。

试想,我们经常在写代码的时候定义静态常量,那么定义静态常量的目的什么呢?当一个常量需要被好几处代码引用的时候,把这个常量定义为静态常量的好处是当这个常量的值需要改变时我们只需要改变静态常量的值,其他引用了这个静态常量的地方都会被改变,做到了一次改变,到处生效;根据这个思想,那么我们就可以在我们的代码中的某处定义一个决定业务组件属性的常量,然后让所有业务组件的build.gradle都引用这个常量,这样当我们改变了常量值的时候,所有引用了这个常量值的业务组件就会根据值的变化改变自己的属性;可是问题来了?静态常量是用Java代码定义的,而改变组件属性是需要在Gradle中定义的,Gradle能做到吗?

Gradle自动构建工具有一个重要属性,可以帮助我们完成这个事情。每当我们用AndroidStudio创建一个Android项目后,就会在项目的根目录中生成一个文件 gradle.properties,我们将使用这个文件的一个重要属性:在Android项目中的任何一个build.gradle文件中都可以把gradle.properties中的常量读取出来;那么我们在上面提到解决办法就有了实际行动的方法,首先我们在gradle.properties中定义一个常量值 isModule(是否是组件开发模式,true为是,false为否):

每次更改“isModule”的值后,需要点击 "Sync Project" 按钮
isModule=false

然后我们在业务组件的build.gradle中读取 isModule,但是 gradle.properties 还有一个重要属性: gradle.properties 中的数据类型都是String类型,使用其他数据类型需要自行转换;也就是说我们读到 isModule 是个String类型的值,而我们需要的是Boolean值,代码如下:

if (isModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}

这样我们第一个问题就解决了,当然了 每次改变isModule的值后,都要同步项目才能生效;

2.组件之间AndroidManifest合并问题

在 AndroidStudio 中每一个组件都会有对应的 AndroidManifest.xml,用于声明需要的权限、Application、Activity、Service、Broadcast等,当项目处于组件模式时,业务组件的 AndroidManifest.xml 应该具有一个 Android APP 所具有的的所有属性,尤其是声明 Application 和要 launch的Activity,但是当项目处于集成模式的时候,每一个业务组件的 AndroidManifest.xml 都要合并到“app壳工程”中,要是每一个业务组件都有自己的 Application 和 launch的Activity,那么合并的时候肯定会冲突,试想一个APP怎么可能会有多个 Application 和 launch 的Activity呢?

但是大家应该注意到这个问题是在组件开发模式和集成开发模式之间转换引起的问题,而在上一节中我们已经解决了组件模式和集成模式转换的问题,另外大家应该都经历过将 Android 项目从 Eclipse 切换到 AndroidStudio 的过程,由于 Android 项目在 Eclipse 和 AndroidStudio开发时 AndroidManifest.xml 文件的位置是不一样的,我们需要在build.gradle 中指定下 AndroidManifest.xml 的位置,AndroidStudio 才能读取到 AndroidManifest.xml,这样解决办法也就有了,我们可以为组件开发模式下的业务组件再创建一个 AndroidManifest.xml,然后根据isModule指定AndroidManifest.xml的文件路径,让业务组件在集成模式和组件模式下使用不同的AndroidManifest.xml,这样表单冲突的问题就可以规避了。


image.png

上图是组件化项目中一个标准的业务组件目录结构,首先我们在main文件夹下创建一个module文件夹用于存放组件开发模式下业务组件的 AndroidManifest.xml,而 AndroidStudio 生成的 AndroidManifest.xml 则依然保留,并用于集成开发模式下业务组件的表单;然后我们需要在业务组件的 build.gradle 中指定表单的路径,代码如下:

 sourceSets {
    main {
        if (isModule.toBoolean()) {
            manifest.srcFile 'src/main/module/AndroidManifest.xml'
        } else {
            manifest.srcFile 'src/main/AndroidManifest.xml'
        }
    }
  }

这样在不同的开发模式下就会读取到不同的 AndroidManifest.xml ,然后我们需要修改这两个表单的内容以为我们不同的开发模式服务。

首先是集成开发模式下的 AndroidManifest.xml,前面我们说过集成模式下,业务组件的表单是绝对不能拥有自己的 Application 和 launch 的 Activity的,也不能声明APP名称、图标等属性,总之app壳工程有的属性,业务组件都不能有,下面是一份标准的集成开发模式下业务组件的 AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.guiying.girls">

<application android:theme="@style/AppTheme">
    <activity
        android:name=".main.GirlsActivity"
        android:screenOrientation="portrait" />
    <activity
        android:name=".girl.GirlActivity"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme.NoActionBar" />
</application>

</manifest>

我在这个表单中只声明了应用的主题,而且这个主题还是跟app壳工程中的主题是一致的,都引用了common组件中的资源文件,在这里声明主题是为了方便这个业务组件中有使用默认主题的Activity时就不用再给Activity单独声明theme了。

然后是组件开发模式下的表单文件:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.guiying.girls">

<application
    android:name="debug.GirlsApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/girls_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".main.GirlsActivity"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".girl.GirlActivity"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme.NoActionBar" />
</application>

</manifest>

组件模式下的业务组件表单就是一个Android项目普通的AndroidManifest.xml,这里就不在过多介绍了。

3.各module如何通信(如:activity,fragment)

在组件化开发的时候,组件之间是没有依赖关系,我们不能在使用显示调用来跳转页面了,因为我们组件化的目的之一就是解决模块间的强依赖问题,假如现在要从A业务组件跳转到业务B组件,并且要携带参数跳转,这时候怎么办呢?而且组件这么多怎么管理也是个问题,这时候就需要引入“路由”的概念了.

在这里我就不做过多介绍了,介绍一个阿里的开源框架:ARouter
各位看官请自行去github查看,这里给张飞机票:https://github.com/alibaba/ARouter

4.retrofit,api接口怎么定义?

请求网络基本每个模块都需要用到,但各个模块都不相互依赖,这时,我们的api接口怎么定义呢
不多说直接上代码

image.png

image.png

image.png

来我们看最后一张图,这里我们传入了一个class<T>这个是用来干嘛的呢,还是不费话直接上代码
A模块下:

image.png

B模块下
image.png
这样就OK了



四.MVP框架:

首先贴出原文地址:http://blog.csdn.net/yulong0809/article/details/78622428

简单的封装咱们就不讲了,直接来个有逼格的。


首先讲下封装的步骤与思路:
1.创建简单的mvp
2.通过注解生成presenter
3.动态创建 presenter工厂
4.自定义工厂类(创建不同的presenter)
5.如何使用

1.简单mvp的实现

思路如下: 
1、首先我们先定义一个接口,用来规定针对这个界面逻辑View需要作出的动作的接口。 
2、让Activity实现这个接口中的方法,也就是V层 
3、创建一个类,用来封装之前的网络请求过程,也就是M层 
4、再创建一个类,用来处理M层和V层之间的通信,也就是P层

下面来实现一下:
1、首先我们先定义一个接口,用来规定针对这个界面逻辑View需要作出的动作的接口。

/**
 * <p>View接口的基类</p>
 *
 * @author yangyoupeng
 * @name IBaseView
 */

public interface IBaseView {
    /**
     * 用来 绑定view 生命周期,解决rxjava内存泄露
     *
     * @param
     * @return
     */
    <T> ObservableTransformer<T, T> bindLifeycle();

    /**
     * 显示正在加载
     */
    void showProgress();

    /**
     * 隐藏正在加载
     */
    void hideProgress();

}

2.定义一个presenter ,来用解偶

/**
 * <p>Presenter的基类</p>
 *
 * @author yangyoupeng   2018、4、24
 * * 使用动态代理模式设计basePresenter的初忠:
 * 因为在使用mvp模式开发中,会遇到一些问题:
 * 比如:activity持有presenter的引用,presenter持有view的引用 ,这就导致几个问题,
 * 1.当activity销毁后,由于p持有activity的引用 导致activity无法释放,最终会引用内存泄漏
 * <p>
 * 2.p层处理完逻辑,调用v层来处理UI,怎么拿到V层的引用
 * <p>
 * 3.请求网络时,网络不太好,在这个时候用户没等到请求完成退出该页面,等获取数据成功之后再拿V的引用 ,这个时候view 有可能被销毁,v层的引用为空
 */

public abstract class BasePresenter<V extends IBaseView> {

    /**
     * 解决第一个问题
     * <p>
     * 弱引用, 防止内存泄漏
     */
    private WeakReference<V> mWeakReference;
    private V mProxyView;

    /**
     * 关联V层和P层
     *
     */
    public void attatchView(V v) {
        mWeakReference = new WeakReference<>(v);
        MvpViewHandler viewHandler = new MvpViewHandler(mWeakReference.get());
        mProxyView = (V) Proxy.newProxyInstance(v.getClass().getClassLoader(), v.getClass().getInterfaces(), viewHandler);
    }


    /**
     * @return P层和V层是否关联.
     */
    public boolean isViewAttached() {
        return mWeakReference != null && mWeakReference.get() != null;
    }


    /**
     * 断开V层和P层
     * 在Acitivity的onDestory()中调用
     */
    public void detachView() {
        if (isViewAttached()) {
            mWeakReference.clear();
            mWeakReference = null;
        }
    }

    /**
     * 解决第二个问题
     */


    public V getView() {
        return mProxyView;
    }


    /**
     * Presenter被销毁时调用
     */
    public void onDestroyPersenter() {
        Log.e("perfect-mvp","P onDestroy = ");
    }
    /**
     * 在Presenter意外销毁的时候被调用,它的调用时机和Activity、Fragment、View中的onSaveInstanceState
     * 时机相同
     *
     */
    public void onSaveInstanceState(Bundle presenterBundle) {
        Log.e("perfect-mvp","P onSaveInstanceState = ");
    }

    /**
     * 动态代理类
     */
    private class MvpViewHandler implements InvocationHandler {
        private IBaseView mvpView;

        MvpViewHandler(IBaseView mvpView) {
            this.mvpView = mvpView;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //解决第三个问题,如果V层没被销毁, 执行V层的方法.
            try {
                if (isViewAttached()) {
                    return method.invoke(mvpView, args);
                }
            } catch (InvocationTargetException e){
                throw e.getCause();
            }


            //P层不需要关注V层的返回值
            return null;
        }
    }
}

3.创建建一个类,用来封装之前的网络请求过程,也就是M层

public class BaseModule {

    public static FoundService createrRetrofit() {
        return RetrofitManager.getInstance().getRetrofitService(FoundService.class);
    }

}

这里就是网络请求的封装,这个下面再讲。
好了,简单的mvp就写到这里,上面我注释已经写的很清楚了,在这就不多说了,这里我就不介绍使用了。



下面咱们玩一点高逼格的

2.如何用注解生成presenter

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatePresenter {
    //这里的BaseMvpPresenter是自己封装好的,可以根据项目需要订制
    //? extends BaseMvpPresenter  这指的是什么意思呢,
    //?代表BaseMvpPresenter的子类  ,  extends代表用于返回参数,如果是super代表传参,
    Class<? extends BaseMvpPresenter> value();
}

3.presenter工厂接口

为什么需要这个呢,因为我们不知道presenter具体 是哪个,所以需要拿到有注解的这个类,动态创建presenter。

public interface PresenterMvpFactory<V extends BaseMvpView,P extends                               
        BaseMvpPresenter<V>> {

    /**
     * 创建Presenter的接口方法
     * @return 需要创建的Presenter
     */
    P createMvpPresenter();
}

presenter工厂实现类

**
 * yangyoupeng  on 2018/4/25.
 * <p>
 * 创建presenter
 * 动态 工厂的实现类
 */

public class MvpPresenterFactroyImpl<V extends IBaseView, P extends BasePresenter<V>> implements IMvpPresenterFactroy<V, P> {

    private final Class<P> mPresenterClass;

    private MvpPresenterFactroyImpl(Class<P> presenterClass) {
        this.mPresenterClass = presenterClass;
    }


    /**
     * 根据注解创建Presenter的工厂实现类
     *
     * @param aClass 需要创建Presenter的V层实现类
     * @param <V>    当前View实现的接口类型
     * @param <P>    当前要创建的Presenter类型
     * @return 工厂类
     */
    public static <V extends IBaseView, P extends BasePresenter<V>> MvpPresenterFactroyImpl creater(Class<?> aClass) {
        //拿到创建presenter的注解
        CreatePresenterAnnotation annotation = aClass.getAnnotation(CreatePresenterAnnotation.class);
        Class<P> currentPresenter = null;
        if (annotation != null) {
            //获取到具体的presenter
            currentPresenter = (Class<P>) annotation.value();

        }
        //传给有参构造
        return new MvpPresenterFactroyImpl(currentPresenter);
    }


    @Override
    public P createMvpPresenter() {
        try {
            return mPresenterClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("注解为空,请检查类上是否声明了@CreatePresenterAnnotation(xxx,class)注解");
        }
    }
}


4.自定义工厂类(创建不同的presenter)

1.创建不同的presenter接口,定义set,get方法,获取不同的presenter

public interface IPresenterProxyFactroy<V extends IBaseView, P extends BasePresenter<V>> {

    /**
     * 设置创建Presenter的工厂
     *
     * @param presenterFactory PresenterFactory类型
     */
    void setPresenterFactory(IMvpPresenterFactroy<V, P> presenterFactory);

    /**
     * 获取Presenter的工厂类
     *
     * @return 返回PresenterMvpFactory类型
     */
    IMvpPresenterFactroy<V, P> getPresenterFactory();

    /**
     * 获取创建的Presenter
     *
     * @return 指定类型的Presenter
     */
    P getMvpPresenter();
}

2.实现类,主要是获取当前presenter实列绑定view生命周期,并做了判断处理,让代码更加健壮如:抛出异常


public class PresenterProxyFactroyImpl<V extends IBaseView, P extends BasePresenter<V>> implements IPresenterProxyFactroy<V, P> {
    /**
     * 获取onSaveInstanceState中bundle的key
     */
    private static final String PRESENTER_KEY = "presenter_key";
    /**
     * Presenter工厂类
     */
    private IMvpPresenterFactroy<V, P> mFactory;
    private P mPresenter;
    private Bundle mBundle;
    private boolean mIsAttchView;

    public PresenterProxyFactroyImpl(IMvpPresenterFactroy<V, P> presenterMvpFactory) {
        this.mFactory = presenterMvpFactory;
    }


    @Override
    public void setPresenterFactory(IMvpPresenterFactroy<V, P> presenterFactory) {
        if (mPresenter != null) {
            throw new IllegalArgumentException("这个方法只能在getMvpPresenter()之前调用,如果Presenter已经创建则不能再修改");
        }
        this.mFactory = presenterFactory;
    }

    @Override
    public IMvpPresenterFactroy<V, P> getPresenterFactory() {
        return mFactory;
    }

    @Override
    public P getMvpPresenter() {
        if (mFactory != null) {
            if (mPresenter == null) {
                mPresenter = mFactory.createMvpPresenter();
            }
        }
        Log.e("perfect-mvp", "Proxy getMvpPresenter = " + mPresenter);
        return mPresenter;
    }


    /**
     * 绑定Presenter和view
     */
    public void onCreate(V mvpView) {
        getMvpPresenter();
        Log.e("perfect-mvp","Proxy onCreate");
        if (mPresenter != null && !mIsAttchView) {
            mPresenter.attatchView(mvpView);
            mIsAttchView = true;
        }
    }

    /**
     * 销毁Presenter持有的View
     */
    private void onDetachMvpView() {
        Log.e("perfect-mvp","Proxy onDetachMvpView = ");
        if (mPresenter != null && mIsAttchView) {
            mPresenter.detachView();
            mIsAttchView = false;
        }
    }

    /**
     * 销毁Presenter
     */
    public void onDestroy() {
        Log.e("perfect-mvp","Proxy onDestroy = ");
        if (mPresenter != null ) {
            onDetachMvpView();
            mPresenter.onDestroyPersenter();
            mPresenter = null;
        }
    }

    /**
     * 意外销毁的时候调用
     * @return Bundle,存入回调给Presenter的Bundle和当前Presenter的id
     */
    public Bundle onSaveInstanceState() {
        Log.e("perfect-mvp","Proxy onSaveInstanceState = ");
        Bundle bundle = new Bundle();
        getMvpPresenter();
        if(mPresenter != null){
            Bundle presenterBundle = new Bundle();
            //回调Presenter
            mPresenter.onSaveInstanceState(presenterBundle);
            bundle.putBundle(PRESENTER_KEY,presenterBundle);
        }
        return bundle;
    }

    /**
     * 意外关闭恢复Presenter
     * @param savedInstanceState 意外关闭时存储的Bundler
     */
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        Log.e("perfect-mvp","Proxy onRestoreInstanceState = ");
        Log.e("perfect-mvp","Proxy onRestoreInstanceState Presenter = " + mPresenter);
        mBundle = savedInstanceState;
    }
}


5.如何使用

1.创建一个baseMvp类
2.创建代理对象
3.在activity不同的生命周期调用代理对象的生命周期,实时绑定

/**
 * yangyoupeng  on 2018/4/25.
 * <p>
 * 想要使用mvp,需要继承此BaseMvpActivity
 * <p>
 * TODO:注意  需要创建 presenter,必需要添加注解 @CreatePresenterAnnotation(xxx.class)
 */

public abstract class BaseMvpActivity<V extends IBaseView, P extends BasePresenter<V>> extends BaseActivity
        implements IPresenterProxyFactroy<V, P>, IBaseView{
    private static final String PRESENTER_SAVE_KEY = "presenter_save_key";
    /**
     *1. 创建被代理对象,传入默认Presenter的工厂
     */
    private PresenterProxyFactroyImpl<V, P> mProxy = new PresenterProxyFactroyImpl<>(MvpPresenterFactroyImpl.<V, P>creater(getClass()));
    private LoadingDialog mLoadingDialog;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mProxy.onCreate((V) this);
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mProxy.onRestoreInstanceState(savedInstanceState.getBundle(PRESENTER_SAVE_KEY));
        }
        mLoadingDialog = new LoadingDialog(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e("perfect-mvp", "V onResume");
//        mProxy.onResume((V) this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("perfect-mvp", "V onDestroy = ");
        mProxy.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.e("perfect-mvp", "V onSaveInstanceState");
        outState.putBundle(PRESENTER_SAVE_KEY, mProxy.onSaveInstanceState());
    }

    @Override
    public void setPresenterFactory(IMvpPresenterFactroy<V, P> presenterFactory) {
        Log.e("perfect-mvp", "V setPresenterFactory");
        mProxy.setPresenterFactory(presenterFactory);
    }

    @Override
    public IMvpPresenterFactroy<V, P> getPresenterFactory() {
        Log.e("perfect-mvp", "V getPresenterFactory");
        return mProxy.getPresenterFactory();
    }

    @Override
    public P getMvpPresenter() {
        Log.e("perfect-mvp", "V getMvpPresenter");
        return mProxy.getMvpPresenter();
    }

    /**
     * 绑定生命周期
     */
    @Override
    public <T> ObservableTransformer<T, T> bindLifeycle() {
        return this.bindToLifecycle();
    }


    @Override
    public void showProgress() {
        if (mLoadingDialog != null) {
            mLoadingDialog.show();
        }
    }

    @Override
    public void hideProgress() {
        if (mLoadingDialog != null) {
            mLoadingDialog.dismiss();
        }
    }

2.通过我们之前写的注解获取presenter实列,做用在类上

@CreatePresenterAnnotation(ProductDetailActivityPresenter.class)
public class ProductDetailActivity extends BaseProductActivity<IProductDetailActivityContract.View, 
    ProductDetailActivityPresenter>implements IProductDetailActivityContract.View {
        
    @BindView(R2.id.product_detail_toolbar)
    TitleBar mToolbar;
    @BindView(R2.id.first)
    FrameLayout mFirst;
    @BindView(R2.id.second)
    FrameLayout mSecond;
    @BindView(R2.id.product_detail_layout)
    DragLayout mProductDetailLayout;

    @BindView(R2.id.iv_calculator)
    ImageView mIvCalculator;
    @BindView(R2.id.btn_invest)
    Button mBtnInvest;
    private String mProductId;
    private static final String TAG = "ProductDetailActivity";
    private String mProductFoot;

    @Override
    public int setLayoutId() {
        return R.layout.activity_product_detail;
    }

这里我讲解一下
@CreatePresenterAnnotation(ProductDetailActivityPresenter.class):注解生成presenter实列
IProductDetailActivityContract.View,这个是契约类,用来管理view,presenter接口,google推荐使用方法。
ProductDetailActivityPresenter:这个是具体的presenter

3.创建契约类

/**
*用来管理view,presenter接口,google推荐使用方法。
*/
public interface IProductDetailActivityContract {

    interface View extends IBaseView {
        void isCertification(InvestmentActivityBean result);


    }

    abstract class Presenter extends BasePresenter<View> {
        public abstract void getInvestData(String productId);
    }

}

4.presenter 的实现

/**
 * yangyoupeng  on 2018/5/2.
 */

public class ProductDetailActivityPresenter extends IProductDetailActivityContract.Presenter {


    @Override
    public void getInvestData(String productId) {
        BaseModule.createrRetrofit()
                .getInvestRecord(ProductListParameter.getProductDetailParameter(productId))
                .compose(RxSchedulers.observableIO2Main(getView()))
                .subscribe(new ProgressObserver<InvestmentActivityBean>(this) {
                    @Override
                    public void onSuccess(InvestmentActivityBean result) {
                          getView().isCertification(result);
                    }
                });
    }
}

IProductDetailActivityContract.Presenter :这个也就是契约类里面的presenter抽象类
好了,基本的使用写完了,不知道我有没有写明白,如有不太明白的地方,欢迎留言一起讨论。


C.网络框架:

网络框架封装,网上的文章一大把,在这里我也把这个封装框架放上来,并在组件化mvp中如果去使用它。
1.retrofit管理类
2.异常管理
3.封装线程的切换
4. observer封装,如请求服务器时,显现dialog,成功隐藏dialog
5. 在组件化mvp中绑定生命周期


1.retrofit管理类

/**
 * yangyoupeng  on 2018/4/12.
 * <p>
 * rxjava+retrofit+框架封装
 */

public class RetrofitManager {
    /**
     * 保存一个retrofit的实例,通过吸(baseUrl来获取)
     */
    private HashMap<String, Retrofit> mRetrofitHashMap = new HashMap<>();

    private static final int DEFAULT_MILLISECONDS = 60000;             //默认的超时时间


    /**
     * 内部类单列设计模式
     */
    private RetrofitManager() {
    }

    private static class RetrofitManagerInstance {
        private final static RetrofitManager RETROFIT_MANAGER = new RetrofitManager();
    }

    public static RetrofitManager getInstance() {
        return RetrofitManagerInstance.RETROFIT_MANAGER;
    }

    /**
     * 获取retrofit的实例
     *
     * @return Retrofit
     */
    private Retrofit getRetrofit(String baseurl) {
        Retrofit retrofit;

        if (mRetrofitHashMap.containsKey(baseurl)) {
            retrofit = mRetrofitHashMap.get(baseurl);
        } else {
            retrofit = createrRetrofit(baseurl);
        }

        return retrofit;
    }

    /**
     * 创建retrofit
     *
     * @return Retrofit
     */
    private Retrofit createrRetrofit(String baseurl) {

        OkHttpClient httpClient = new OkHttpClient().newBuilder()
                .readTimeout(DEFAULT_MILLISECONDS, TimeUnit.SECONDS)
                .connectTimeout(DEFAULT_MILLISECONDS, TimeUnit.SECONDS)
                .writeTimeout(DEFAULT_MILLISECONDS, TimeUnit.SECONDS)
                .addNetworkInterceptor(new InterceptorUtil().HeaderInterceptor(BaseApplication.getInstance()))//添加其他拦截器
                .addInterceptor(InterceptorUtil.LogInterceptor())//添加日志拦截器
                .retryOnConnectionFailure(true)
                .build();

        return new Retrofit.Builder()
                .baseUrl(baseurl)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(httpClient)
                .build();
    }


    /**
     *根据各模块业务接口 获取不同的retrofit service接口对象
     */
    public <T> T getRetrofitService(Class<T> cls) {

        return createrRetrofit(BaseApi.getBaseUrl()).create(cls);
    }


}

2.异常管理


public class ExceptionUtil {
    private static final String TAG = "ExceptionUtil";
    private final static int UnknownHostException = 1000;
    private final static int SocketTimeoutException = 2000;
    private final static int JSONException = 3000;
    private final static int ServiceException = 500;
    private final static int CustomerException = 400;
    private final static int RedirectException = 300;
    private final static int NoLoginException = 401;

    public static int exceptionHandler(Throwable e) {
        int code = 0;
        if (e instanceof UnknownHostException) {
            code = UnknownHostException;
        } else if (e instanceof SocketTimeoutException) {
            code = SocketTimeoutException;
        } else if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            code = convertCode(httpException);
        } else if (e instanceof ParseException || e instanceof JSONException
                || e instanceof com.google.gson.JsonIOException) {
            code = JSONException;
        }
        return code;
    }

    private static int convertCode(HttpException httpException){
        int code;
        if (httpException.code() >= 500 && httpException.code() < 600) {
            code = ServiceException;
        } else if (httpException.code() >= 400 && httpException.code() < 500) {
            if(httpException.code() == 401){
                code = NoLoginException;
            } else {
                code = CustomerException;
            }
        } else if (httpException.code() >= 300 && httpException.code() < 400) {
            code = RedirectException;
        } else {
            code = httpException.code();
        }

        return code;
    }

    public static String getMsg(int code){
        String errorMsg = "网络不给力";
        switch (code){
            case UnknownHostException:
                errorMsg = "网络不给力";
                break;
            case SocketTimeoutException:
                errorMsg = "请求网络超时";
                break;
            case JSONException:
                errorMsg = "数据解析错误";
                break;
            case ServiceException:
                errorMsg = "服务器处理请求出错";
                break;
            case CustomerException:
                errorMsg = "服务器无法处理请求";
                break;
            case RedirectException:
                errorMsg = "请求被重定向到其他页面";
                break;
            case NoLoginException:
                errorMsg = "请重新登录";
                break;
        }
        return errorMsg;
    }
**
 * yangyoupeng  on 2018/4/12.
 * <p>
 * 异常处理类
 */
public class RxExceptionUtil {
    private static final String TAG = "RxExceptionUtil";
    public static String exceptionHandler(Throwable e) {
        String errorMsg = "未知错误";
        if (e instanceof UnknownHostException) {
            errorMsg = "网络不可用";
        } else if (e instanceof SocketTimeoutException) {
            errorMsg = "请求网络超时";
        } else if (e instanceof HttpException) {
            HttpException httpException = (HttpException) e;
            errorMsg = convertStatusCode(httpException);

        } else if (e instanceof ParseException || e instanceof JSONException
                || e instanceof com.google.gson.JsonIOException) {
            errorMsg = "数据解析错误";
        }

        return errorMsg;
    }

    private static String convertStatusCode(HttpException httpException){
        Log.d(TAG, "convertStatusCode: "+httpException.code());
        String msg;
        if (httpException.code() >= 500 && httpException.code() < 600) {
            msg = "服务器处理请求出错";
        } else if (httpException.code() >= 400 && httpException.code() < 500) {
            if(httpException.code() == 401){
//                throw new UnLoginException();
                msg = "请重新登录";
            } else {
                msg = "服务器无法处理请求";
            }
        } else if (httpException.code() >= 300 && httpException.code() < 400) {
            msg = "请求被重定向到其他页面";
        } else {
            msg = httpException.message();
        }
        return msg;
    }
}

3.封装线程的切换

/**
 * yangyoupeng  on 2018/4/20.
 * <p>
 * 切换线程 与绑定rxlifeycle 生命周期
 */

public class RxSchedulers {

    public static <T> ObservableTransformer<T, T> observableIO2Main(IBaseView iBaseView) {
        return upstream -> upstream.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .compose(iBaseView.bindLifeycle());
    }
}

在这里我用了lamada表达式,大家有空可以去了解下

3.observer封装,如请求服务器时,显现dialog,成功隐藏dialog

**
 * yangyoupeng  on 2018/4/12.
 * <p>
 * 请求服务器,返回的数据做同一处理
 */

public abstract class BaseObserver<T> implements Observer<BaseResponse<T>> {
    private static final String TAG = "BaseObserver";

    @Override
    public void onSubscribe(Disposable d) {

    }

    @Override
    public final void onNext(BaseResponse<T> tBaseResponse) {
        Log.d(TAG, "服务器返回的code: " + tBaseResponse.getCode());
        switch (tBaseResponse.getCode()) {
            case 200:
                onSuccess(tBaseResponse.getData());
                break;
            default:
                onFailure(new Exception(tBaseResponse.getMsg()), tBaseResponse.getCode(), tBaseResponse.getMsg());
                break;
        }
    }

    @Override
    public void onError(@NonNull Throwable e) {
        int code = ExceptionUtil.exceptionHandler(e);
        Log.d(TAG, "错误信息---" + e.getMessage() + "-----错误码: " + code);
        String msg = ExceptionUtil.getMsg(code);
        onFailure(e, code, msg);
    }

    @Override
    public void onComplete() {

    }

    public abstract void onSuccess(T result);

    public void onFailure(Throwable e, int code, String errorMsg) {
        Log.d(TAG, "onFailure:  什么错误: " + e + "-------错误信息:" + errorMsg);
        if (code == 401) {
            EventBus.getDefault().post(new EventType(EventType.EVENT_NO_LOGIN));
            if (e instanceof HttpException) {
                HttpException httpException = (HttpException) e;
                HttpUrl url = httpException.response().raw().request().url();
                String s = String.valueOf(url);
                Log.d(TAG, "onFailure: "+s);
                if(!s.contains("api/account/index")){
                    ARouter.getInstance().build(RouterPath.LOGIN_REGISTER_ACTIVITY).navigation();
                }
            }
        } else if (code == 402) {//实名认证
            EventBus.getDefault().post(new EventType(EventType.TO_REA_NNAME_AUTHENTICATION));
        } else {
            ToastUtils.showShortToast(errorMsg);
        }
    }

}
/**
 * yangyoupeng  on 2018/4/12.
 * <p>
 * 扩展类,用来调用请求网络时的进度条
 */

public abstract class ProgressObserver<T> extends BaseObserver<T> {
    private BasePresenter mBasePresenter;


    protected ProgressObserver(BasePresenter basePresenter) {
        this.mBasePresenter = basePresenter;
    }


    @Override
    public void onSubscribe(@NonNull Disposable d) {
        if (!d.isDisposed()) {
            mBasePresenter.getView().showProgress();
        }
    }

    @Override
    public void onComplete() {
        if (mBasePresenter.isViewAttached()) {
            mBasePresenter.getView().hideProgress();
        }
    }


    @Override
    public void onError(@NonNull Throwable e) {
        super.onError(e);
        if (mBasePresenter.isViewAttached()) {
            mBasePresenter.getView().hideProgress();
        }
    }
}

5.在组件化mvp中绑定生命周期

1.首先需要在这里定义bindLifeycle返回值是ObservableTransformer

public interface IBaseView {
    /**
     * 用来 绑定view 生命周期,解决rxjava内存泄露
     *
     * @param
     * @return
     */
    <T> ObservableTransformer<T, T> bindLifeycle();

2.当前baeActivity 继承RxAppCompatActivity

SuppressLint("Registered")
public abstract class BaseActivity extends RxAppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(setLayoutId());
        GetuiPushClient.getInstance().init(getApplication());
        ButterKnife.bind(this);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); // 禁止所有的activity横屏
        ViewManager.getInstance().addActivity(this);
        initView(savedInstanceState);
        initData();
        EventBus.getDefault().register(this);
    }

实现IBaseView方法,绑定生命周期

public abstract class BaseMvpActivity<V extends IBaseView, P extends BasePresenter<V>> extends BaseActivity
        implements IPresenterProxyFactroy<V, P>, IBaseView{
    private static final String PRESENTER_SAVE_KEY = "presenter_save_key";


    /**
     * 绑定生命周期
     */
    @Override
    public <T> ObservableTransformer<T, T> bindLifeycle() {
        return this.bindToLifecycle();
    }

3.在网络请求中通过.compose()去切换线程并绑定生命周期

    @Override
    public void getData(String productId, String type) {
        BaseModule.createrRetrofit()
                .getInvestRecord(ProductListParameter.getProductDetailParameter(productId))
                .compose(RxSchedulers.observableIO2Main(getView()))
                .subscribe(new ProgressObserver<InvestmentActivityBean>(this) {
                    @Override
                    public void onSuccess(InvestmentActivityBean result) {
                        getView().displayData(result, type);
                    }
                });
    }

到这里就结束啦,细节实在太多,没办法一篇文章写出来。所以代码我都贴出来了,如有不太明白的地方欢迎一起探讨交流, QQ: 1003605461。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,050评论 25 707
  • 不怕跌倒,所以飞翔 组件化开发 参考资源 Android组件化方案 为什么要组件化开发 解决问题 实际业务变化非常...
    笔墨Android阅读 2,943评论 0 0
  • 1. Android组件化开发 在Android项目组件化之前,我们的项目都是像下图那样,一个单一工程下,根据不同...
    CHSmile阅读 4,501评论 1 34
  • 今天要说的事情都很重要。 01 健康很重要。 健康是一切的基础。没有健康不能工作,不能为家人做饭,不能去想去的地方...
    梅洛的听雨轩阅读 522评论 0 1