組件化開(kāi)發(fā)+MVP框架+Rxjava2+Rxlifecycle2+retrofit2網(wǎng)絡(luò)框架封裝

前言

應(yīng)公司需求,重構(gòu)項(xiàng)目,特搭建一套適合自己公司項(xiàng)目的框架


一.所需掌握的技能點(diǎn)

A.組件化開(kāi)發(fā):

你需要了解以下知識(shí)點(diǎn)

1.gradle基礎(chǔ)    
2.注解生成器(apt)
3.注解的使用
3.路由器(本項(xiàng)目使用的是阿里開(kāi)源的arouter)
4.思維導(dǎo)圖工具的使用(目的:是用來(lái)分離項(xiàng)目的業(yè)務(wù))

B.MVP框架:

你需要了解以下知識(shí)點(diǎn)

1.注解的使用
2.泛型的使用    
3.動(dòng)態(tài)代理設(shè)計(jì)模式
4.抽象工廠設(shè)計(jì)模式
5.單列設(shè)計(jì)模式
6.mvp設(shè)計(jì)思想,簡(jiǎn)單框架的搭建

C.網(wǎng)絡(luò)框架:

你需要了解以下知識(shí)點(diǎn)

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

ok,如果以上知識(shí)點(diǎn)差不多掌握那么這套框架你分分鐘搞定。寫(xiě)這篇文章主要目的,一是為了記錄自己所學(xué)知識(shí),二是為了鞏固自己。如有寫(xiě)的不好的地方,請(qǐng)各位朋友多多海涵。

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


二.搭建框架時(shí)遇到的問(wèn)題:

A組件化遇到的問(wèn)題

1.組件開(kāi)發(fā)時(shí),我需要什么環(huán)境
2.組件之間AndroidManifest合并問(wèn)題
3.各module如何通信(如:activity,fragment)
4.retrofit,api接口怎么定義?

B MVP框架搭建遇到的問(wèn)題

1.presenter層如何拿到view的引用 ,當(dāng)view層銷(xiāo)毀時(shí)產(chǎn)生內(nèi)存泄漏,如何解決
2.view 為空時(shí),p層怎么處理
3.使用動(dòng)態(tài)代理生成P層,在view層如何用注解拿到p層

C 網(wǎng)絡(luò)框架搭建遇到的問(wèn)題

1.封裝retrofit時(shí),okhttp,如果封裝公共參數(shù)
2.如果動(dòng)態(tài)生成api接口類(lèi)
3.如果封裝rxjava的Observable
4.rxlifecycle綁定



進(jìn)入正題


三.組件化

參考文章: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

我們?cè)賮?lái)回顧下問(wèn)題

1.組件開(kāi)發(fā)時(shí),我需要什么環(huán)境
2.組件之間AndroidManifest合并問(wèn)題
3.各module如何通信(如:activity,fragment)
4.retrofit,api接口怎么定義?

1.組件開(kāi)發(fā)時(shí),我需要什么環(huán)境

組件開(kāi)發(fā)環(huán)境主要是利用:組件模式和集成模式的轉(zhuǎn)換

apply plugin: ‘com.android.application’這個(gè)是application屬性,可以獨(dú)立運(yùn)行的Android程序,也就是我們的APP

apply plugin: ‘com.android.library’library屬性,不可以獨(dú)立運(yùn)行,一般是Android程序依賴(lài)的庫(kù)文件;

module的屬性是在每個(gè)組件的 build.gradle 文件中配置的,當(dāng)我們?cè)诮M件模式開(kāi)發(fā)時(shí),業(yè)務(wù)組件應(yīng)處于application屬性,這時(shí)的業(yè)務(wù)組件就是一個(gè) Android App,可以獨(dú)立開(kāi)發(fā)和調(diào)試;而當(dāng)我們轉(zhuǎn)換到集成模式開(kāi)發(fā)時(shí),業(yè)務(wù)組件應(yīng)該處于 library 屬性,這樣才能被我們的“app殼工程”所依賴(lài),組成一個(gè)具有完整功能的APP;

