Android--带你一点点封装项目 MVP+BaseActivity+Retrofit+Dagger+RxJava(一)

1,其实早就想把这些东西给封装封装的,一直没有时间,今天刚好项目进入到测试阶段了,Bug同事在哪儿测试的飞起,但发现提bug的尽然是我(得意脸),然后上午把ios的包测试了一下,顺便把服务器给测挂了(别问我是怎么做到的),现在服务器的同事还在拿着刀满街找我呐。好了不扯了,就想标题写了,一直想把这一块揉在一起写写,那就趁这个机会吧。

先看看今天我们要实现的效果:

Android--带你一点点封装项目 MVP+BaseActivity+Retrofit+Dagger+RxJava(一)

2,有些童鞋就很气愤了,麻蛋,裤子都脱了,你给我看这个!!!!

   其实我也想多写点的啊,还想把App下载写上呢,没事,我们慢慢一点点的来,,先来看一下我们接口的数据吧

{
"code": 200,
"message": "",
"data": {
"code": "1.1.0",
"size": "8.6M",
"des": "1.【新增】自动更新\\r\\n2.【修改】部分ProgressBar替换为SVG\\r\\n3.【修改】Gank板块部分改动\\r\\n"
}
}

  可以看到,这是一个标准的接口数据,外部包含 code、message、data三大门神,这样我们封装Response就很好解决了。

  看一下我们的BaseResponse类,很简单,没有什么讲的

  BaseResponse.java

package com.qianmo.myview2.response;

/**
* Created by wangjitao on 2016/11/8 0008.
* 数据返回类类
*/
public class BaseResponse<T> {
private int code;
private String message;
private T data; public T getData() {
return data;
} public void setData(T data) {
this.data = data;
} public String getMessage() {
return message;
} public void setMessage(String message) {
this.message = message;
} public int getCode() {
return code;
} public void setCode(int code) {
this.code = code;
}
}

  再看一下我们这次的功能,就是一个简单的网络请求,判断当前app版本和服务器上的版本,从而判断是否是最新的,看到这里的同学可能就很疑惑了,麻蛋,不是说好了是封装封装嘛,为什么到现在还在说功能,嗯,阿呆哥哥只是想从本质上对比一下如果我们使用传统的MVC去写的话该是怎么写的啊,相信现在很多同学已经在脑海中已经有了代码了,不过我猜你们的Activity中的代码会很多(奸笑脸),好吧,我前面的博客也介绍了MVP模式,现在我们是要把MVP运用在项目里面,所以,我们就要开始封装基类了!!

3 ,封装

①BaseView

我们知道在MVP模式中我们的V是和我们用户界面的UI有关的,不管是控件的隐藏和显示,还是控件大小的改变都是我们的V来处理的,所以这里我们只是写了个简单的夜间模式的UI改变的方法、还有展示错误信息的方法,很简单

package com.qianmo.myview2.base;

import android.view.View;

/**
* Created by wangjitao on 2016/11/8 0008.
* 一般的Activity中要用到View操作无非是显示加载框、影藏加载框、显示出错信息、显示当数据为空的时候的view之类的
*/
public interface BaseView { void showError(String msg); void useNightMode(boolean isNight); }

  

②BasePresenter

P在MVP架构中是主要用于逻辑处理的,所以一是我们最经常要写的,看一下代码,就是AttachView和DetachView(这也没什么好解释的)

package com.qianmo.myview2.base;

/**
* Created by wangjitao on 2016/11/8 0008.
* MVP框架的简单封装 P处理层
*/
public interface BasePresenter<T extends BaseView> { void attachView(T view); void detachView();
}

③App

这个类一般用于初始化一些数据,如屏幕的信息之类的和Activity的简单的管理啊,还有一些第三方SDK的初始化

