MVP+Retrofit+RxJava搭建网络请求平台

一、说明

       在Android开发中,网络请求是必不可少的一部分,在我的理解中,App相当于一个壳,它的主要功能是与服务端进行数据交换,用户在操作App时,无非就是通过App查看服务端数据或者将数据提交给服务端,当然,一个好的App也必须拥有良好的设计风格,确保良好的用户体验,要想做到良好的用户体验,首先,产品的设计一定要合理,我们在平时的开发过程中,也要关注产品的设计,对于不合理的设计一定要提出质疑,其次,开发人员应该具备较深的开发功底,对于Android的基础知识要了然于胸,比如Android的绘制流程、事件分发、线程切换、内存回收、网络请求等等,只有掌握了这些,我们才能避免App出现界面卡顿、事件冲突、内存泄露等问题,从而开发出用户体验良好的产品。在这篇文章中,我们来一起分析一下现在App开发中用到最多的网络请求模式:MVP+Retrofit+RxJava。

二、MVP

      何为MVP,MVP其实是一种解耦的方式,通过MVP模式,我们可以降低应用代码的耦合度,让代码变得更加清晰。很多Android的初学者在开发过程中,会将所用的功能都塞到Activity或者Fragment中,比如网络请求、线程切换等,这样虽然也可以完成功能,但是会导致Activity或者Fragment代码量剧增,在后期维护过程中,将会变得异常麻烦。我们在设计模式中学到过,对于不合理的设计就要进行纠正,在上面的例子中,这所以维护代码很困难,是因为我们将所用的功能都在Activity中处理,这不符合面向对象设计的第一个原则-----单一职责原则,所谓单一职责原则,是指一个类应该是一组相关性很高的函数和数据结构的封装,而我们现在的Activity显然不满足这一原则,Activity应该对应的是界面的显示,它应该只关注界面的显示部分,而至于其他的功能,比如网络请求等,应该由其他的类完成,所以,在MVP模式中,我们添加了Presenter类来分担Activity的其他功能,Activity只需要处理界面的显示,而为了降低类之间的耦合度,我们要遵循面向对象设计的另一个原则-----依赖倒置原则,即高层次模块不应该依赖低层次模块的具体实现,两者之间应该依赖其抽象,即Activity和Presenter之间都不应该直接依赖,而应该建立接口,两者之间依赖于接口,在MVP模式中,M指的是Model,表示数据逻辑和模型实体,对于功能较为复杂的模块,我们可以将一些逻辑封装到Model中,提供给Presenter调用,比如对数据库以及数据模型的操作等,通过Model我们可以控制Presenter的大小,以免其变得臃肿。后面,我们将通过实际代码来展现MVP模式的使用。

三、Retrofit

      何为Retrofit呢?Retrofit是一款用于Android和Java端的网络请求框架。由于其接口简单易用、扩展性强等特点,被广泛用于Android端网络请求,下面我们来一起看一下如何使用Retrofit来请求数据:

这里提供一个翻译的接口,接口定义如下:

请求格式:
    请求HOST:http://fy.iciba.com
    请求方法:POST
    请求URL:/ajax.php?a=fy&f=auto&t=auto
    请求参数w:表示要翻译的文字
    
返回格式:
{
	"status": 1,
	"content": {
		"from": "zh-CN",
		"to": "en-US",
		"vendor": "tencent",
		"out": "They think they'll be home.", // 返回的翻译内容
		"ciba_use": "\u6765\u81ea\u673a\u5668\u7ffb\u8bd1\u3002",
		"ciba_out": "",
		"err_no": 0
	}
}

下面,我们来看一下如何用Retrofit来请求翻译接口

1、添加依赖

打开App的build.gradle文件,添加

    compile 'com.squareup.retrofit2:retrofit:2.3.0' // retrofit依赖
    compile 'com.squareup.retrofit2:converter-gson:2.3.0' // retrofit中用来将Json格式数据转换成具体实体类对象的依赖

2、定义返回的实体类

翻译接口返回的数据格式是Json格式的,由于Java是面向对象的语言,我们在开发中需要将Json格式的数据转换成对应的实体类对象,这样开发者就可以直接操作实体类对象来操作数据了。这里推荐一款快速将Json格式数据转换成对应类的插件-----GsonFormat,它在Android Studio中的安装很简单,大家可以自行搜索。我们新建一个实体类,并利用GsonFormat实现其内容:

public class TranslateBean {

    /**
     * status : 1
     * content : {"from":"en-EU","to":"zh-CN","vendor":"tencent","out":"女人的心是一片深藏着秘密的海洋","ciba_use":"来自机器翻译。","ciba_out":"","err_no":0}
     */

    private int status;
    private ContentBean content;

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public ContentBean getContent() {
        return content;
    }

    public void setContent(ContentBean content) {
        this.content = content;
    }

    public static class ContentBean {
        /**
         * from : en-EU
         * to : zh-CN
         * vendor : tencent
         * out : 女人的心是一片深藏着秘密的海洋
         * ciba_use : 来自机器翻译。
         * ciba_out :
         * err_no : 0
         */

        private String from;
        private String to;
        private String vendor;
        private String out;
        private String ciba_use;
        private String ciba_out;
        private int err_no;
        private List word_mean;

        public List getWord_mean() {
            return word_mean;
        }

        public void setWord_mean(List word_mean) {
            this.word_mean = word_mean;
        }

        public String getWordMeanString() {
            if (word_mean == null) {
                return null;
            } else {
                String wordMeanString = "";
                for (String wordLine : word_mean) {
                    wordMeanString += wordLine;
                    wordMeanString += "\n";
                }
                return wordMeanString;
            }
        }

        public String getFrom() {
            return from;
        }

        public void setFrom(String from) {
            this.from = from;
        }

        public String getTo() {
            return to;
        }

        public void setTo(String to) {
            this.to = to;
        }

        public String getVendor() {
            return vendor;
        }

        public void setVendor(String vendor) {
            this.vendor = vendor;
        }

        public String getOut() {
            return out;
        }

        public void setOut(String out) {
            this.out = out;
        }

        public String getCiba_use() {
            return ciba_use;
        }

        public void setCiba_use(String ciba_use) {
            this.ciba_use = ciba_use;
        }

        public String getCiba_out() {
            return ciba_out;
        }

        public void setCiba_out(String ciba_out) {
            this.ciba_out = ciba_out;
        }

        public int getErr_no() {
            return err_no;
        }

        public void setErr_no(int err_no) {
            this.err_no = err_no;
        }
    }
}

3、定义网络请求接口

import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;

/**
 * Created by dell on 2018/9/6.
 */

public interface TranslateApi {
    @POST("ajax.php?a=fy&f=auto&t=auto")
    @FormUrlEncoded
    Call translateRequest(@Field("w") String input);
}

和其他的网络请求框架不一样,Retrofit将网络请求参数的定义放置在接口中完成,然后通过解读注解来解析参数,在上面的例子中,@post表示请求方法是POST请求,请求的URL是ajax.php?a=fy&f=auto&t=auto,@FormUrlEncoded表示请求参数是表单格式的,@Field("w")表示POST的请求参数的"w"字段。可能你会发现,上面的接口没有定义Post请求的Host部分,其实,因为对一个应用来说,Host部分一般是不变的,所以Retrofit支持将其全局配置。Retrofit支持多种请求方法以及参数的配置,具体大家可以自行搜索。