但是我們?nèi)绾巫尳M件在這兩種模式之間自動(dòng)轉(zhuǎn)換呢?總不能每次需要轉(zhuǎn)換模式的時(shí)候去每個(gè)業(yè)務(wù)組件的 Gralde 文件中去手動(dòng)把 Application 改成 library 吧?如果我們的項(xiàng)目只有兩三個(gè)組件那么這個(gè)辦法肯定是可行的,手動(dòng)去改一遍也用不了多久,但是在大型項(xiàng)目中我們可能會(huì)有十幾個(gè)業(yè)務(wù)組件,再去手動(dòng)改一遍必定費(fèi)時(shí)費(fèi)力,這時(shí)候就需要程序員發(fā)揮下懶的本質(zhì)了。

試想,我們經(jīng)常在寫(xiě)代碼的時(shí)候定義靜態(tài)常量,那么定義靜態(tài)常量的目的什么呢?當(dāng)一個(gè)常量需要被好幾處代碼引用的時(shí)候,把這個(gè)常量定義為靜態(tài)常量的好處是當(dāng)這個(gè)常量的值需要改變時(shí)我們只需要改變靜態(tài)常量的值,其他引用了這個(gè)靜態(tài)常量的地方都會(huì)被改變,做到了一次改變,到處生效;根據(jù)這個(gè)思想,那么我們就可以在我們的代碼中的某處定義一個(gè)決定業(yè)務(wù)組件屬性的常量,然后讓所有業(yè)務(wù)組件的build.gradle都引用這個(gè)常量,這樣當(dāng)我們改變了常量值的時(shí)候,所有引用了這個(gè)常量值的業(yè)務(wù)組件就會(huì)根據(jù)值的變化改變自己的屬性;可是問(wèn)題來(lái)了?靜態(tài)常量是用Java代碼定義的,而改變組件屬性是需要在Gradle中定義的,Gradle能做到嗎?

Gradle自動(dòng)構(gòu)建工具有一個(gè)重要屬性,可以幫助我們完成這個(gè)事情。每當(dāng)我們用AndroidStudio創(chuàng)建一個(gè)Android項(xiàng)目后,就會(huì)在項(xiàng)目的根目錄中生成一個(gè)文件 gradle.properties,我們將使用這個(gè)文件的一個(gè)重要屬性:在Android項(xiàng)目中的任何一個(gè)build.gradle文件中都可以把gradle.properties中的常量讀取出來(lái);那么我們?cè)谏厦嫣岬浇鉀Q辦法就有了實(shí)際行動(dòng)的方法,首先我們?cè)趃radle.properties中定義一個(gè)常量值 isModule(是否是組件開(kāi)發(fā)模式,true為是,false為否):

每次更改“isModule”的值后,需要點(diǎn)擊 "Sync Project" 按鈕
isModule=false

然后我們?cè)跇I(yè)務(wù)組件的build.gradle中讀取 isModule,但是 gradle.properties 還有一個(gè)重要屬性: gradle.properties 中的數(shù)據(jù)類(lèi)型都是String類(lèi)型,使用其他數(shù)據(jù)類(lèi)型需要自行轉(zhuǎn)換;也就是說(shuō)我們讀到 isModule 是個(gè)String類(lèi)型的值,而我們需要的是Boolean值,代碼如下:

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

這樣我們第一個(gè)問(wèn)題就解決了,當(dāng)然了 每次改變isModule的值后,都要同步項(xiàng)目才能生效;

2.組件之間AndroidManifest合并問(wèn)題