package com.qianmo.myview2;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager; import java.util.HashSet;
import java.util.Set; /**
* Created by wangjitao on 2016/11/8 0008.
*/
public class App extends Application { private static App instance;
private Set<Activity> allActivities; public static int SCREEN_WIDTH = -1;
public static int SCREEN_HEIGHT = -1;
public static float DIMEN_RATE = -1.0F;
public static int DIMEN_DPI = -1; public static synchronized App getInstance() {
return instance;
} @Override
public void onCreate() {
super.onCreate(); instance = this; getScreenSize();
} /**
* 初始化屏幕宽高
*/
public void getScreenSize() {
WindowManager windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
Display display = windowManager.getDefaultDisplay();
display.getMetrics(dm);
DIMEN_RATE = dm.density / 1.0F;
DIMEN_DPI = dm.densityDpi;
SCREEN_WIDTH = dm.widthPixels;
SCREEN_HEIGHT = dm.heightPixels;
if (SCREEN_WIDTH > SCREEN_HEIGHT) {
int t = SCREEN_HEIGHT;
SCREEN_HEIGHT = SCREEN_WIDTH;
SCREEN_WIDTH = t;
}
} /**
* 添加activity
*/
public void addActivity(Activity act) {
if (allActivities == null) {
allActivities = new HashSet<>();
}
allActivities.add(act);
} /**
* 移除activity
*/
public void removeActivity(Activity act) {
if (allActivities != null) {
allActivities.remove(act);
}
} /**
* 退出app
*/
public void exitApp() {
if (allActivities != null) {
synchronized (allActivities) {
for (Activity act : allActivities) {
act.finish();
}
}
}
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
} /**
* 这还有一系列的第三方SDK的初始化
*/
}

④BaseActivity

这个貌似是最重要的,这里我们考虑到5.0一下的机器切换Activity比较丑,所以写了下切换的动画,,然后就是一系列的初始化,直接贴出来吧

package com.qianmo.myview2.base;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View; import com.qianmo.myview2.App;
import com.qianmo.myview2.R; import butterknife.Unbinder;
import butterknife.ButterKnife; /**
* Created by wangjitao on 2016/11/8 0008.
* 基类Activity的封装
* 一般使用mvp模式的话会在BaseActivity中进行P和V的初始化绑定
*/
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements BaseView {
protected T mPresenter;
protected Activity mContext;
private Unbinder mUnbinder; public enum TransitionMode {
LEFT, RIGHT, TOP, BOTTOM, SCALE, FADE
} protected void onCreate(@Nullable Bundle savedInstanceState) {
if (toggleOverridePendingTransition()) {
switch (getOverridePendingTransitionMode()) {
case LEFT:
overridePendingTransition(R.anim.left_in, R.anim.left_out);
break;
case RIGHT:
overridePendingTransition(R.anim.right_in, R.anim.right_out);
break;
case TOP:
overridePendingTransition(R.anim.top_in, R.anim.top_out);
break;
case BOTTOM:
overridePendingTransition(R.anim.bottom_in, R.anim.bottom_out);
break;
case SCALE:
overridePendingTransition(R.anim.scale_in, R.anim.scale_out);
break;
case FADE:
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
break;
}
} super.onCreate(savedInstanceState);
setContentView(getLayout()); mUnbinder = ButterKnife.bind(this);
mContext = this; createPresenter(); if (mPresenter != null)
mPresenter.attachView(this);
App.getInstance().addActivity(this);
initEventAndData();
} @Override
protected void onStart() {
super.onStart(); } @Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null)
mPresenter.detachView();
mUnbinder.unbind();
App.getInstance().removeActivity(this);
} protected void setToolBar(Toolbar toolbar, String title) {
toolbar.setTitle(title);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onBackPressed();
}
});
} protected abstract int getLayout(); protected abstract void initEventAndData(); protected abstract boolean toggleOverridePendingTransition(); protected abstract TransitionMode getOverridePendingTransitionMode(); protected abstract void createPresenter();
}

⑤ BaseRecyclerViewAdapter

