一步步搭建Retrofit+RxJava+MVP網絡請求框架(一)

首先,展現一下封裝好以後的項目的層級結構。 
一、先建立一個RetrofitApiService.javaphp

 

package com.xdw.retrofitrxmvpdemo.http;

import com.xdw.retrofitrxmvpdemo.model.UserInfo;

import retrofit2.http.GET;
import retrofit2.http.Query;
import rx.Observable;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

public interface RetrofitApiService {

    @GET("userinfo")
    Observable<UserInfo> getUserInfo(@Query("uid") int uid);

}

 

這裏就是把原生的retrofit中的Call換成了RxJava中的Observable。java

二、封裝RetrofitUtilandroid

package com.xdw.retrofitrxmvpdemo.http;

import android.content.Context;

import com.google.gson.GsonBuilder;
import com.xdw.retrofitrxmvpdemo.constant.UrlConstant;

import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

public class RetrofitUtil {
    private Context mCntext;
    //聲明Retrofit對象
    private Retrofit mRetrofit;
    //聲明RetrofitApiService對象
    private RetrofitApiService retrofitApiService;
    OkHttpClient client = new OkHttpClient();
    GsonConverterFactory factory = GsonConverterFactory.create(new GsonBuilder().create());

    //因爲該對象會被頻繁調用,採用單例模式,下面是一種線程安全模式的單例寫法
    private volatile static RetrofitUtil instance;

    public static RetrofitUtil getInstance(Context context){
        if (instance == null) {
            synchronized (RetrofitUtil.class) {
                if (instance == null) {
                    instance = new RetrofitUtil(context);
                }
            }
        }
        return instance;
    }
    private RetrofitUtil(Context mContext){
        mCntext = mContext;
        init();
    }

    //初始化Retrofit
    private void init() {
        mRetrofit = new Retrofit.Builder()
                .baseUrl(UrlConstant.BASE_URL)
                .client(client)
                .addConverterFactory(factory)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
        retrofitApiService = mRetrofit.create(RetrofitApiService.class);
    }

    public RetrofitApiService getRetrofitApiService(){
        return retrofitApiService;
    }
}

 

三、封裝DataManager,這裏和原生的retrofit的封裝同樣該類用來管理RetrofitApiService中對應的各類API接口,當作Retrofit和presenter中的橋樑,Activity就不用直接和retrofit打交道了json

 

package com.xdw.retrofitrxmvpdemo.manager;

import android.content.Context;

import com.xdw.retrofitrxmvpdemo.http.RetrofitApiService;
import com.xdw.retrofitrxmvpdemo.http.RetrofitUtil;
import com.xdw.retrofitrxmvpdemo.model.UserInfo;

import rx.Observable;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

//該類用來管理RetrofitApiService中對應的各類API接口,
// 當作Retrofit和presenter中的橋樑,Activity就不用直接和retrofit打交道了
public class DataManager {
    private RetrofitApiService mRetrofitService;
    private volatile static DataManager instance;

    private DataManager(Context context){
        this.mRetrofitService = RetrofitUtil.getInstance(context).getRetrofitApiService();
    }
    //因爲該對象會被頻繁調用,採用單例模式,下面是一種線程安全模式的單例寫法
    public static DataManager getInstance(Context context) {
        if (instance == null) {
            synchronized (DataManager.class) {
                if (instance == null) {
                    instance = new DataManager(context);
                }
            }
        }
        return instance;
    }

    //將retrofit的業務方法映射到DataManager中,之後統一用該類來調用業務方法
    //之後再retrofit中增長業務方法的時候,相應的這裏也要添加,好比添加一個getOrder
    public Observable<UserInfo> getUserInfo(int uid){
        return mRetrofitService.getUserInfo(uid);
    }
}
 

 

四、建立一個Presenter接口,這裏就引入了Presenter這個東西了,後面咱們根據具體的業務建立對應的實例去實現該接口。該接口主要面向數據,後面還會建立一個PresentView接口(面向View的),而後經過設計模式

BindPresentView方法將這2個接口關聯起來,從而實現對數據和Activity的管理。api

package com.xdw.retrofitrxmvpdemo.presenter;



import com.xdw.retrofitrxmvpdemo.pv.PresentView;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

public interface Presenter {
    //Presenter初始化
    void onCreate();
    //銷燬
    void onDestroy();
    //綁定視圖
    void BindPresentView(PresentView presentView);
}

 

五、定義一個實現Presenter的基礎類BasePresenter,後續具體功能類繼承於它,主要是爲了在該類中寫一些共用方法,好比
CompositeSubscription的建立與銷燬。(CompositeSubscription幹嗎用的能夠查閱RxJava的資料)安全

 