在 AndroidStudio 中每一個(gè)組件都會(huì)有對(duì)應(yīng)的 AndroidManifest.xml,用于聲明需要的權(quán)限、Application、Activity、Service、Broadcast等,當(dāng)項(xiàng)目處于組件模式時(shí),業(yè)務(wù)組件的 AndroidManifest.xml 應(yīng)該具有一個(gè) Android APP 所具有的的所有屬性,尤其是聲明 Application 和要 launch的Activity,但是當(dāng)項(xiàng)目處于集成模式的時(shí)候,每一個(gè)業(yè)務(wù)組件的 AndroidManifest.xml 都要合并到“app殼工程”中,要是每一個(gè)業(yè)務(wù)組件都有自己的 Application 和 launch的Activity,那么合并的時(shí)候肯定會(huì)沖突,試想一個(gè)APP怎么可能會(huì)有多個(gè) Application 和 launch 的Activity呢?

但是大家應(yīng)該注意到這個(gè)問(wèn)題是在組件開(kāi)發(fā)模式和集成開(kāi)發(fā)模式之間轉(zhuǎn)換引起的問(wèn)題,而在上一節(jié)中我們已經(jīng)解決了組件模式和集成模式轉(zhuǎn)換的問(wèn)題,另外大家應(yīng)該都經(jīng)歷過(guò)將 Android 項(xiàng)目從 Eclipse 切換到 AndroidStudio 的過(guò)程,由于 Android 項(xiàng)目在 Eclipse 和 AndroidStudio開(kāi)發(fā)時(shí) AndroidManifest.xml 文件的位置是不一樣的,我們需要在build.gradle 中指定下 AndroidManifest.xml 的位置,AndroidStudio 才能讀取到 AndroidManifest.xml,這樣解決辦法也就有了,我們可以為組件開(kāi)發(fā)模式下的業(yè)務(wù)組件再創(chuàng)建一個(gè) AndroidManifest.xml,然后根據(jù)isModule指定AndroidManifest.xml的文件路徑,讓業(yè)務(wù)組件在集成模式和組件模式下使用不同的AndroidManifest.xml,這樣表單沖突的問(wèn)題就可以規(guī)避了。


image.png

上圖是組件化項(xiàng)目中一個(gè)標(biāo)準(zhǔn)的業(yè)務(wù)組件目錄結(jié)構(gòu),首先我們?cè)趍ain文件夾下創(chuàng)建一個(gè)module文件夾用于存放組件開(kāi)發(fā)模式下業(yè)務(wù)組件的 AndroidManifest.xml,而 AndroidStudio 生成的 AndroidManifest.xml 則依然保留,并用于集成開(kāi)發(fā)模式下業(yè)務(wù)組件的表單;然后我們需要在業(yè)務(wù)組件的 build.gradle 中指定表單的路徑,代碼如下:

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

這樣在不同的開(kāi)發(fā)模式下就會(huì)讀取到不同的 AndroidManifest.xml ,然后我們需要修改這兩個(gè)表單的內(nèi)容以為我們不同的開(kāi)發(fā)模式服務(wù)。

首先是集成開(kāi)發(fā)模式下的 AndroidManifest.xml,前面我們說(shuō)過(guò)集成模式下,業(yè)務(wù)組件的表單是絕對(duì)不能擁有自己的 Application 和 launch 的 Activity的,也不能聲明APP名稱(chēng)、圖標(biāo)等屬性,總之a(chǎn)pp殼工程有的屬性,業(yè)務(wù)組件都不能有,下面是一份標(biāo)準(zhǔn)的集成開(kāi)發(fā)模式下業(yè)務(wù)組件的 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>

我在這個(gè)表單中只聲明了應(yīng)用的主題,而且這個(gè)主題還是跟app殼工程中的主題是一致的,都引用了common組件中的資源文件,在這里聲明主題是為了方便這個(gè)業(yè)務(wù)組件中有使用默認(rèn)主題的Activity時(shí)就不用再給Activity單獨(dú)聲明theme了。

然后是組件開(kāi)發(fā)模式下的表單文件:
<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>

組件模式下的業(yè)務(wù)組件表單就是一個(gè)Android項(xiàng)目普通的AndroidManifest.xml,這里就不在過(guò)多介紹了。

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