package com.qianmo.myview2.base;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; import com.balysv.materialripple.MaterialRippleLayout;
import com.qianmo.myview2.R; import java.util.List; /**
* Created by wangjitao on 2016/11/7 0007.
* 对简单的recycleview进行简单的封装
*/
public abstract class BaseRecyclerViewAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
private Context context;
private LayoutInflater inflater;
private List<T> datas;
private int layoutId;
protected OnItemClickListner onItemClickListner;//单击事件
protected OnItemLongClickListner onItemLongClickListner;//长按单击事件
private boolean clickFlag = true;//单击事件和长单击事件的屏蔽标识 public BaseRecyclerViewAdapter(Context context, List<T> datas, int layoutId) {
this.context = context;
this.datas = datas;
this.layoutId = layoutId;
this.inflater = LayoutInflater.from(context);
} @Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
BaseViewHolder holder = new BaseViewHolder(inflater.inflate(layoutId, parent, false));
MaterialRippleLayout.on(holder.getView(R.id.ll_all))
.rippleOverlay(true)
.rippleAlpha(0.2f)
.rippleColor(context.getResources().getColor(R.color.colorAccent))
.rippleHover(true)
.create();
return holder;
} @Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
bindData(holder, datas.get(position), position);
} @Override
public int getItemCount() {
return datas == null ? 0 : datas.size();
} protected abstract void bindData(BaseViewHolder holder, T data, int position); public void setOnItemClickListner(OnItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
} public void setOnItemLongClickListner(OnItemLongClickListner onItemLongClickListner) {
this.onItemLongClickListner = onItemLongClickListner;
} public interface OnItemClickListner {
void onItemClickListner(View v, int position);
} public interface OnItemLongClickListner {
void onItemLongClickListner(View v, int position);
}
}

⑥ BaseViewHolder

package com.qianmo.myview2.base;

import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View; /**
* Created by wangjitao on 2016/11/7 0007.
* 万能的Viewholder
*/
public class BaseViewHolder extends RecyclerView.ViewHolder { private SparseArray<View> views; public BaseViewHolder(View view) {
super(view);
this.views = new SparseArray<>();
} public <T extends View> T getView(int viewId) {
View view = views.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
} public View getRootView() {
return itemView;
}
}

由于上一篇介绍了怎么封装RecycleView的Adapter,在这里就不在废话了  

4,好了东西都封装好了,看一下我们在实际功能中怎么写吧,由于我们上面的功能,我们要实现一个简单的版本判断,由于要使用网络,这里我打算使用Retrofit+RxJava,但是还没有封装好,就直接拿没封装的直接用了,这里创建mvp模式的话要创建好多个接口啊 ,所以推荐是用MVPHelper这个插件,挺好用的  ,好了,先看一下我们的MainContract

package com.qianmo.myview2.contract;

import com.qianmo.myview2.base.BasePresenter;
import com.qianmo.myview2.base.BaseView;
import com.qianmo.myview2.bean.VersionBean; /**
* Created by wangjitao on 2016/11/8 0008.
* 首页逻辑处理
*/
public class MainContract { public interface View extends BaseView {
//View效果就是展示下载进度框
void showUpdateDialog(VersionBean bean); void showProgressDialog(); void DissProgressDialog(); void ShowToast(String message);
} public interface Presenter extends BasePresenter<View> {
//一般在首页我们会进行一个版本的更新(功能)
void checkVersion(String currentVersion);
} }

就只是接口的常见,并把这次功能要用到的方法全部抽象出来,

看一下我们Presenter的实现类

MainPresenterImpl.java

package com.qianmo.myview2.presenter;

import android.widget.Toast;

import com.qianmo.myview2.CheckVersionActivity;
import com.qianmo.myview2.MainActivity;
import com.qianmo.myview2.api.AppVersionService;
import com.qianmo.myview2.bean.VersionBean;
import com.qianmo.myview2.contract.MainContract;
import com.qianmo.myview2.response.BaseResponse;
import com.qianmo.myview2.utils.Constant; import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers; /**
* Created by MVPHelper on 2016/11/08
*/ public class MainPresenterImpl implements MainContract.Presenter { private MainContract.View mView; @Override
public void checkVersion(final String currentVersion) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
AppVersionService movieService = retrofit.create(AppVersionService.class); movieService.getVersion()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<BaseResponse<VersionBean>>() {
@Override
public void onStart() {
mView.showProgressDialog();
} @Override
public void onCompleted() {
mView.DissProgressDialog();
} @Override
public void onError(Throwable e) {
mView.DissProgressDialog();
mView.ShowToast("请求出错");
} @Override
public void onNext(BaseResponse<VersionBean> versionBeanBaseResponse) {
if (Integer.valueOf(currentVersion.replace(".", "")) < Integer.valueOf(versionBeanBaseResponse.getData().getCode().replace(".", ""))) {
// mView.showUpdateDialog(versionBean);
//这里表示发现新版本
mView.ShowToast("发现最新版本");
} else {
//表示这就是最新版本
mView.ShowToast("已经是最新版本");
}
}
});
} @Override
public void attachView(MainContract.View view) {
mView = view;
} @Override
public void detachView() {
mView = null;
} }

