最近公司做的项目用到了MVP
,期间查了很多资料,也看了github
上别人搭的MVP
框架,基本用到dagger2
,也有的用到了别的技术,各种门类的都用,通过对比,选出了比较简易的搭建框架,用到的主要技术就是Retrofit2+RxJava2
,期间也学了不少RxJava
和Retrofit
的高级用法,比如服务器返回的Response
,token
时效性,失败重试机制,compose
的用法,感觉Rxjava
和Retrofit
还是很强大的。由于有的涉及到业务代码,就不方便贴出。在此记录下我们自己的框架搭建教程,方便以后回顾使用。
1.)项目net目录结构
我将项目公用的代码抽出为一个net
的module
,可以方便以后重复使用。项目的目录包名
app 存放的是一些全局变量,比如我们使用
retrofit
的base_url
mvp.base mvp的抽象提出到base中,方便不同
activity
和fragment
使用recyclerview 列表抽象封装,仿照
BRVAH
的封装retrofit_encapsulation
retrofit
的封装rx_encapsulation
rxjava
的封装type 泛型的实例化
接下来一一介绍这几个包名下的封装
2.) app
这个是仿照慕课网
,这门课的部分封装,其实与mvp
的封装没多大关系,但是这种封装可以统一管理多个全局变量的问题,当公司有多条业务线时,我们可以统一存储。
主要有三个类,AppConstant
, ConfigKeys
,Configurator
ConfigKeys
public enum ConfigKeys {
API_HOST,
APPLICATION_CONTEXT,
CONFIG_READY,
HANDLER,
}
存放的是全局变量的引用,枚举方便引用。
ConfigKeys
public class Configurator {
private static final HashMap<Object, Object> ANT_CONFIGS = new HashMap<>();
private static final Handler HANDLER = new Handler();
public Configurator() {
ANT_CONFIGS.put(ConfigKeys.CONFIG_READY, false);
}
public static Configurator getInstance() {
return Holder.INSTANCE;
}
private static class Holder {
private static final Configurator INSTANCE = new Configurator();
}
public final Configurator withApiHost(String apiHost) {
ANT_CONFIGS.put(ConfigKeys.API_HOST, apiHost);
return this;
}
final Configurator withApplicationContext(Context context) {
ANT_CONFIGS.put(ConfigKeys.APPLICATION_CONTEXT, context.getApplicationContext());
return this;
}
private void checkConfiguration() {
final boolean isReady = (boolean) ANT_CONFIGS.get(ConfigKeys.CONFIG_READY);
if (!isReady) {
throw new RuntimeException("Configuration is not ready,call configure");
}
}
/**
* 配置完成
*/
public final void configure() {
ANT_CONFIGS.put(ConfigKeys.CONFIG_READY, true);
}
@SuppressWarnings("unused")
final <T> T getConfiguration(Object key) {
checkConfiguration();
final Object value = ANT_CONFIGS.get(key);
if (value == null) {
throw new NullPointerException(key.toString() + " IS NULL");
}
return (T) ANT_CONFIGS.get(key);
}
}
用Hashmap
来作为容器,存放全局变量,由于要存放的类型不是固定的类型,使用了Object
来作为值得存储类型。
AppConstant
public class AppConstant {
public static Configurator init(Context context) {
Configurator.getInstance()
.withApplicationContext(context);
return Configurator.getInstance();
}
public static Configurator getConfigurator() {
return Configurator.getInstance();
}
@SuppressWarnings("unused")
public static <T> T getConfiguration(Object key) {
return getConfigurator().getConfiguration(key);
}
public static Context getApplicationContext() {
return getConfigurator().getConfiguration(ConfigKeys.APPLICATION_CONTEXT);
}
public static String getApiHost() {
return getConfigurator().getConfiguration(ConfigKeys.API_HOST);
}
我们每次要去获取值时候,直接通过AppConstant
来获取,比如获取存放的BaseUrl
,可以这样调用 AppConstant.getApiHost()
,每次获取只需要从AppConstant
获取
既然讲了获取全局变量的值当然要讲怎么存值。我们在使用这个包下面的东西时只需要这样使用
public class App extends Application{
String HOST = "http://news-at.zhihu.com/api/4/"; //自己公司的域名地址
@Override
public void onCreate() {
super.onCreate();
AppConstant.init(this)
.withApiHost(HOST)
.configure();
}
}
在Application中做统一存储
3.) mvp.base
这个包下面是所有mvp
抽象的封装,所以东西还是挺多的。
主要有 BaseView
, BasePresenter
, BaseMVPActivity
, BaseModel
, BaseActivity
几个类,
BaseView
public interface BaseView {
Context getContext();
void dealMsgError(String msg);
}
所有view
接口的公用回调,这里如果不做处理,那么其他地方都要写,所以这里写,其他接口只需要继承这个接口就行了。
BaseModel
这个接口是一个空实现,只是为了抽象解耦的作用。
BasePresenter
public abstract class BasePresenter<V extends BaseView, M extends BaseModel> {
protected V mView;
protected M mModel;
protected RxManager rxManager = new RxManager();
public void attachVM(V view, M model) {
mView = view;
mModel = model;
}
public void detachVM(){
rxManager.clear();
mView = null;
mModel = null;
}
}
这个抽象类里面用到了两个泛型,是为了规范其实现类,同时这个类的实现是给Activity和Fragment实现和attach。同时这个类里面还用到了RxManager
,主要就是对CompositeDisposable
的包装,管理RxJava的生命周期,当页面结束时,自动取消RxJava的事件传递。
BaseMVPActivity
public abstract class BaseMVPActivity<P extends BasePresenter, M extends BaseModel> extends BaseActivity implements BaseView{
protected P mPresenter;
protected M mModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mPresenter = TUtil.getT(this, 0);
mModel = TUtil.getT(this, 1);
mPresenter.attachVM(this, mModel);
super.onCreate(savedInstanceState);
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mPresenter != null){
mPresenter.detachVM();
}
}
@Override
public Context getContext() {
return mContext;
}
@Override
public void dealMsgError(String msg) {
}
}
这个类是对Activity中使用mvp的封装,我们如果使用只需要继承这个抽象的acitivity
就行了,同时指定需要的泛型具体实现,在BaseMVPActivity
中自动实例化泛型的参数。同时在抽象类中实现BaseView,可以避免在具体的activity中的空实现问题。
BaseActivity
就是不使用mvp的抽象,如果我们页面没有使用网络请求,可以不必实现BaseMVPActivity
。
4.) recyclerview
这个包路径是对recyclerview的封装,这个只是纯粹为了简化recyclerview的使用,与mvp无关,就不多说了,代码后面贴出,直接看代码。
5) retrofit_encapsulation
这个是对网络请求的抽象,我第一次封装时,没有考虑到多个BaseUrl的问题,Retrofit的创建都写死了,后来看了这门课Retrofit 从入门封装到源码解析,参照他的教程重新改了一遍。这个包下主要有两个类。分别是RetrofitManager
,RetrofitNet
RetrofitManager
public class RetrofitManager {
private static Context mContext;
private OkHttpClient mOkHttpClient;
private static class InnerHolder {
private static RetrofitManager INSTACE = new RetrofitManager();
}
/**
* 需要context时候调用,可以在Application中调用
* @param context
*/
public static void init(Context context) {
//防止内存泄露
mContext = context.getApplicationContext();
}
public static RetrofitManager getInstance() {
return InnerHolder.INSTACE;
}
public Retrofit newRetrofit(String url) {
// 拿到Retrofit实例
return new Retrofit.Builder()
.baseUrl(url)
//引入Gson解析库 ,就可以直接以实体的形式拿到返回值
.addConverterFactory(GsonConverterFactory.create())
//加入我们自定义的Gson解析库,就可以更友好的处理错误
//.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
//将我们客制化的OkHttp实例传入
.client(mOkHttpClient)
.build();
}
private RetrofitManager() {
//在构造方法里 最终是为了得到一个单例的OkhttpClient实例
OkHttpClient.Builder builder = new OkHttpClient.Builder();
//加入自己业务需要的拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.i("OkHttp", message);
}
});
//缓存设置
//构建缓存位置
mOkHttpClient = builder.addInterceptor(loggingInterceptor).build();
}
}
基本抄的那个视频的代码,主要就是实例化Retrofit
,同时我们可以在实例化Retrofit
的同时,加入Okhttp
拦截器,比如很常用的日志拦截器HttpLoggingInterceptor,由于Retrofit
默认不返回Json
,加入这个拦截器,可以极大提高我们调试技巧。同时也可以加入其他拦截器,可以根据业务自己判定。同时可以根据自己业务加入自定义的Gson解析。比如这样的结构
{
errorCode:1,
msg:"成功",
data:{
.....
}
}
我们只需要data里面的内容,这时候我们就可以自定义Gson解析器,其实也就是改造GsonConverterFactory
的代码而已。
RetrofitNet
public class RetrofitNet {
private static Retrofit mRetrofit;
public static <T> T getService(Class<T> clazz) {
return retrofit().create(clazz);
}
private static Retrofit retrofit() {
if (mRetrofit == null) {
mRetrofit = RetrofitManager.getInstance().newRetrofit(AppConstant.getApiHost());
}
return mRetrofit;
}
}
通过这个去获取Retrofit
的实例,同时去构建Service
。
一般我们去获取Retrofit的原则是这样的
一个BaseUrl一个Retrofit实例
多个Retrofit共用一个OkHttp实例
6.) rx_encapsulation
对于RxJava的生命周期处理
RxManager
public class RxManager {
private CompositeDisposable disposable = new CompositeDisposable();
public void add(Disposable d){
disposable.add(d);
}
public void clear(){
disposable.dispose();
disposable = null;
}
}
当生命周期结束,自动取消RxJava事件传递
7.) type
这个包下面的类是为了实例化Presenter和Model
public class TUtil {
public static <T> T getT(Object o, int i) {
try {
return ((Class<T>) ((ParameterizedType) (o.getClass().getGenericSuperclass())).getActualTypeArguments()[i]).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassCastException e) {
e.printStackTrace();
}
return null;
}
}
利用反射去实例化Presenter和Model,来达到不用在Activity中手动实例化的目的。
8.) 使用
a. Contract规范View和Presenter,减少接口的创建
public interface HttpContract {
interface View extends BaseView{
void getWelecomeDataSuccess(WelcomeBean welcomeBean);
void getDailyList(DailyListBean respBean);
}
abstract class Presenter extends BasePresenter<View, HttpModel>{
public abstract void getWelecomData(String res);
public abstract void getDailyList();
}
}
b. Model
public class HttpModel implements BaseModel{
public Observable<DailyListBean> getDailyList(){
return RetrofitNet.getService(HttpService.class).getDailyList();
}
}
统一调用HttpService的方法。
c.Presenter
public class HttpPresenter extends HttpContract.Presenter{
@Override
public void getDailyList() {
rxManager.add(mModel.getDailyList().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<DailyListBean>() {
@Override
public void accept(DailyListBean dailyListBean) throws Exception {
mView.getDailyList(dailyListBean);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
mView.dealMsgError(throwable.getMessage());
}
}));
}
}
d. Activity中指定泛型参数,统一实例化
public class MVPActivity extends BaseMVPActivity<HttpPresenter, HttpModel> implements HttpContract.View
//使用
mPresenter.getDailyList();
基本使用也就这多了。
9. 结尾
其实还有好多没写出来,一是自己能力不够,二是对RxJava掌握还是不够好,以后会写点Retrofit的源码,加深下自己网络框架方面的学习。