在組件化開(kāi)發(fā)的時(shí)候,組件之間是沒(méi)有依賴(lài)關(guān)系,我們不能在使用顯示調(diào)用來(lái)跳轉(zhuǎn)頁(yè)面了,因?yàn)槲覀兘M件化的目的之一就是解決模塊間的強(qiáng)依賴(lài)問(wèn)題,假如現(xiàn)在要從A業(yè)務(wù)組件跳轉(zhuǎn)到業(yè)務(wù)B組件,并且要攜帶參數(shù)跳轉(zhuǎn),這時(shí)候怎么辦呢?而且組件這么多怎么管理也是個(gè)問(wèn)題,這時(shí)候就需要引入“路由”的概念了.

在這里我就不做過(guò)多介紹了,介紹一個(gè)阿里的開(kāi)源框架:ARouter
各位看官請(qǐng)自行去github查看,這里給張飛機(jī)票:https://github.com/alibaba/ARouter

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

請(qǐng)求網(wǎng)絡(luò)基本每個(gè)模塊都需要用到,但各個(gè)模塊都不相互依賴(lài),這時(shí),我們的api接口怎么定義呢
不多說(shuō)直接上代碼

image.png

image.png

image.png

來(lái)我們看最后一張圖,這里我們傳入了一個(gè)class<T>這個(gè)是用來(lái)干嘛的呢,還是不費(fèi)話直接上代碼
A模塊下:

image.png

B模塊下
image.png
這樣就OK了



四.MVP框架:

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

簡(jiǎn)單的封裝咱們就不講了,直接來(lái)個(gè)有逼格的。


首先講下封裝的步驟與思路:
1.創(chuàng)建簡(jiǎn)單的mvp
2.通過(guò)注解生成presenter
3.動(dòng)態(tài)創(chuàng)建 presenter工廠
4.自定義工廠類(lèi)(創(chuàng)建不同的presenter)
5.如何使用

1.簡(jiǎn)單mvp的實(shí)現(xiàn)

思路如下: 
1、首先我們先定義一個(gè)接口,用來(lái)規(guī)定針對(duì)這個(gè)界面邏輯View需要作出的動(dòng)作的接口。 
2、讓Activity實(shí)現(xiàn)這個(gè)接口中的方法,也就是V層 
3、創(chuàng)建一個(gè)類(lèi),用來(lái)封裝之前的網(wǎng)絡(luò)請(qǐng)求過(guò)程,也就是M層 
4、再創(chuàng)建一個(gè)類(lèi),用來(lái)處理M層和V層之間的通信,也就是P層

下面來(lái)實(shí)現(xiàn)一下:
1、首先我們先定義一個(gè)接口,用來(lái)規(guī)定針對(duì)這個(gè)界面邏輯View需要作出的動(dòng)作的接口。

/**
 * <p>View接口的基類(lèi)</p>
 *
 * @author yangyoupeng
 * @name IBaseView
 */

public interface IBaseView {
    /**
     * 用來(lái) 綁定view 生命周期,解決rxjava內(nèi)存泄露
     *
     * @param
     * @return
     */
    <T> ObservableTransformer<T, T> bindLifeycle();

    /**
     * 顯示正在加載
     */
    void showProgress();

    /**
     * 隱藏正在加載
     */
    void hideProgress();

}

2.定義一個(gè)presenter ,來(lái)用解偶

/**
 * <p>Presenter的基類(lèi)</p>
 *
 * @author yangyoupeng   2018、4、24
 * * 使用動(dòng)態(tài)代理模式設(shè)計(jì)basePresenter的初忠:
 * 因?yàn)樵谑褂胢vp模式開(kāi)發(fā)中,會(huì)遇到一些問(wèn)題:
 * 比如:activity持有presenter的引用,presenter持有view的引用 ,這就導(dǎo)致幾個(gè)問(wèn)題,
 * 1.當(dāng)activity銷(xiāo)毀后,由于p持有activity的引用 導(dǎo)致activity無(wú)法釋放,最終會(huì)引用內(nèi)存泄漏
 * <p>
 * 2.p層處理完邏輯,調(diào)用v層來(lái)處理UI,怎么拿到V層的引用
 * <p>
 * 3.請(qǐng)求網(wǎng)絡(luò)時(shí),網(wǎng)絡(luò)不太好,在這個(gè)時(shí)候用戶沒(méi)等到請(qǐng)求完成退出該頁(yè)面,等獲取數(shù)據(jù)成功之后再拿V的引用 ,這個(gè)時(shí)候view 有可能被銷(xiāo)毀,v層的引用為空
 */