4、利用接口类发起网络请求

    private void requestTranslate() {
        Retrofit retrofit = new Retrofit.Builder() // 通过Builder模式构造一个Retrofit对象
                .baseUrl("http://fy.iciba.com/") // 配置HOST
                .addConverterFactory(GsonConverterFactory.create()) // 添加解读返回数据的对象,因为返回数据是Json格式的,所以这里蚕蛹GsonConverterFactory来解读
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
        TranslateApi translateApi = retrofit.create(TranslateApi.class); // 传入定义的接口,获得一个实现了TranslateApi接口的实体对象
        Call call = translateApi.translateRequest(mInputEditText.getText().toString()); // 调用请求方法,返回一个Call对象
        call.enqueue(new Callback() { // 调用Call对象的enqueue方法发起异步网络请求,enqueue方法需要传入一个Callback对象,用来处理网络请求的回调
            @Override
            public void onResponse(Call call, Response response) { // 网络请求正确的回调
                TranslateBean translateBean = response.body(); // 处理返回结果
                if (translateBean.getContent().getOut() != null) {
                    mOutputText.setText(translateBean.getContent().getOut());
                } else {
                    mOutputText.setText(translateBean.getContent().getWordMeanString());
                }
            }

            @Override
            public void onFailure(Call call, Throwable t) { // 网络请求错误的回调
                mOutputText.setText("翻译出错了!");
            }
        });

从使用方法来看,Retrofit最大的特点是将请求参数的定义配置在接口中,关于Retrofit的实现原理我们后面再进行分析。

四、RxJava

首先来看一下RxJava的定义:RxJava 是一个 基于事件流、实现异步操作的库,我们通过RxJava,主要是用来实现异步操作,这点和AsyncTask类似,RxJava由于其使用简单、功能强大、逻辑优雅的特点被广泛的在App开发中使用。在本篇文章中,我们只介绍如何让RxJava和Retrofit结合使用。

1、在build中添加RxJava依赖

    compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    compile 'io.reactivex.rxjava2:rxandroid:2.1.0'

2、修改请求接口的返回类型为RxJava的Observable对象

/**
 * Created by dell on 2018/9/6.
 */

public interface TranslateApi {
    @POST("ajax.php?a=fy&f=auto&t=auto")
    @FormUrlEncoded
    Observable translateRxRequest(@Field("w") String input); // 将返回类型修改为RxJava的Observable对象
}

3、给Retrofit添加处理返回结果为RxJava的Observable的适配器

   Retrofit retrofit = new Retrofit.Builder() // 通过Builder模式构造一个Retrofit对象
                .baseUrl("http://fy.iciba.com/") // 配置HOST
                .addConverterFactory(GsonConverterFactory.create()) // 添加解读返回数据的对象,因为返回数据是Json格式的,所以这里蚕蛹GsonConverterFactory来解读
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 添加处理返回结果为Observable类型的适配器
                .build();

4、利用接口发起网络请求

    private void requestTranslate() {
        Retrofit retrofit = new Retrofit.Builder() // 通过Builder模式构造一个Retrofit对象
                .baseUrl("http://fy.iciba.com/") // 配置HOST
                .addConverterFactory(GsonConverterFactory.create()) // 添加解读返回数据的对象,因为返回数据是Json格式的,所以这里蚕蛹GsonConverterFactory来解读
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // 添加处理返回结果为Observable类型的适配器
                .build();
        TranslateApi translateApi = retrofit.create(TranslateApi.class); // 传入定义的接口,获得一个实现了TranslateApi接口的实体对象

        Observable observable = translateApi.translateRxRequest(mInputEditText.getText().toString()); // 获得一个被观察者
        observable.subscribeOn(Schedulers.io()). // 表示在子线程中完成网络请求
                observeOn(AndroidSchedulers.mainThread()). // 表示在UI线程中处理网络结果
                subscribe(new Observer() { // 订阅观察者

            @Override
            public void onError(Throwable throwable) { // 接口出错
                mOutputText.setText("翻译出错了!");
            }

            @Override
            public void onComplete() {

            }

            @Override
            public void onSubscribe(@NonNull Disposable d) {

            }

            @Override
            public void onNext(TranslateBean translateBean) { // 处理返回数据
                if (translateBean.getContent().getOut() != null) {
                    mOutputText.setText(translateBean.getContent().getOut());
                } else {
                    mOutputText.setText(translateBean.getContent().getWordMeanString());
                }
            }
        });
    }

RxJava的实现原理我们后面会专门分析。

五、MVP+Retrofit+RxJava代码实践

根据上面对MVP、Retrofit以及RxJava的介绍,我们可以在应用中搭建MVP模式来进行网络请求,首先,新建一些Pacakge,将不同功能的代码分类:

MVP+Retrofit+RxJava搭建网络请求平台_第1张图片

其中activity包用来存储Activity的代码,api包用来存储描述网络请求接口的代码,bean包用来存储网络数据的实体类,presenter包用来存储presenter,utils包用来存储辅助类,view包用来定义显示方面的接口。

1、用GsonFormat生成网络数据实体类并放在在bean中

public class TranslateBean {

    /**
     * status : 1
     * content : {"from":"en-EU","to":"zh-CN","vendor":"tencent","out":"女人的心是一片深藏着秘密的海洋","ciba_use":"来自机器翻译。","ciba_out":"","err_no":0}
     */

    private int status;
    private ContentBean content;

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public ContentBean getContent() {
        return content;
    }

    public void setContent(ContentBean content) {
        this.content = content;
    }

    public static class ContentBean {
        /**
         * from : en-EU
         * to : zh-CN
         * vendor : tencent
         * out : 女人的心是一片深藏着秘密的海洋
         * ciba_use : 来自机器翻译。
         * ciba_out :
         * err_no : 0
         */

        private String from;
        private String to;
        private String vendor;
        private String out;
        private String ciba_use;
        private String ciba_out;
        private int err_no;
        private List word_mean;

        public List getWord_mean() {
            return word_mean;
        }

        public void setWord_mean(List word_mean) {
            this.word_mean = word_mean;
        }

        public String getWordMeanString() {
            if (word_mean == null) {
                return null;
            } else {
                String wordMeanString = "";
                for (String wordLine : word_mean) {
                    wordMeanString += wordLine;
                    wordMeanString += "\n";
                }
                return wordMeanString;
            }
        }

        public String getFrom() {
            return from;
        }

        public void setFrom(String from) {
            this.from = from;
        }

        public String getTo() {
            return to;
        }

        public void setTo(String to) {
            this.to = to;
        }

        public String getVendor() {
            return vendor;
        }

        public void setVendor(String vendor) {
            this.vendor = vendor;
        }

        public String getOut() {
            return out;
        }

        public void setOut(String out) {
            this.out = out;
        }

        public String getCiba_use() {
            return ciba_use;
        }

        public void setCiba_use(String ciba_use) {
            this.ciba_use = ciba_use;
        }

        public String getCiba_out() {
            return ciba_out;
        }

        public void setCiba_out(String ciba_out) {
            this.ciba_out = ciba_out;
        }

        public int getErr_no() {
            return err_no;
        }

        public void setErr_no(int err_no) {
            this.err_no = err_no;
        }
    }
}

2、定义网络请求接口并放在api包中

public interface TranslateApi {

    @POST("ajax.php?a=fy&f=auto&t=auto")
    @FormUrlEncoded
    Observable translateRxRequest(@Field("w") String input);
}

3、编写Retrofit辅助类并放在utils包下

public class RetrofitUtil {
    public static RetrofitUtil INSTANCE;

    OkHttpClient client = new OkHttpClient();
    GsonConverterFactory factory = GsonConverterFactory.create();
    private Retrofit mRetrofit = null;
    public static RetrofitUtil getInstance(){ // 通过单例模式生成一个全局唯一的RetrofitUtil对象
        if (INSTANCE == null) {
            synchronized (RetrofitUtil.class) {
                if (INSTANCE == null) {
                    INSTANCE = new RetrofitUtil();
                }
            }
        }
        return INSTANCE;
    }

    private RetrofitUtil(){
        init();
    }

    private void init() {
        mRetrofit = new Retrofit.Builder() // 配置Retrofit参数
                .baseUrl("http://fy.iciba.com/")
                .client(client)
                .addConverterFactory(factory)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }

    public TranslateApi getTranslateApi() {
        return mRetrofit.create(TranslateApi.class);
    }
}

RetrofitUtil是一个RetrofitUtil辅助类,它通过单例模式生成了全局唯一的一个RetrofitUtil对象,其中封装了初始化Retrofit的过程,这样做的目的是减少调用者的复杂程度,调用者通过RetrofitUtil提供的API可以轻松的获取一个TranslateApi的请求实例,而不用再去关注Retrofit的创建过程。

4、定义基类Presenter接口

public interface Presenter {
    void onCreate();
    void attachView(View view);
    void onStop();
}

里面我们可以看到,定义了一些方法,onCreate、onStop等方法对应着Activity中生命周期的方法,当然没必要写上Activity生命周期中所有回调方法,通常也就用到了onCreate和onStop,除非需求很复杂,在Activity不同生命周期请求的情况不同。接着我们定义了一个attachView方法,用于绑定我们定义的View。

5、定义基类View接口

public interface View {
}

6、定义ITranslatePresenter和ITranslateView

public interface ITranslatePresenter extends Presenter {
    public void requestTranslate(String word); // 请求网络接口
}

public interface ITranslateView extends View {
    public void requestTranslateSuccess(TranslateBean translateBean); // 处理网络请求成功的方法
    public void requestTranslateFail(); // 处理网络请求失败的方法
}

定义这两个接口的目的是为了让Activity和Presenter尽量不依赖其具体的实现类,而依赖其抽象。

7、实现TranslatePresenter

public class TranslatePresenter implements ITranslatePresenter {
    ITranslateView mTranslateView;

    @Override
    public void onCreate() {

    }

    @Override
    public void attachView(View view) {
        mTranslateView = (ITranslateView) view; // 绑定View
    }

    @Override
    public void onStop() {

    }

    @Override
    public void requestTranslate(String word) {
        Observable observable = RetrofitUtil.getInstance().getTranslateApi().translateRxRequest(word); // 获得一个被观察者
        observable.subscribeOn(Schedulers.io()). // 表示在子线程中完成网络请求
                observeOn(AndroidSchedulers.mainThread()). // 表示在UI线程中处理网络结果
                subscribe(new Observer() { // 订阅观察者

            @Override
            public void onError(Throwable throwable) { // 接口出错
                mTranslateView.requestTranslateFail(); // 调用View处理网络出错的情况
            }

            @Override
            public void onComplete() {

            }

            @Override
            public void onSubscribe(@NonNull Disposable d) {

            }

            @Override
            public void onNext(TranslateBean translateBean) { // 处理返回数据
                mTranslateView.requestTranslateSuccess(translateBean); // 调用View处理返回数据
            }
        });
    }
}

8、在Activity中实现ITranslateView接口并绑定Presenter

public class RetrofitTestActivity extends AppCompatActivity implements ITranslateView {
    ITranslatePresenter mTranslatePresenter; // 声明Presenter

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_retrofit_test);

        mTranslatePresenter = new TranslatePresenter(); // 生成对应的Presenter
        mTranslatePresenter.onCreate(); // 调用Presenter的onCreate方法
        mTranslatePresenter.attachView(this); // 给Presenter绑定View
    }

    private void requestTranslate() { // 调用Presenter请求网络数据
        mTranslatePresenter.requestTranslate(mInputEditText.getText().toString());
    }

    @Override
    public void requestTranslateSuccess(TranslateBean translateBean) { // 处理网络请求数据
        if (translateBean.getContent().getOut() != null) {
            mOutputText.setText(translateBean.getContent().getOut());
        } else {
            mOutputText.setText(translateBean.getContent().getWordMeanString());
        }
    }

    @Override
    public void requestTranslateFail() { // 处理网络请求失败
        mOutputText.setText("翻译出错了!");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mTranslatePresenter.onStop();
    }
}

