前言
这篇文章主要记录如何搭建一个MVP架构,同时结合Retrofit,RxJava,Okhttp等主流框架实现网络请求,方便后面的复习以及快速开发。
项目分包
base:放一些基类,Activity,Adapter,Present等
common:存放常量,Application
model:模型层,存放实体类以及网络请求相关
presenter:P层,接收View层的命令,调用M层获取数据,通知View层更新
util:存放一些工具类
view:View层,包括Activity,Fragment,adapter,自定义View等
用到的框架依赖
//RxJava
compile 'io.reactivex:rxjava:1.1.6'
compile 'com.trello:rxlifecycle:0.6.1'
compile 'com.trello:rxlifecycle-components:0.6.1'
//Picasso
compile 'com.squareup.picasso:picasso:2.5.2'
//Retrofit库
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
//切换Android主线程
compile 'io.reactivex:rxandroid:1.0.1'
// Okhttp库
compile 'com.squareup.okhttp3:okhttp:3.1.2'
//dagger2(暂时没用到)
compile 'com.google.dagger:dagger:2.4'
apt 'com.google.dagger:dagger-compiler:2.4'
//java注解(暂时没用到)
compile 'org.glassfish:javax.annotation:10.0-b28'
//日志
compile 'com.orhanobut:logger:1.15'
//okhttp通用参数拦截器(暂时没用到)
compile 'com.github.jkyeo:okhttp-basicparamsinterceptor:v0.9'
View层实现
首先我们定义一个MvpView
/**
* 所有View的基类
*/
public interface MvpView {
}
我们自己定义的View需要实现这个接口
public interface IMovieView extends MvpView {
void showLoading();
void hideLoading();
void showError(Throwable e);
void setMovieInfo(MovieInfo movieInfo);
}
Presenter层
/**
* P层接口基类,需要关联View
*/
interface MvpPresenter<V extends MvpView> {
//关联View
void attachView(V view);
//解除关联
void detachView();
}
/**
* 实现Presenter层的具体逻辑
*/
public class BasePresenter<V extends MvpView> implements MvpPresenter<V>{
private V mMvpView;
//管理RxJava的订阅
protected CompositeSubscription mCompositeSubscription;
//关联View
@Override
public void attachView(V view) {
mMvpView = view;
mCompositeSubscription = new CompositeSubscription();
}
@Override
public void detachView() {
mMvpView = null;
mCompositeSubscription.clear();
mCompositeSubscription = null;
}
protected V getMvpView() {
return mMvpView;
}
}
Activity中的处理
public abstract class BaseActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base);
initPresenter();
}
@Override
protected void onDestroy() {
super.onDestroy();
detachView();
}
//需要子类初始化Presenter
public abstract void initPresenter();
//解除View以及RxJava,防止内存泄漏
public abstract void detachView();
}
/**
*Adapter的基类
*/
public abstract class BasicAdapter<T> extends BaseAdapter {
private Context mContext;
private List<T> mDatas;
private int mItemLayoutId;
public BasicAdapter(Context context,List<T> data,int itemLayoutId){
mContext = context;
mDatas = data;
mItemLayoutId = itemLayoutId;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public T getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
BasicViewHolder viewHolder = getViewHolder(position,convertView,parent);
convert(viewHolder,getItem(position));
return viewHolder.getConvertView();
}
//交给子类去处理具体的逻辑和显示
protected abstract void convert(BasicViewHolder viewHolder, T item);
private BasicViewHolder getViewHolder(int position,View convertView,ViewGroup parent){
return BasicViewHolder.get(mContext,convertView,parent,mItemLayoutId,position);
}
}
/**
* 公共的ViewHolder
*/
public class BasicViewHolder {
private View mConvertView;
private int mPosition;
private SparseArray<View> mViews;
private Context mContext;
private BasicViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
//填充布局
mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
mContext = context;
mPosition = position;
mViews = new SparseArray<>();
mConvertView.setTag(this);
}
static BasicViewHolder get(Context context, View converView, ViewGroup parent, int layoutId, int position) {
if (converView == null) {
return new BasicViewHolder(context, parent, layoutId, position);
} else {
return (BasicViewHolder) converView.getTag();
}
}
View getConvertView() {
return mConvertView;
}
private <T extends View> T getView(int viewId){
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId,view);
}
return (T) view;
}
public BasicViewHolder setText(int viewId,String text) {
TextView textView = getView(viewId);
textView.setText(text);
return this;
}
public BasicViewHolder setImageResource(int viewId,int resId) {
ImageView imageView = getView(viewId);
imageView.setImageResource(resId);
return this;
}
public BasicViewHolder setImageBitmap(int viewId, Bitmap bitmap){
ImageView imageView = getView(viewId);
imageView.setImageBitmap(bitmap);
return this;
}
public BasicViewHolder setImageUri(int viewId, String uri) {
ImageView imageView = getView(viewId);
Picasso.with(mContext).load(uri).into(imageView);
return this;
}
public int getPosition() {
return mPosition;
}
}
**
*全局Application
*/
public class MovieApplication extends Application {
private static MovieApplication mInstance;
@Override
public void onCreate() {
super.onCreate();
//初始化Log类
LogUtil.initLog(this);
mInstance = this;
}
//方便在其他地方获取Application的实例
public static MovieApplication getApplication() {
return mInstance;
}
}
/*
* Application中方法的执行顺序:
* 构造方法
* attachBaseContext
* onCreate
* */
上面就是项目的一些代码实例,完整的代码可以下载Demo查看
点此下载Demo源码
然后贴出最后的实现效果
接下来说一下项目中遇到的一些问题
打印Log
测试环境我们需要打印Log,方便测试,正式环境需要停止打log,如何判断当前的环境,下面说一种简单的方法。
/**
* Log工具类
*/
public class LogUtil {
private static boolean mIsDebug;
public static void initLog(Context context){
//Log初始化
Logger.init("MvpDemo")
.methodCount(0);
mIsDebug = context.getApplicationInfo() != null &&
(context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE;
}
public static void v(String msg,Object... obj){
if (mIsDebug) {
Logger.v(msg,obj);
}
}
public static void w(String msg,Object... obj){
if (mIsDebug) {
Logger.w(msg,obj);
}
}
public static void e(String msg,Object... obj){
if (mIsDebug) {
Logger.e(msg,obj);
}
}
public static void d(String msg,Object... obj){
if (mIsDebug) {
Logger.d(msg,obj);
}
}
}
我们判断的原理是根据ApplicationInfo中的一个标记位,具体可参考
#Android源码#ApplicationInfo中flags的设计