public abstract class BasePresenter<V extends IBaseView> {

    /**
     * 解決第一個(gè)問(wèn)題
     * <p>
     * 弱引用, 防止內(nèi)存泄漏
     */
    private WeakReference<V> mWeakReference;
    private V mProxyView;

    /**
     * 關(guān)聯(lián)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層是否關(guān)聯(lián).
     */
    public boolean isViewAttached() {
        return mWeakReference != null && mWeakReference.get() != null;
    }


    /**
     * 斷開(kāi)V層和P層
     * 在Acitivity的onDestory()中調(diào)用
     */
    public void detachView() {
        if (isViewAttached()) {
            mWeakReference.clear();
            mWeakReference = null;
        }
    }

    /**
     * 解決第二個(gè)問(wèn)題
     */


    public V getView() {
        return mProxyView;
    }


    /**
     * Presenter被銷(xiāo)毀時(shí)調(diào)用
     */
    public void onDestroyPersenter() {
        Log.e("perfect-mvp","P onDestroy = ");
    }
    /**
     * 在Presenter意外銷(xiāo)毀的時(shí)候被調(diào)用,它的調(diào)用時(shí)機(jī)和Activity、Fragment、View中的onSaveInstanceState
     * 時(shí)機(jī)相同
     *
     */
    public void onSaveInstanceState(Bundle presenterBundle) {
        Log.e("perfect-mvp","P onSaveInstanceState = ");
    }

    /**
     * 動(dòng)態(tài)代理類(lèi)
     */
    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 {
            //解決第三個(gè)問(wèn)題,如果V層沒(méi)被銷(xiāo)毀, 執(zhí)行V層的方法.
            try {
                if (isViewAttached()) {
                    return method.invoke(mvpView, args);
                }
            } catch (InvocationTargetException e){
                throw e.getCause();
            }


            //P層不需要關(guān)注V層的返回值
            return null;
        }
    }
}

3.創(chuàng)建建一個(gè)類(lèi),用來(lái)封裝之前的網(wǎng)絡(luò)請(qǐng)求過(guò)程,也就是M層

public class BaseModule {

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

}

這里就是網(wǎng)絡(luò)請(qǐng)求的封裝,這個(gè)下面再講。
好了,簡(jiǎn)單的mvp就寫(xiě)到這里,上面我注釋已經(jīng)寫(xiě)的很清楚了,在這就不多說(shuō)了,這里我就不介紹使用了。



下面咱們玩一點(diǎn)高逼格的

2.如何用注解生成presenter

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface CreatePresenter {
    //這里的BaseMvpPresenter是自己封裝好的,可以根據(jù)項(xiàng)目需要訂制
    //? extends BaseMvpPresenter  這指的是什么意思呢,
    //?代表BaseMvpPresenter的子類(lèi)  ,  extends代表用于返回參數(shù),如果是super代表傳參,
    Class<? extends BaseMvpPresenter> value();
}

3.presenter工廠接口

為什么需要這個(gè)呢,因?yàn)槲覀儾恢纏resenter具體 是哪個(gè),所以需要拿到有注解的這個(gè)類(lèi),動(dòng)態(tài)創(chuàng)建presenter。

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

    /**
     * 創(chuàng)建Presenter的接口方法
     * @return 需要?jiǎng)?chuàng)建的Presenter
     */
    P createMvpPresenter();
}

presenter工廠實(shí)現(xiàn)類(lèi)

