首先,展示一下封装好之后的项目的层级结构。
1、先创建一个RetrofitApiService.java
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。
2、封装RetrofitUtil
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; } }
3、封装DataManager,这里和原生的retrofit的封装一样该类用来管理RetrofitApiService中对应的各种API接口,当做Retrofit和presenter中的桥梁,Activity就不用直接和retrofit打交道了
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); } }
4、创建一个Presenter接口,这里就引入了Presenter这个东西了,后面我们根据具体的业务创建对应的实例去实现该接口。该接口主要面向数据,后面还会创建一个PresentView接口(面向View的),然后通过
BindPresentView方法将这2个接口关联起来,从而实现对数据和Activity的管理。
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); }
5、定义一个实现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) { //与具体视图进行绑定,留个子类进行扩展 } }
6、创建一个接口PresentView,面向视图View的接口,和前面的Prenseter配合使用
package com.xdw.retrofitrxmvpdemo.pv; /** * Created by 夏德旺 on 2017/12/8. */ //面向视图View的接口,和前面的Prenseter配合使用 public interface PresentView { //定义一个最基础的接口,里面就包含一个出错信息的回调 //因为大多数时候报错的时候都是采用一条信息提示 //如果需要负责的报错接口,请重载onError,是重载不是重写 void onError(String result); }
7、之前还已经创建了2个类,这里补充下,一个是UrlConstant(用来存放常量的,这里用来存放Base Url),另外一个是数据模型类UserInfo
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 + '}'; } }
8、下面就需要将具体的业务加入到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); }
9、写一个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; } }) ); } }
10、好了,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很方便,现在却加了这么多代码,视乎变得更麻烦了。但是自己仔细想想设计模式,当业务变得越来越复杂的时候,这种封装就会变得越来越有意义。
列举下之后像该项目中扩展业务的步骤,比如加一个订单功能。
操作步骤:
1、添加对应的model类Order
2、RetrofitApiService中添加对应的网络请求api
3、将新添加的api映射到DataManager中
4、添加业务对应的PrensentView实例OrderPv
5、添加业务对应的Presenter实例OrderPresenter
6、在需要该业务的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