package com.xdw.retrofitrxmvpdemo.presenter;


import com.xdw.retrofitrxmvpdemo.pv.PresentView;

import rx.subscriptions.CompositeSubscription;

/**
 * Created by 夏德旺 on 2017/12/8.
 */
//定義一個Presenter的基礎類,後續具體功能類繼承於它
public class BasePresenter implements Presenter {
    //聲明一個CompositeSubscription對象,注意是protected修飾符,便於子類進行調用
    protected CompositeSubscription mCompositeSubscription;
    @Override
    public void onCreate() {
        //在基礎類中對CompositeSubscription進行初始化,子類中就不用再寫一次
        //子類若是須要對onCreate進行重寫,記得先調用super.onCreate();
        mCompositeSubscription = new CompositeSubscription();
    }

    @Override
    public void onDestroy() {
        //釋放CompositeSubscription,不然會形成內存泄漏
        if (mCompositeSubscription.hasSubscriptions()){
            mCompositeSubscription.unsubscribe();
        }
    }

    @Override
    public void BindPresentView(PresentView presentView) {
        //與具體視圖進行綁定,留個子類進行擴展
    }
}

 

六、建立一個接口PresentView,面向視圖View的接口,和前面的Prenseter配合使用網絡

 

package com.xdw.retrofitrxmvpdemo.pv;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

//面向視圖View的接口,和前面的Prenseter配合使用
public interface PresentView {
    //定義一個最基礎的接口,裏面就包含一個出錯信息的回調
    //由於大多數時候報錯的時候都是採用一條信息提示
    //若是須要負責的報錯接口,請重載onError,是重載不是重寫
    void onError(String result);
}

 


七、以前還已經建立了2個類,這裏補充下,一個是UrlConstant(用來存放常量的,這裏用來存放Base Url),另一個是數據模型類UserInfoapp

 

UrlConstant.java:框架

 

package com.xdw.retrofitrxmvpdemo.constant;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

public class UrlConstant {
    /**
     * 域名:
     * 調試:http://10.1.1.192
     */
    public static final String BASE_URL="http://10.1.1.192";
}

 

UserInfo.java:

package com.xdw.retrofitrxmvpdemo.model;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

public class UserInfo {
    private String username;
    private int age;

    public UserInfo(String username, int age) {
        this.username = username;
        this.age = age;
    }

    public String getUsername() {
        return username;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}

 

八、下面就須要將具體的業務加入到Presenter中了,先寫一個UserInfoPv繼承自PresentView接口,這個是根據具體業務添加。

 

package com.xdw.retrofitrxmvpdemo.pv;

import com.xdw.retrofitrxmvpdemo.model.UserInfo;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

public interface UserInfoPv extends PresentView {
    //對基礎接口PresentView進行擴展,添加onSuccess回調
    //由於該回調與具體的業務對應,因此不能寫到基礎接口裏面
    //好比UserInfo的回調就建立一個UserInfoPv的接口,若是新增一個Order的業務,
    //則新增一個OrderPv的接口
    void onSuccess(UserInfo userInfo);
}
 

 

九、寫一個UserInfoPresenter繼承BasePresenter類,在該類中實現數據的請求,而且將該類與業務對應的PresentView進行綁定,這裏是和UserInfoPv綁定

 

package com.xdw.retrofitrxmvpdemo.presenter;

import android.content.Context;

import com.xdw.retrofitrxmvpdemo.manager.DataManager;
import com.xdw.retrofitrxmvpdemo.model.UserInfo;
import com.xdw.retrofitrxmvpdemo.pv.PresentView;
import com.xdw.retrofitrxmvpdemo.pv.UserInfoPv;

import rx.Observer;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

//該類是具體業務presenter,如需增長另外一個業務,好比Order
//則能夠再建立一個OrderPresenter
public class UserInfoPresenter extends BasePresenter {
    private Context mContext;
    private UserInfoPv mUserInfoPv;
    private UserInfo mUserInfo;
    public UserInfoPresenter (Context context){
        this.mContext = context;
    }

    @Override
    public void BindPresentView(PresentView presentView) {
        mUserInfoPv = (UserInfoPv)presentView;
    }