**
 * yangyoupeng  on 2018/4/25.
 * <p>
 * 創(chuàng)建presenter
 * 動(dòng)態(tài) 工廠的實(shí)現(xiàn)類(lèi)
 */

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;
    }


    /**
     * 根據(jù)注解創(chuàng)建Presenter的工廠實(shí)現(xiàn)類(lèi)
     *
     * @param aClass 需要?jiǎng)?chuàng)建Presenter的V層實(shí)現(xiàn)類(lèi)
     * @param <V>    當(dāng)前View實(shí)現(xiàn)的接口類(lèi)型
     * @param <P>    當(dāng)前要?jiǎng)?chuàng)建的Presenter類(lèi)型
     * @return 工廠類(lèi)
     */
    public static <V extends IBaseView, P extends BasePresenter<V>> MvpPresenterFactroyImpl creater(Class<?> aClass) {
        //拿到創(chuàng)建presenter的注解
        CreatePresenterAnnotation annotation = aClass.getAnnotation(CreatePresenterAnnotation.class);
        Class<P> currentPresenter = null;
        if (annotation != null) {
            //獲取到具體的presenter
            currentPresenter = (Class<P>) annotation.value();

        }
        //傳給有參構(gòu)造
        return new MvpPresenterFactroyImpl(currentPresenter);
    }


    @Override
    public P createMvpPresenter() {
        try {
            return mPresenterClass.newInstance();
        } catch (Exception e) {
            throw new RuntimeException("注解為空,請(qǐng)檢查類(lèi)上是否聲明了@CreatePresenterAnnotation(xxx,class)注解");
        }
    }
}


4.自定義工廠類(lèi)(創(chuàng)建不同的presenter)

1.創(chuàng)建不同的presenter接口,定義set,get方法,獲取不同的presenter

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

    /**
     * 設(shè)置創(chuàng)建Presenter的工廠
     *
     * @param presenterFactory PresenterFactory類(lèi)型
     */
    void setPresenterFactory(IMvpPresenterFactroy<V, P> presenterFactory);

    /**
     * 獲取Presenter的工廠類(lèi)
     *
     * @return 返回PresenterMvpFactory類(lèi)型
     */
    IMvpPresenterFactroy<V, P> getPresenterFactory();

    /**
     * 獲取創(chuàng)建的Presenter
     *
     * @return 指定類(lèi)型的Presenter
     */
    P getMvpPresenter();
}

2.實(shí)現(xiàn)類(lèi),主要是獲取當(dāng)前presenter實(shí)列綁定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工廠類(lèi)
     */
    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("這個(gè)方法只能在getMvpPresenter()之前調(diào)用,如果Presenter已經(jīng)創(chuàng)建則不能再修改");
        }
        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;
        }
    }

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

    /**
     * 銷(xiāo)毀Presenter
     */
    public void onDestroy() {
        Log.e("perfect-mvp","Proxy onDestroy = ");
        if (mPresenter != null ) {
            onDetachMvpView();
            mPresenter.onDestroyPersenter();
            mPresenter = null;
        }
    }

    /**
     * 意外銷(xiāo)毀的時(shí)候調(diào)用
     * @return Bundle,存入回調(diào)給Presenter的Bundle和當(dāng)前Presenter的id
     */
    public Bundle onSaveInstanceState() {
        Log.e("perfect-mvp","Proxy onSaveInstanceState = ");
        Bundle bundle = new Bundle();
        getMvpPresenter();
        if(mPresenter != null){
            Bundle presenterBundle = new Bundle();
            //回調(diào)Presenter
            mPresenter.onSaveInstanceState(presenterBundle);
            bundle.putBundle(PRESENTER_KEY,presenterBundle);
        }
        return bundle;
    }

    /**
     * 意外關(guān)閉恢復(fù)Presenter
     * @param savedInstanceState 意外關(guān)閉時(shí)存儲(chǔ)的Bundler
     */
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        Log.e("perfect-mvp","Proxy onRestoreInstanceState = ");
        Log.e("perfect-mvp","Proxy onRestoreInstanceState Presenter = " + mPresenter);
        mBundle = savedInstanceState;
    }
}