没有什么好讲解的,其实还可以把presenter层封装一下的,可以在封装成这样的

public class BasePresenter<T extends MvpView> implements Presenter<T> {

    private T mMvpView;

    @Override
public void attachView(T mvpView) {
mMvpView = mvpView;
} @Override
public void detachView() {
mMvpView = null;
} public boolean isViewAttached() {
return mMvpView != null;
} public T getMvpView() {
return mMvpView;
} public void checkViewAttached() {
if (!isViewAttached()) throw new MvpViewNotAttachedException();
} public static class MvpViewNotAttachedException extends RuntimeException {
public MvpViewNotAttachedException() {
super("Please call Presenter.attachView(MvpView) before" +
" requesting data to the Presenter");
}
}
}

  ok,最后看一下我们的Activity

package com.qianmo.myview2;

import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast; import com.qianmo.myview2.base.BaseActivity;
import com.qianmo.myview2.bean.VersionBean;
import com.qianmo.myview2.contract.MainContract;
import com.qianmo.myview2.presenter.MainPresenterImpl; import butterknife.BindView; public class CheckVersionActivity extends BaseActivity<MainPresenterImpl> implements MainContract.View, View.OnClickListener {
@BindView(R.id.btn_getVersion)
Button btnGetVersion;
@BindView(R.id.progressBar)
ProgressBar progressBar; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
} @Override
protected int getLayout() {
return R.layout.activity_main;
} @Override
protected void initEventAndData() {
btnGetVersion.setOnClickListener(this); } @Override
protected boolean toggleOverridePendingTransition() {
return false;
} @Override
protected TransitionMode getOverridePendingTransitionMode() {
return null;
} @Override
protected void createPresenter() {
mPresenter = new MainPresenterImpl();
} @Override
public void showError(String msg) { } @Override
public void useNightMode(boolean isNight) { } @Override
public void showUpdateDialog(VersionBean bean) { } @Override
public void showProgressDialog() {
progressBar.setVisibility(View.VISIBLE);
} @Override
public void DissProgressDialog() {
progressBar.setVisibility(View.GONE);
} @Override
public void ShowToast(String message) {
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
} @Override
public void onClick(View view) {
try {
PackageManager pm = getPackageManager();
PackageInfo pi = pm.getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
String versionName = pi.versionName;
mPresenter.checkVersion(versionName);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}

  再贴一下布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
> <include
android:id="@+id/toolbar"
layout="@layout/view_toolbar"/> <android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar"
android:visibility="gone"
> </android.support.v7.widget.RecyclerView> <Button
android:id="@+id/btn_getVersion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="400dp"
android:text="检查版本"
/> <ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
/>
</RelativeLayout>

  build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0" defaultConfig {
applicationId "com.qianmo.myview2"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
} dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:design:25.+'
compile 'com.android.support:recyclerview-v7:25.+'
compile 'com.android.support:cardview-v7:25.+'
compile 'com.balysv:material-ripple:1.0.2'
compile 'com.jakewharton:butterknife:8.2.1'
apt 'com.jakewharton:butterknife-compiler:8.2.1'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
compile 'com.google.code.gson:gson:2.6.2'
}

  好了,这样我们就把MVP简单的封装了,有没有很简单啊,下一篇接着封装Retrofit+Rxjava+Dagger。

see you next time·······

上一篇:JsUnit && JUnit之讲解


下一篇:css实现div两列布局——左侧宽度固定,右侧宽度自适应(两种方法)