Android 教你一步步搭建 MVP+Retrofit+RxJava 网络请求框架
好了,到这里我们就基本了解了 Retrofit 的整个工作流程。
3.RxJava
我们这篇文章主要介绍搭建整体网络请求框架,所以关于 RxJava 的基础知识,我这就不再详细介绍了,网上也有很多文章,对 RxJava 还不是很了解的同学,推荐你看一下扔物线的这篇文章[给 Android 开发者的 RxJava 详解](
)
下面我们来看一下 RxJava 和 retrofit 的结合使用,为了使 Rxjava 与 retrofit 结合,我们需要在 Retrofit 对象建立的时候添加一句代码addCallAdapterFactory(RxJavaCallAdapterFactory.create())
,当然你还需要在 build.gradle 文件中添加如下依赖:
????????compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
完整的代码如下:
`Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/v2/")
.addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())//支持 RxJava
.build();`
然后我们还需要修改 RetrofitService 中的代码:
`public interface RetrofitService {
@GET("book/search")
Observable<Book> getSearchBook(@Query("q") String name,
@Query("tag") String tag, @Query("start") int start,
@Query("count") int count);`
可以看到,在原来的 RetrofitService 中我们把 getSearchBook 方法返回的类型 Call 改为了 Observable,也就是被观察者。其他都没变。然后就是创建 RetrofitService 实体类:
RetrofitService service = retrofit.create(RetrofitService.class);
和上面一样,创建完 RetrofitService ,就可以调用里面的方法了:
Observable<Book> observable = service.getSearchBook("金瓶梅", null, 0, 1);
其实这一步,就是创建了一个 rxjava 中 observable,即被观察者,有了被观察者,就需要一个观察者,且订阅它:
`observable.subscribeOn(Schedulers.io())//请求数据的事件发生在 io 线程
.observeOn(AndroidSchedulers.mainThread())//请求完成后在主线程更显 UI
.subscribe(new Observer<Book>() {//订阅
@Override
public void onCompleted() {
//所有事件都完成,可以做些操作。。。
}
@Override
public void onError(Throwable e) {
e.printStackTrace(); //请求过程中发生错误
}
@Override
public void onNext(Book book) {//这里的 book 就是我们请求接口返回的实体类
}
}`
在上面中我们可以看到,事件的消费在 Android 主线程,所以我们还要在 build.gradle 中添加如下依赖:
????????compile 'io.reactivex:rxandroid:1.2.0'
这样我们就引入了 RxAndroid,RxAndroid 其实就是对 RxJava 的扩展。比如上面这个 Android 主线程在 RxJava 中就没有,因此要使用的话就必须得引用 RxAndroid。
4.实践
接下来我们就看看,在一个项目中上面三者是如何配合的。我们打开 Android Studio,新建一个项目取名为 MVPDemo。这个 demo 的功能也很简单,就是点击按钮调用上面的那个测试接口,将请求下来书的信息显示在屏幕上。首先我们来看一下这个工程的目录结构:
我们可以看到,在项目的包名下,我们建了三个主要的文件夹:app、service、ui。当然根据项目的需要你也可以添加更多其他的文件夹,比如一些工具类等。其中 app 文件夹中可以建一个 Application 类,用于设置应用全局的一些属性,这里为了使项目更加简单就没有添加;然后,我们再来看看 ui 文件夹下,这个文件夹下主要放一些关于界面的东西。在里面我们又建了三个文件夹:activity、adapter、fragment,我想看名字你就清楚里面要放什么了。最后我们在重点看看 service 文件夹中的东西。首先我们来看看里面重要的两个类:RetrofitHelper 和 RetrofitService。RetrofitHelper 主要用于 Retrofit 的初始化:
`public class RetrofitHelper {
private Context mCntext;
OkHttpClient client = new OkHttpClient();
GsonConverterFactory factory = GsonConverterFactory.create(new GsonBuilder().create());
private static RetrofitHelper instance = null;
private Retrofit mRetrofit = null;
public static RetrofitHelper getInstance(Context context){
if (instance == null){
instance = new RetrofitHelper(context);
}
return instance;
}
private RetrofitHelper(Context mContext){
mCntext = mContext;
init();
}
private void init() {
resetApp();
}
private void resetApp() {
mRetrofit = new Retrofit.Builder()
.baseUrl("https://api.douban.com/v2/")
.client(client)
.addConverterFactory(factory)
.addCallAdapterFactory(RxJavaCallA
dapterFactory.create())
.build();
}
public RetrofitService getServer(){
return mRetrofit.create(RetrofitService.class);
}
}`
代码并不复杂,其中 resetApp 方法,就是前面介绍的 Retrofit 的创建,getServer 方法就是为了获取 RetrofitService 接口类的实例化。然后定义了一个静态方法 getInstance 用于获取自身 RetrofitHelper 的实例化,并且只会实例化一次。
接下来,看一下 RetrofitService,其中代码还是上面一样:
`public interface RetrofitService {
@GET("book/search")
Observable<Book> getSearchBooks(@Query("q") String name,
@Query("tag") String tag, @Query("start") int start,
@Query("count") int count);
}`
然后我们依次来看一下 service 文件夹下的四个文件夹:entity、manager、presenter 和 view。其中 entity 下放我们请求的实体类,这里就是 Book。接下来我们来看一下 manager 中 DataManager。这个类其实就是为了让你更方便的调用 RetrofitService 中定义的方法:
`public class DataManager {
private RetrofitService mRetrofitService;
public DataManager(Context context){
this.mRetrofitService = RetrofitHelper.getInstance(context).getServer();
}
public Observable<Book> getSearchBooks(String name,String tag,int start,int count){
return mRetrofitService.getSearchBooks(name,tag,start,count);
}
}`
可以看到,在它的构造方法中,我们得到了 RetrofitService 的实例化,然后定义了一个和 RetrofitService 中同名的方法,里面其实就是调用 RetrofitService 中的这个方法。这样,把 RetrofitService 中定义的方法都封装到 DataManager 中,以后无论在哪个要调用方法时直接在 DataManager 中调用就可以了,而不是重复建立 RetrofitService 的实例化,再调用其中的方法。
好了,我们再来看一下 presenter 和 view,我们在前面说过,presenter 主要用于网络的请求以及数据的获取,view 就是将 presenter 获取到的数据进行展示。首先我们先来看 view,我们看到我们建了两个接口类 View 和 BookView,其中 View 是空的,主要用于和 Android 中的 View 区别开来:
`public interface View {
}`
然后让 BookView 继承自我们自己定义的 View :
`public interface BookView extends View {
void onSuccess(Book mBook);
void onError(String result);
}`
可以看到在里面定义两个方法,一个 onSuccess,如果 presenter 请求成功,将向该方法传入请求下来的实体类,也就是 Book,view 拿到这个数据实体类后,就可以进行关于这个数据的展示或其他的一些操作。如果请求失败,就会向这个 view 传入失败信息,你可以弹个 Toast 来提示请求失败。通常这两个方法比较常用,当然你可以根据项目需要来定义一些其他的方法。接下来我们看看 presenter 是如何进行网络请求的 。我们也定义了一个基础 Presenter:
`public interface Presenter {
void onCreate();
void onStart();//暂时没用到
void onStop();
void pause();//暂时没用到
void attachView(View view);
void attachIncomingIntent(Intent intent);//暂时没用到
}`
里面我们可以看到,定义了一些方法,前面几个 onCreate、onStart 等方法对应着 Activity 中生命周期的方法,当然没必要写上 Activity 生命周期中所有回调方法,通常也就用到了 onCreate 和 onStop,除非需求很复杂,在 Activity 不同生命周期请求的情况不同。接着我们定义了一个 attachView 方法,用于绑定我们定义的 View。也就是,你想把请求下来的数据实体类给哪个 View 就传入哪个 View。下面这个 attachIncomingIntent 暂且没用到,就不说了。好了,我们来看一下 BookPresenter 具体是怎么实现的:
`public class BookPresenter implements Presenter {
private DataManager manager;
private CompositeSubscription mCompositeSubscription;
private Context mContext;
private BookView mBookView;
private Book mBook;
public BookPresenter (Context mContext){
this.mContext = mContext;
}
@Override
public void onCreate() {
manager = new DataManager(mContext);
mCompositeSubscription = new CompositeSubscription();
}
@Override
public void onStart() {
}
@Override
public void onStop() {
if (mCompositeSubscription.hasSubscriptions()){
mCompositeSubscription.unsubscribe();
}
}
@Override
public void pause() {
}
@Override
public void attachView(View view) {
mBookView = (BookView)view;
}
@Override
public void attachIncomingIntent(Intent intent) {
}
public void getSearchBooks(String name,String tag,int start,int count){
mCompositeSubscription.add(manager.getSearchBooks(name,tag,start,count)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Book>() {
@Override
public void onCompleted() {
if (mBook != null){
mBookView.onSuccess(mBook);
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
mBookView.onError("请求失败!!");
}
@Override
public void onNext(Book book) {
mBook = book;
}
})
);
}
}`
BookPresenter 实现了我们定义的基础 Presenter,在 onCreate 中我们创建了 DataManager 的实体类,便于调用 RetrofitService 中的方法,还新建了一个 CompositeSubscription 对象,CompositeSubscription 是用来存放 RxJava 中的订阅关系的。注意请求完数据要及时清掉这个订阅关系,不然会发生内存泄漏。可在 onStop 中通过调用 CompositeSubscription 的 unsubscribe 方法来取消这个订阅关系,不过一旦调用这个方法,那么这个 CompositeSubscription 也就无法再用了,要想再用只能重新 new 一个。然后我们可以看到在 attachView 中,我们把 BookView 传进去。也就是说我们要把请求下来的实体类交给 BookView 来处理。接下来我们定义了一个方法 getSearchBooks,名字和入参都和请求接口 RetrofitService 中的方法相同。这里的这个方法也就是请求的具体实现过程。其实也很简单,就是向 CompositeSubscription 添加一个订阅关系。上面我们已经说过 manager.getSearchBooks 就是调用 RetrofitService 的 getSearchBooks 方法,而这个方法返回的是一个泛型为 Book 的 Observable,即被观察者,然后通过 subscribeOn(Schedulers.io())来定义请求事件发生在 io 线程,然后通过 observeOn(AndroidSchedulers.mainThread())来定义事件在主线程消费,即在主线程进行数据的处理,最后通过 subscribe 使观察者订阅它。在观察者中有三个方法:onNext、onCompleted、onError。当请求成功话,就会调用 onNext,并传入请求返回的 Book 实体类,我们在 onNext 中,把请求下来的 Book 实体类存到内存中,当请求结束后会调用 onCompleted,我们把请求下来的 Book 实体类交给 BookView 处理就可以了,如果请求失败,那么不会调用 onCompleted 而调用 onError,这样我们可以向 BookView 传递错误消息。
好了,这样我们我们就可以调用这个接口方法来进行网络的请求了,我们先写一下页面的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:orientation="vertical"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<Button
android:id="@+id/button"
android:onClick="getFollowers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="请求"/>
</LinearLayout>
界面很简单,一共两个控件,一个 Button,点击时进行网络请求,一个 TextView,用于显示请求下来的数据。然后我么看一下 Activity 中代码:
`public class MainActivity extends AppCompatActivity {
private TextView text;
private Button button;
private BookPresenter mBookPresenter = new BookPresenter(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);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBookPresenter.getSearchBooks("金瓶梅", null, 0, 1);
}
});
mBookPresenter.onCreate();
mBookPresenter.attachView(mBookView);
}
private BookView mBookView = new BookView() {
@Override
public void onSuccess(Book mBook) {
text.setText(mBook.toString());
评论