可以看到,Activity只需要调用Presenter提供的接口去请求网络数据,并且实现处理网络请求成功和失败的接口即可,我们将具体的网络请求以及对网络返回的结果的处理逻辑都交由给了Presenter去处理,Activity只需要关注显示的逻辑即可。

优化

1、避免RxJava内存泄露

在上面的例子中,我们的被观察者(Observable)在订阅了观察者(Observer)后,并没有解除订阅关系,这样就有可能会造成内存泄露,我们可以新建了一个CompositeDisposable对象,CompositeDisposable是用来存放RxJava中的订阅关系的,在不需要在请求数据后取消订阅关系,避免内存泄露。

public class TranslatePresenter implements ITranslatePresenter {
    ITranslateView mTranslateView;
    CompositeDisposable mCompositeDisposable;

    @Override
    public void onCreate() {
        mCompositeDisposable = new CompositeDisposable();
    }

    @Override
    public void attachView(View view) {
        mTranslateView = (ITranslateView) view; // 绑定View
    }

    @Override
    public void onStop() {
        if (mCompositeDisposable != null) {
            mCompositeDisposable.clear(); // 在不需要在进行网络请求后解除订阅关系,避免内存泄露
            mCompositeDisposable = null;
        }
        mTranslateView = null;
    }

    @Override
    public void requestTranslate(String word) {
        Observable observable = RetrofitUtil.getInstance().getTranslateApi().translateRxRequest(word); // 获得一个被观察者
        observable.subscribeOn(Schedulers.io()). // 表示在子线程中完成网络请求
                observeOn(AndroidSchedulers.mainThread()). // 表示在UI线程中处理网络结果
                subscribe(new Observer() { // 订阅观察者

            @Override
            public void onError(Throwable throwable) { // 接口出错
                mTranslateView.requestTranslateFail(); // 调用View处理网络出错的情况
            }

            @Override
            public void onComplete() {

            }

            @Override
            public void onSubscribe(@NonNull Disposable d) {
                mCompositeDisposable.add(d); // 添加订阅关系到CompositeDisposable中
            }

            @Override
            public void onNext(TranslateBean translateBean) { // 处理返回数据
                mTranslateView.requestTranslateSuccess(translateBean); // 调用View处理返回数据
            }
        });
    }
}