    //在presenter中實現業務邏輯,此處會調用前面封裝好的retrofit的東西
    //將處理結果綁定到對應的PresentView實例,這樣Activity和PresentView實例綁定好以後,
    //Activity->PresentView->Presenter->retrofit的關係就打通了
    public void getUserInfo(int uid){
        super.mCompositeSubscription.add(DataManager.getInstance(mContext).getUserInfo(uid)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<UserInfo>() {
                    @Override
                    public void onCompleted() {
                        if (mUserInfo != null){
                            mUserInfoPv.onSuccess(mUserInfo);
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                        mUserInfoPv.onError("請求失敗!!");
                    }

                    @Override
                    public void onNext(UserInfo userInfo) {
                        mUserInfo = userInfo;
                    }
                })
        );
    }
}

 

十、好了,retrofit與rxjava,presenter相關的東西都封裝好了,接下來輪到以前苦逼的Activity出場了,在Activity中展現presenter傳遞過來的數據。

 

package com.xdw.retrofitrxmvpdemo.activity;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.xdw.retrofitrxmvpdemo.R;
import com.xdw.retrofitrxmvpdemo.model.UserInfo;
import com.xdw.retrofitrxmvpdemo.presenter.UserInfoPresenter;
import com.xdw.retrofitrxmvpdemo.pv.UserInfoPv;

public class MainActivity extends AppCompatActivity {
    private TextView text;
    private Button button;
    //定義須要調用的presenter對象
    private UserInfoPresenter mUserInfoPresenter =new UserInfoPresenter(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView)findViewById(R.id.text);
        button = (Button)findViewById(R.id.button);
        //在Activity建立的時候同時初始化presenter,這裏onCreater不是指的建立presenter對象,
        // 而是作一些presenter的初始化操做,名字應該取名init更好理解點,我這裏就不重命名了
        mUserInfoPresenter.onCreate();
        //將presenter和PresentView進行綁定,實際上就是將presenter和Activity視圖綁定,
        //這個是MVP模式中V與P交互的關鍵
        mUserInfoPresenter.BindPresentView(mUserInfoPv);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //點擊按鈕觸發presenter裏面的方法
                mUserInfoPresenter.getUserInfo(1);
            }
        });

    }

    //採用內部類方法定義presentView對象,該對象用來將Activity和presenter進行綁定
    //綁定了之後主線程中就能夠經過回調來獲取網絡請求的數據
    private UserInfoPv mUserInfoPv = new UserInfoPv(){
        @Override
        public void onSuccess(UserInfo userInfo) {
            text.setText(userInfo.toString());
        }

        @Override
        public void onError(String result) {
            Toast.makeText(MainActivity.this,result, Toast.LENGTH_SHORT).show();
        }
    };

    //在Activity銷燬的時候,必定要對CompositeSubscription進行釋放,不然會形成內存泄漏
    //釋放操做封裝到了presenter的ondestroy方法中
    @Override
    protected void onDestroy(){
        super.onDestroy();
        mUserInfoPresenter.onDestroy();
    }
}

 

補充下:截圖中的Result這個類是我實際項目中用到的,這裏簡化了下業務邏輯,就沒用到它。

Result.java:

 

package com.xdw.retrofitrxmvpdemo.model;

/**
 * Created by 夏德旺 on 2017/12/8.
 */

public class Result<T> {
    private int code;
    private String msg;
    private T data;


    public Result(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public T getData() {
        return data;
    }

    @Override
    public String toString() {
        return "Result{" +
                "code=" + code +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

 

 


最後說明下,可能有的人以爲原本以前直接在Activity中寫retrofit很方便,如今卻加了這麼多代碼,視乎變得更麻煩了。可是本身仔細想一想設計模式,當業務變得愈來愈複雜的時候,這種封裝就會變得愈來愈有意義。

 

列舉下以後像該項目中擴展業務的步驟,好比加一個訂單功能。

操做步驟:

一、添加對應的model類Order

二、RetrofitApiService中添加對應的網絡請求api

三、將新添加的api映射到DataManager中

四、添加業務對應的PrensentView實例OrderPv

五、添加業務對應的Presenter實例OrderPresenter

六、在須要該業務的UI線程(Activity或Fragment)中調用具體業務對應的Presenter

看着新添加一個業務的過程是否是不少?可是邏輯很清晰,擴展很容易,寫代碼不是說代碼量越少,表明代碼寫的越好越有效率。

以後還會對該框架進一步封裝,加入攔截器,網絡請求等待框等功能的封裝。

 

補充下我這裏測試用的服務端源碼,就一個index.php文件:

<?php 
header("Content-Type: application/json; charset=utf-8");
    $uid=$_GET['uid'];
    if($uid==1){
        $arr['code']=200;
        $arr['msg']='ok';
        $data['username']='xiadewang';
        $data['age']=30;
        $arr['userinfo']=$data;
        echo json_encode($data,JSON_UNESCAPED_UNICODE);            
    }

 

android代碼連接:http://download.csdn.net/download/aaaabbbb1019/10151399

相關文章
相關標籤/搜索