5.如何使用

1.創(chuàng)建一個(gè)baseMvp類(lèi)
2.創(chuàng)建代理對(duì)象
3.在activity不同的生命周期調(diào)用代理對(duì)象的生命周期,實(shí)時(shí)綁定

/**
 * yangyoupeng  on 2018/4/25.
 * <p>
 * 想要使用mvp,需要繼承此BaseMvpActivity
 * <p>
 * TODO:注意  需要?jiǎng)?chuàng)建 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. 創(chuàng)建被代理對(duì)象,傳入默認(rèn)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.通過(guò)我們之前寫(xiě)的注解獲取presenter實(shí)列,做用在類(lèi)上

@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實(shí)列
IProductDetailActivityContract.View,這個(gè)是契約類(lèi),用來(lái)管理view,presenter接口,google推薦使用方法。
ProductDetailActivityPresenter:這個(gè)是具體的presenter

3.創(chuàng)建契約類(lèi)

/**
*用來(lái)管理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 的實(shí)現(xiàn)

/**
 * 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 :這個(gè)也就是契約類(lèi)里面的presenter抽象類(lèi)
好了,基本的使用寫(xiě)完了,不知道我有沒(méi)有寫(xiě)明白,如有不太明白的地方,歡迎留言一起討論。


C.網(wǎng)絡(luò)框架:

網(wǎng)絡(luò)框架封裝,網(wǎng)上的文章一大把,在這里我也把這個(gè)封裝框架放上來(lái),并在組件化mvp中如果去使用它。
1.retrofit管理類(lèi)
2.異常管理
3.封裝線程的切換
4. observer封裝,如請(qǐng)求服務(wù)器時(shí),顯現(xiàn)dialog,成功隱藏dialog
5. 在組件化mvp中綁定生命周期


1.retrofit管理類(lèi)

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

public class RetrofitManager {
    /**
     * 保存一個(gè)retrofit的實(shí)例,通過(guò)吸(baseUrl來(lái)獲?。?     */
    private HashMap<String, Retrofit> mRetrofitHashMap = new HashMap<>();

    private static final int DEFAULT_MILLISECONDS = 60000;             //默認(rèn)的超時(shí)時(shí)間


    /**
     * 內(nèi)部類(lèi)單列設(shè)計(jì)模式
     */
    private RetrofitManager() {
    }

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

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

    /**
     * 獲取retrofit的實(shí)例
     *
     * @return Retrofit
     */
    private Retrofit getRetrofit(String baseurl) {
        Retrofit retrofit;

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

        return retrofit;
    }

    /**
     * 創(chuàng)建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();
    }


    /**
     *根據(jù)各模塊業(yè)務(wù)接口 獲取不同的retrofit service接口對(duì)象
     */
    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 = "網(wǎng)絡(luò)不給力";
        switch (code){
            case UnknownHostException:
                errorMsg = "網(wǎng)絡(luò)不給力";
                break;
            case SocketTimeoutException:
                errorMsg = "請(qǐng)求網(wǎng)絡(luò)超時(shí)";
                break;
            case JSONException:
                errorMsg = "數(shù)據(jù)解析錯(cuò)誤";
                break;
            case ServiceException:
                errorMsg = "服務(wù)器處理請(qǐng)求出錯(cuò)";
                break;
            case CustomerException:
                errorMsg = "服務(wù)器無(wú)法處理請(qǐng)求";
                break;
            case RedirectException:
                errorMsg = "請(qǐng)求被重定向到其他頁(yè)面";
                break;
            case NoLoginException:
                errorMsg = "請(qǐng)重新登錄";
                break;
        }
        return errorMsg;
    }
**
 * yangyoupeng  on 2018/4/12.
 * <p>
 * 異常處理類(lèi)
 */