Presenter的onStop方法是我们在Activity的onDestory方法中调用的,既然Activity都销毁了,那么肯定也不会调用Presenter进行网络请求了。

2、封装Presenter公共部分

在上面的代码中,Presenter有些部分属于所有的Presenter都需要的部分,我们可以编写一个BasePresenter基类:

public abstract class BasePresenter implements Presenter {
    View mView;
    CompositeDisposable mCompositeDisposable;

    @Override
    public void onCreate() {
        mCompositeDisposable = new CompositeDisposable();
    }

    @Override
    public void attachView(View view) {
        mView = view; // 绑定View
    }

    @Override
    public void onStop() {
        if (mCompositeDisposable != null) {
            mCompositeDisposable.clear(); // 在不需要在进行网络请求后解除订阅关系,避免内存泄露
            mCompositeDisposable = null;
        }
        mView = null;
    }

    /**
     * 统一处理添加订阅
     * @param observable
     * @param observer
     */
    protected void addSubscrible(Observable observable, Observer observer) {
        if (observable != null && observer != null) {
            observable.subscribeOn(Schedulers.io()). // 表示在子线程中完成网络请求
                    observeOn(AndroidSchedulers.mainThread()).  // 表示在UI线程中处理网络结果
                    subscribe(observer);  // 订阅观察者
        }
    }
}
public class TranslatePresenter extends BasePresenter implements ITranslatePresenter {

