之前写过一个类似的框架搭建,但是遗留下了一个问题就是,Rxjava中的订阅何如与项目中的Activity和Fragment如何绑定生命周期,避免内存泄漏以及抛出一些异常问题,这里使用了RxLifecycle。不多说,上代码。(该例子中使用的Rxjava,RxLifecycle都是2.0以下的,2.0上手难度有点高,况且我1.0还不懂)。
一、Presenter基类:
public abstract class BasePresenter<V> implements Presenter<V> {
protected WeakReference<V> weakReference;
@Override
public void attachView(V view) {
this.weakReference = new WeakReference<V>(view);
}
protected V getView(){
return weakReference.get();
}
@Override
public void detachView() {
if (weakReference != null){
weakReference.clear();
weakReference = null;
}
}
}
这里还有Presenter基类里面的两个接口,用于绑定以及解绑view
public interface Presenter<V> {
void attachView(V view);
void detachView();
}
二、Activity基类,基类继承RxLifeCycle中的RxAppCompatActivity,用于生命周期的绑定:
public abstract class BaseActivity<V,P extends BasePresenter<V>> extends RxAppCompatActivity{
public Activity mActivity;
public P mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = this;
mPresenter = createPresenter();
}
@Override
protected void onResume() {
super.onResume();
mPresenter.attachView((V)this);
}
protected abstract P createPresenter();
@Override
protected void onDestroy() {
if (mPresenter != null){
mPresenter.detachView();
}
super.onDestroy();
}
}
三、Fragment基类,同理:
public abstract class BaseFragment<V,P extends BasePresenter<V>> extends RxFragment {
public Activity mActivity;
public P mPresenter;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mPresenter = createPresent();
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
mPresenter.attachView((V)this);
}
protected abstract P createPresent();
@Override
public void onDestroy() {
if (mPresenter != null) {
mPresenter.detachView();
}
super.onDestroy();
}
}
我这里的attachView()方法是放在onResume()方法中,有人放在onCreate/onCreateView中,我觉得这样肯能会出问题,就是当返回某界面时,该界面已经执行了onDestroy()方法,也就是说,把View的弱引用消除了,这时就可能会抛异常了。应该是这样吧?我也是自己瞎想的。
四、Module的基类,这里就是将观察者与被观察者关联起来,同时使用bindUntilEvent绑定Activity的生命周期,以至于当Activity执行onDestroy()时,观察者与被观察者解除订阅关系,这样就可以防止内存泄漏等问题。
public class BaseModule {
protected BaseActivity baseActivity;
protected BaseFragment baseFragment;
public BaseModule(BaseActivity baseActivity) {
this.baseActivity = baseActivity;
}
public BaseModule(BaseFragment baseFragment) {
this.baseFragment = baseFragment;
}
protected <T> void doActivitySubscribe(Observable<T> observer, Subscriber<T> subscriber){
observer.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(baseActivity.<T>bindUntilEvent(ActivityEvent.DESTROY))
.subscribe(subscriber);
}
protected <T> void doFragmentSubscribe(Observable<T> observer, Subscriber<T> subscriber){
observer.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(baseFragment.<T>bindUntilEvent(FragmentEvent.DESTROY))
.subscribe(subscriber);
}
}
五,接下来看Subscriber观察者,这里面有一些对网络请求的处理:
public abstract class HttpSubscriber<T> extends Subscriber<BaseBen<T>> {
private Context context;
private boolean isShowDialog;
private LoadingDialog loadingDialog;
public HttpSubscriber(Context context, boolean isShowDialog) {
this.context = context;
this.isShowDialog = isShowDialog;
}
@Override
public void onStart() {
super.onStart();
Log.i("tag","onStart");
if (InternetUtil.isNetWorking(context)){
if (loadingDialog ==null && isShowDialog){
loadingDialog = new LoadingDialog.Builder(context).create();
loadingDialog.show();
}
}else {
Toast.showToast(context,"暂无网络");
onError("暂无网络");
if (!isUnsubscribed()){
unsubscribe();
}
}
}
@Override
public void onCompleted() {
Log.i("tag","onCompleted");
if (!isUnsubscribed()){
unsubscribe();
}
if (loadingDialog != null && isShowDialog && loadingDialog.isShowing()){
loadingDialog.dismiss();
loadingDialog = null;
}
}
@Override
public void onError(Throwable e) {
Log.i("tag","onErrorMessage:"+e.getMessage());
if(e instanceof Exception){
if ( e instanceof JsonParseException ||e instanceof JSONException){
onError("数据解析异常");
Toast.showToast(context,"数据解析异常");
}else if (e instanceof ConnectException){
onError("网络连接异常");
Toast.showToast(context,"网络连接异常");
}else if (e instanceof SocketTimeoutException){
onError("请求超时");
Toast.showToast(context,"请求超时");
}else if (e instanceof HttpException){
Toast.showToast(context,"服务异常");
onError("服务器异常");
}else if (e instanceof UnknownHostException){
Toast.showToast(context,"服务Host异常");
onError("服务Host异常");
}else {
onError("未知异常");
Toast.showToast(context,"异常:"+e.getMessage());
}
}else {
onError("未知异常");
Toast.showToast(context,"未知异常");
}
if (loadingDialog != null && isShowDialog && loadingDialog.isShowing()){
loadingDialog.dismiss();
loadingDialog = null;
}
}
@Override
public void onNext(BaseBen<T> tBaseBen) {
onSuccess(tBaseBen);
}
public abstract void onSuccess(BaseBen<T> tBaseBen);
public abstract void onError(String msg);
}
六,对返回的bean进行封装
public class BaseBen<T> {
private int code;
private String msg;
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
七,接下来是网络层:OkHttp+Retrofit
public class HttpClient {
private Retrofit retrofit;
private static final int DEFAULT_TIMEOUT = 10;
//cookie持久化
private static ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(MyApplication.getMyApplication()));
//设置缓存目录
private static final File cacheDirectory = new File(MyApplication.getMyApplication().getCacheDir().getAbsolutePath(), "httpCache");
private static Cache cache = new Cache(cacheDirectory, 10 * 1024 * 1024);
//构造方法私有
private HttpClient() {
// 创建一个OkHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// 设置网络请求超时时间
builder.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
builder.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
builder.cookieJar(cookieJar);
// 失败后尝试重新请求
builder.retryOnConnectionFailure(false);
builder.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.i("http", message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY));
builder.cache(cache);
//----------------------------基本设置------------------------------------------------------
retrofit = new Retrofit.Builder()
.client(builder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(Constant.BASE_URL)
.build();
}
/**
* 调用单例对象
*/
private static HttpClient getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 创建单例对象
*/
private static class SingletonHolder {
static HttpClient INSTANCE = new HttpClient();
}
/**
* @return 指定service实例
*/
public static <T> T getService(Class<T> clazz) {
return HttpClient.getInstance().retrofit.create(clazz);
}
}
这里用到了一个Cookie持久化的框架,有的后台项目可能是从session里面去取你的登录标志,比如Token,而在OkHttp中每次请求的Cookie不一样,导致第二次请求的时候服务端拿不到你的登录状态,所以无法请求到你的数据。
八、Retrofit框架中使用的请求方法:
public interface RequestApi {
@FormUrlEncoded
@POST("/demo/test")
Observable<BaseBen<JsonObject>> login(@Field("name")String name,@Field("psw")String psd);
}
最后,我们看看怎么使用,我这里模拟一个登录,给服务端送入一个用户名和密码,首先,写View层,三个方法,一个获取名字,一个获取密码,然后就是更新View
public interface ITestView {
String getName();
String getPsw();
void update(BaseBen<JsonObject> object);
}
再写Model层:
public class TestModel extends BaseModule {
public TestModel(BaseActivity baseActivity) {
super(baseActivity);
}
public void login(final String name,final String psw,final OnRequestCallback onRequestCallback){
doActivitySubscribe(HttpClient.getService(RequestApi.class).login(name,psw),new HttpSubscriber<JsonObject>(baseActivity,true){
@Override
public void onSuccess(BaseBen<JsonObject> jsonObjectBaseBen) {
onRequestCallback.onSuccess(jsonObjectBaseBen);
Log.i("tag","loginonSuccess");
}
@Override
public void onError(String msg) {
onRequestCallback.onFailed(msg);
Log.i("tag","loginonError:"+msg);
}
});
}
public interface OnRequestCallback{
void onSuccess(BaseBen<JsonObject> jsonObjectBaseBen);
void onFailed(String msg);
}
}
然后就就是Presenter,执行登录动作逻辑:
public class TestPresenter extends BasePresenter<ITestView> {
private ITestView iTestView;
private TestModel testModel;
public TestPresenter(ITestView iTestView, BaseActivity activity) {
this.iTestView = iTestView;
testModel = new TestModel(activity);
}
public void login(){
testModel.login(iTestView.getName(), iTestView.getPsw(), new TestModel.OnRequestCallback() {
@Override
public void onSuccess(BaseBen<JsonObject> jsonObjectBaseBen) {
iTestView.update(jsonObjectBaseBen);
}
@Override
public void onFailed(String msg) {
}
});
}
}
看看Activity是这么写的:
public class TestActivity extends BaseActivity<ITestView,TestPresenter> implements ITestView {
@InjectView(R.id.name)
EditText name;
@InjectView(R.id.psw)
EditText psw;
@InjectView(R.id.btn)
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.inject(this);
}
@Override
protected TestPresenter createPresenter() {
return new TestPresenter(this,this);
}
@OnClick(R.id.btn)
public void onViewClicked(View view) {
switch (view.getId()){
case R.id.btn:
mPresenter.login();
break;
}
}
@Override
public String getName() {
return name.getText().toString();
}
@Override
public String getPsw() {
return psw.getText().toString();
}
@Override
public void update(BaseBen<JsonObject> object) {
}
}
好了,完了,这个MVP模式可能我个人认为是最简单的写法,MVP模式其实有很多种写法,见笑了。
最后,感谢这位的学习资源,谢谢 !
附demo源码:源码下载 !!!