public class RxExceptionUtil {
    private static final String TAG = "RxExceptionUtil";
    public static String exceptionHandler(Throwable e) {
        String errorMsg = "未知錯(cuò)誤";
        if (e instanceof UnknownHostException) {
            errorMsg = "網(wǎng)絡(luò)不可用";
        } else if (e instanceof SocketTimeoutException) {
            errorMsg = "請(qǐng)求網(wǎng)絡(luò)超時(shí)";
        } 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 = "數(shù)據(jù)解析錯(cuò)誤";
        }

        return errorMsg;
    }

    private static String convertStatusCode(HttpException httpException){
        Log.d(TAG, "convertStatusCode: "+httpException.code());
        String msg;
        if (httpException.code() >= 500 && httpException.code() < 600) {
            msg = "服務(wù)器處理請(qǐng)求出錯(cuò)";
        } else if (httpException.code() >= 400 && httpException.code() < 500) {
            if(httpException.code() == 401){
//                throw new UnLoginException();
                msg = "請(qǐng)重新登錄";
            } else {
                msg = "服務(wù)器無(wú)法處理請(qǐng)求";
            }
        } else if (httpException.code() >= 300 && httpException.code() < 400) {
            msg = "請(qǐng)求被重定向到其他頁(yè)面";
        } 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表達(dá)式,大家有空可以去了解下

3.observer封裝,如請(qǐng)求服務(wù)器時(shí),顯現(xiàn)dialog,成功隱藏dialog

**
 * yangyoupeng  on 2018/4/12.
 * <p>
 * 請(qǐng)求服務(wù)器,返回的數(shù)據(jù)做同一處理
 */

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, "服務(wù)器返回的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, "錯(cuò)誤信息---" + e.getMessage() + "-----錯(cuò)誤碼: " + 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:  什么錯(cuò)誤: " + e + "-------錯(cuò)誤信息:" + 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) {//實(shí)名認(rèn)證
            EventBus.getDefault().post(new EventType(EventType.TO_REA_NNAME_AUTHENTICATION));
        } else {
            ToastUtils.showShortToast(errorMsg);
        }
    }

}
/**
 * yangyoupeng  on 2018/4/12.
 * <p>
 * 擴(kuò)展類(lèi),用來(lái)調(diào)用請(qǐng)求網(wǎng)絡(luò)時(shí)的進(jìn)度條
 */

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 {
    /**
     * 用來(lái) 綁定view 生命周期,解決rxjava內(nèi)存泄露
     *
     * @param
     * @return
     */
    <T> ObservableTransformer<T, T> bindLifeycle();

2.當(dāng)前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);
    }

實(shí)現(xiàn)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.在網(wǎng)絡(luò)請(qǐng)求中通過(guò).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);
                    }
                });
    }

到這里就結(jié)束啦,細(xì)節(jié)實(shí)在太多,沒(méi)辦法一篇文章寫(xiě)出來(lái)。所以代碼我都貼出來(lái)了,如有不太明白的地方歡迎一起探討交流, QQ: 1003605461。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,741評(píng)論 25 709
  • 不怕跌倒,所以飛翔 組件化開(kāi)發(fā) 參考資源 Android組件化方案 為什么要組件化開(kāi)發(fā) 解決問(wèn)題 實(shí)際業(yè)務(wù)變化非常...
    筆墨Android閱讀 3,094評(píng)論 0 0
  • 1. Android組件化開(kāi)發(fā) 在Android項(xiàng)目組件化之前,我們的項(xiàng)目都是像下圖那樣,一個(gè)單一工程下,根據(jù)不同...
    CHSmile閱讀 4,696評(píng)論 1 34
  • 今天要說(shuō)的事情都很重要。 01 健康很重要。 健康是一切的基礎(chǔ)。沒(méi)有健康不能工作,不能為家人做飯,不能去想去的地方...
    梅洛的聽(tīng)雨軒閱讀 649評(píng)論 0 1

友情鏈接更多精彩內(nèi)容