    @Override
    public void requestTranslate(String word) {
        final ITranslateView translateView = (ITranslateView) mView;
        Observable observable = RetrofitUtil.getInstance().getTranslateApi().translateRxRequest(word); // 获得一个被观察者
        addSubscrible(observable, new Observer() { // 订阅观察者

            @Override
            public void onError(Throwable throwable) { // 接口出错
                translateView.requestTranslateFail(); // 调用View处理网络出错的情况
            }

            @Override
            public void onComplete() {

            }

            @Override
            public void onSubscribe(@NonNull Disposable d) {
                mCompositeDisposable.add(d); // 添加订阅关系到CompositeDisposable中
            }

            @Override
            public void onNext(TranslateBean translateBean) { // 处理返回数据
                translateView.requestTranslateSuccess(translateBean); // 调用View处理返回数据
            }
        });
    }
}

按照类似的思想,我们可以将Activity中处理Presenter的公共部分也在BaseActivity中处理

public abstract class BaseActivity extends AppCompatActivity implements View {
    Presenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initPresenter();

        if (mPresenter != null) {
            mPresenter.onCreate();
            mPresenter.attachView(this);
        }
    }

    protected abstract void initPresenter();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.onStop();
            mPresenter = null;
        }
    }
}
public class RetrofitTestActivity extends BaseActivity implements ITranslateView {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_retrofit_test);
    }

    @Override
    protected void initPresenter() {
        mPresenter = new TranslatePresenter();
    }

    private void requestTranslate() {
        ((ITranslatePresenter)mPresenter).requestTranslate(mInputEditText.getText().toString());
    }

    @Override
    public void requestTranslateSuccess(TranslateBean translateBean) { // 处理网络请求数据
        if (translateBean.getContent().getOut() != null) {
            mOutputText.setText(translateBean.getContent().getOut());
        } else {
            mOutputText.setText(translateBean.getContent().getWordMeanString());
        }
    }

    @Override
    public void requestTranslateFail() { // 处理网络请求失败
        mOutputText.setText("翻译出错了!");
    }
}

可以看到,经过优化,我们的Activity和Presenter在使用时变得方便了许多,但是在相互调用时还是需要强类型转换,这是因为每个Activity和Presenter的功能都是不一样的,在基类中不能依赖具体的实现类,有没有什么办法可以去除强类型转换呢?答案是通过泛型:

public abstract class BaseActivity

extends AppCompatActivity implements View { P mPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initPresenter(); if (mPresenter != null) { mPresenter.onCreate(); mPresenter.attachView(this); } } protected abstract void initPresenter(); @Override protected void onDestroy() { super.onDestroy(); if (mPresenter != null) { mPresenter.onStop(); mPresenter = null; } } }

public class RetrofitTestActivity extends BaseActivity implements ITranslateView {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_retrofit_test);
    }

    @Override
    protected void initPresenter() {
        mPresenter = new TranslatePresenter();
    }

    private void requestTranslate() {
        mPresenter.requestTranslate(mInputEditText.getText().toString());
    }

    @Override
    public void requestTranslateSuccess(TranslateBean translateBean) { // 处理网络请求数据
        if (translateBean.getContent().getOut() != null) {
            mOutputText.setText(translateBean.getContent().getOut());
        } else {
            mOutputText.setText(translateBean.getContent().getWordMeanString());
        }
    }

    @Override
    public void requestTranslateFail() { // 处理网络请求失败
        mOutputText.setText("翻译出错了!");
    }
}

我们BaseActivity中将Presenter声明为泛型,具体的Presenter类型在具体的Activity中指定,这样在具体的Activity中使用Presenter就不需要进行类型转换了。类似的,我们修改Presenter的代码:

public abstract class BasePresenter implements Presenter {
    V mView;
    CompositeDisposable mCompositeDisposable;

    @Override
    public void onCreate() {
        mCompositeDisposable = new CompositeDisposable();
    }

    @Override
    public void attachView(View view) {
        mView = (V) view; // 绑定View
    }

    @Override
    public void onStop() {
        if (mCompositeDisposable != null) {
            mCompositeDisposable.clear(); // 在不需要在进行网络请求后解除订阅关系,避免内存泄露
            mCompositeDisposable = null;
        }
        mView = null;
    }

    /**
     * 统一处理添加订阅
     * @param observable
     * @param observer
     */
    protected void addSubscrible(Observable observable, Observer observer) {
        if (observable != null && observer != null) {
            observable.subscribeOn(Schedulers.io()). // 表示在子线程中完成网络请求
                    observeOn(AndroidSchedulers.mainThread()).  // 表示在UI线程中处理网络结果
                    subscribe(observer);  // 订阅观察者
        }
    }
}
public class TranslatePresenter extends BasePresenter implements ITranslatePresenter {

    @Override
    public void requestTranslate(String word) {
        Observable observable = RetrofitUtil.getInstance().getTranslateApi().translateRxRequest(word); // 获得一个被观察者
        addSubscrible(observable, new Observer() { // 订阅观察者

            @Override
            public void onError(Throwable throwable) { // 接口出错
                mView.requestTranslateFail(); // 调用View处理网络出错的情况
            }

            @Override
            public void onComplete() {

            }

            @Override
            public void onSubscribe(@NonNull Disposable d) {
                mCompositeDisposable.add(d); // 添加订阅关系到CompositeDisposable中
            }

            @Override
            public void onNext(TranslateBean translateBean) { // 处理返回数据
                mView.requestTranslateSuccess(translateBean); // 调用View处理返回数据
            }
        });
    }
}

六、总结

网络请求是Android中必不可少的环节,这篇文章介绍了如何搭建一个Android开发中很常用的网络请求模式:MVP+Retrofit+RxJava,但是这些还只停留在用的层面,对于其其实现原理,我们还需要去分析Retrofit和RxJava的源码。

你可能感兴趣的:(android)