内容简介:最新Retrofit + RxJava + MVP
此处搭建的框架是目前最新版本,项目今天刚搭建好,( ^__^ ) 嘻嘻……。
先撸上包:
compile ‘com.jakewharton:butterknife:8.6.0’
compile ‘com.jakewharton:butterknife-compiler:8.6.0’
compile ‘io.reactivex.rxjava2:rxjava:2.1.0’
compile ‘io.reactivex.rxjava2:rxandroid:2.0.1’
compile ‘com.squareup.retrofit2:converter-gson:2.3.0’
compile ‘com.squareup.retrofit2:retrofit:2.3.0’
compile ‘com.google.code.gson:gson:2.8.0’
compile ‘com.squareup.retrofit2:adapter-rxjava2:2.3.0’
本人还是挺嫌弃Gson的,一定会有人说,嫌弃还用,我勒个去,我想支持下国产用fastjson,丫的,retrofit2.X没给出支持包,不想使用其它第三方包,只能等有空的时候自己摸索一个出来了,更高效的LoganSquare也不支持,无语了,那就考虑下jackson,发现jackson的包1M多,算了,jackson洗洗睡吧,无奈之下选择了gson,肯定会有人想,为啥主流的json解析工具gson这么受嫌弃,gson在解析的效率上对比其它几个第三方,还是明显偏低的。
废话不多说,直接撸代码,因为写了很多详细的注释,未来还会写几篇帖子专门分析,此处就不多做介绍。
先看下目录结构图
除了删除test文件和删了baseUrl里的地址外,其它全部上传到我的github了,最后会附上地址。
BaseActivity:
/** * Created by Zero on 2017/5/25. */ public abstract class BaseActivity<Pre extends BasePresenter> extends AppCompatActivity implements OnClickListener { private static final String DIALOG_LOADING = "DialogLoading"; private boolean mVisible; private LoadingDialogFragment waitDialog = null; protected Pre presenter; protected final Handler mHandler = new MyHandler(this); private BroadcastReceiver receiver; private IntentFilter filter; private class MyHandler extends Handler { private final WeakReference<BaseActivity> mActivity; /** * 因为内部类会隐式强引用当前类,采用弱引用,避免长生命周期导致内存泄漏 * * @param activity */ private MyHandler(BaseActivity activity) { mActivity = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { if (mActivity.get() != null) { requestOver(msg); } } } @Override protected final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().setNavigationBarColor(Color.BLACK); } if (initLayout() != 0) { /** * 设置布局,其实很多view注解框架都可以对layout抓取到,但还是习惯这样写,^_^ */ setContentView(initLayout()); ButterKnife.bind(this); } try { if (getPsClass() != null) { if (getPsClass().newInstance() instanceof BasePresenter) { /** * presenter实例化,new和newInstance()不清晰,自己百度 */ presenter = (Pre) getPsClass().newInstance(); /** * 把一些必要的数据和presenter传过去 */ presenter.initBaseData(this, mHandler, getIView(), getIntent()); } else { throw new RuntimeException("必须继承BasePresenter"); } } } catch (InstantiationException e) { /** * 不能newInstance()导致的错误 */ e.printStackTrace(); } catch (IllegalAccessException e) { /** * 权限不足,主要是构造方法使用了private */ e.printStackTrace(); } initData(); initViewAndListen(); } /** * 传入需要过滤的action不定参数 * * @param filterActions */ protected void registerReceiver(@NonNull String... filterActions) { filter = filter == null ? new IntentFilter() : filter; for (String action : filterActions) { filter.addAction(action); } registerReceiver(filter); } /** * 传入filter,注册广播 * * @param filter */ protected void registerReceiver(@NonNull IntentFilter filter) { // TODO Auto-generated method stub receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { executeReceiver(context, intent); } }; LocalBroadcastManager.getInstance(this).registerReceiver( receiver, filter); } /** * 接收到广播 * * @param context * @param intent */ protected void executeReceiver(Context context, Intent intent) { } /** * setontentview() * * @return */ abstract protected int initLayout(); /** * 使用比如ButterKnife可以不使用 */ abstract protected void initViewAndListen(); /** * 初始化简单数据,比如传过来的title */ abstract protected void initData(); /** * 不用多个类实现OnClickListener * * @param v */ abstract protected void onclick(View v); /** * @return presenter, 此处不能使用返回Pre类型,newInstance()方法是class下的,Pre不能使用newInstance()实例化 */ abstract protected Class getPsClass(); /** * 接口回调 * * @return */ abstract protected BaseInterface getIView(); /** * 把发送到view层的message传递到presenter层处理,因为采用了rxjava和retrofit, * 很多view不在使用handler发送数据,所以没写成抽象方法 * * @param msg */ protected void requestOver(Message msg) { if (presenter != null) { presenter.handMsg(msg); } } protected void to(Intent intent) { startActivity(intent); } protected void to(Class<?> T) { Intent intent = new Intent(this, T); to(intent); } protected void to(Class<?> T, Bundle bundle) { Intent intent = new Intent(this, T); intent.putExtras(bundle); to(intent); } @Override public void onBackPressed() { if (waitDialog != null) { hideProcessDialog(); } else { super.onBackPressed(); } } public LoadingDialogFragment showProcessDialog() { return showProcessDialog(R.string.loading); } public LoadingDialogFragment showProcessDialog(int resId) { return showProcessDialog(getString(resId)); } private LoadingDialogFragment showProcessDialog(String msg) { if (mVisible) { FragmentManager fm = getSupportFragmentManager(); if (waitDialog == null) { waitDialog = LoadingDialogFragment.newInstance(msg); } if (!waitDialog.isAdded()) { waitDialog.show(fm, DIALOG_LOADING); } return waitDialog; } return null; } public void hideProcessDialog() { if (mVisible && waitDialog != null) { try { waitDialog.dismiss(); waitDialog = null; } catch (Exception ex) { ex.printStackTrace(); } } } @Override public void setVisible(boolean visible) { mVisible = visible; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { View v = getCurrentFocus(); if (isShouldHideKeyboard(v, ev)) { hideKeyboard(v.getWindowToken()); v.clearFocus(); } } return super.dispatchTouchEvent(ev); } /** * 根据EditText所在坐标和用户点击的坐标相对比,来判断是否隐藏键盘,因为当用户点击EditText时则不能隐藏 * * @param v * @param event * @return */ private boolean isShouldHideKeyboard(View v, MotionEvent event) { if (v != null && (v instanceof EditText)) { int[] l = {0, 0}; v.getLocationInWindow(l); int left = l[0], top = l[1], bottom = top + v.getHeight(), right = left + v.getWidth(); if (event.getX() > left && event.getX() < right && event.getY() > top && event.getY() < bottom) { // 点击EditText的事件,忽略它。 return false; } else { return true; } } return false; } /** * 获取InputMethodManager,隐藏软键盘 * * @param token */ private void hideKeyboard(IBinder token) { if (token != null) { InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); im.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS); } } @Override public void onClick(View v) { // TODO Auto-generated method stub onclick(v); } @Override protected void onDestroy() { super.onDestroy(); /** * 移除mHandler,避免因为移除mHandler超activity生命周期工作造成内存泄漏 */ mHandler.removeCallbacksAndMessages(null); if (receiver != null) { LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); } } }
这是我从上一个私活项目演化而来,当然,上个项目也是我从无到有,目前此类还少了一个对toolbar的封装。
BaseObserver类
/** * Observer的封装 * Created by Zero on 2017/5/28. */ public class BaseObserver<T> implements Observer<ResponseBody> { private IResponse iResponse; private Gson mGson; private final Type finalNeedType; private static final int UNLOGIN_EXCEPTION = 33333; private static final int REQUEST_EXCEPTION = 1003; public BaseObserver(IResponse<T> iResponse) { this.iResponse = iResponse; mGson = new Gson(); final Type[] types = iResponse.getClass().getGenericInterfaces(); if (MethodHandler(types) == null || MethodHandler(types).size() == 0) { } finalNeedType = MethodHandler(types).get(0); } /** * 通过反射,拿到所需要的类型 * @param types * @return */ private List<Type> MethodHandler(Type[] types) { List<Type> needTypes = new ArrayList<>(); for (Type paramType : types) { if (paramType instanceof ParameterizedType) { Type[] parenTypes = ((ParameterizedType) paramType).getActualTypeArguments(); for (Type childType : parenTypes) { needTypes.add(childType); if (childType instanceof ParameterizedType) { Type[] childTypes = ((ParameterizedType) childType).getActualTypeArguments(); for (Type type : childTypes) { needTypes.add(type); } } } } } return needTypes; } @Override public void onSubscribe(Disposable d) { } @Override public void onNext(ResponseBody responseBody) { try { /** * responseBody.string()当前打断点,获取不到值,具体原因还未去查找,此处先用result接收 */ String result = responseBody.string(); BaseResponse httpResponse = mGson.fromJson(result,finalNeedType); if (httpResponse.isSuccess()) { iResponse.onSuccess(httpResponse); } else { if (httpResponse.getCode() == UNLOGIN_EXCEPTION) { iResponse.onError(new UnLoginException(httpResponse.getCode(), httpResponse.getMessage())); } else if (httpResponse.getCode() == REQUEST_EXCEPTION) { iResponse.onError(new RequestExpiredException(httpResponse.getCode(), httpResponse.getMessage())); } else { iResponse.onError(new APIException(httpResponse.getCode(), httpResponse.getMessage())); } } } catch (IOException e) { iResponse.onError(e); } } @Override public void onError(Throwable e) { iResponse.onError(e); } @Override public void onComplete() { } }
RetrofitFactory类
/** * 此类主要是对retrofit进行配置 * Created by Zero on 2017/5/26. */ public class RetrofitFactory { private RetrofitFactory() { new RuntimeException("反射个毛线,好玩吗?"); } private static OkHttpClient httpClient = MyOkHttpClient.getInstance(); private static ApiService retrofitService; private static String baseUrl = ""; private static Retrofit retrofit; /** * 默认为ApiService * * @return */ public static ApiService getInstance() { if (retrofitService == null) { synchronized (RetrofitFactory.class) { if (retrofitService == null) { retrofitService = getInstanceRetrofit().create(ApiService.class); } } } return retrofitService; } /** * baseUrl */ private static void getBaseUrl() { baseUrl = HttpConfig.getServer(); } private static Retrofit getInstanceRetrofit() { if (retrofit == null) { synchronized (RetrofitFactory.class) { if (retrofit == null) { if (TextUtils.isEmpty(baseUrl)) { getBaseUrl(); } retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(httpClient) .build(); } } } return retrofit; } /** * 用于创建自定义的apiService * * @param clazz * @param <T> * @return */ public static <T> T createRetrofitService(final Class<T> clazz) { return getInstanceRetrofit().create(clazz); } }
RequestUtil类
/** * 请求的封装入口 * Created by Zero on 2017/5/25. */ public class RequestUtil { /** * get方式处理 * * @param url * @param map * @param iResponse * @param <T> */ public static <T> Observable<ResponseBody> getDispose(String url, Map map, final IResponse<T> iResponse) { Observable<ResponseBody> observable = RetrofitFactory.getInstance().executeGet(url, map); return getObservable(observable, iResponse, null); } private static <T> Observable<ResponseBody> getDispose(String url, Map map, final IResponse<T> iResponse, Map cacheMap) { Observable<ResponseBody> observable = RetrofitFactory.getInstance().executeGet(url, map); return getObservable(observable, iResponse, cacheMap); } /** * 自定义ApiService * * @param clazz * @param <T> * @return */ public static <T> T getCutomService(Class<T> clazz) { return RetrofitFactory.createRetrofitService(clazz); } /********************************post********************************/ public static <T> void postDispose(String url, Map map, final IResponse<T> iResponse) { Observable<ResponseBody> observable = RetrofitFactory.getInstance().executePost(url, map); observable.observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(new BaseObserver<>(iResponse)); } private static <T> Observable<ResponseBody> postDispose(String url, Map map, final IResponse<T> iResponse, Map cacheMap) { Observable<ResponseBody> observable = RetrofitFactory.getInstance().executePost(url, map); return getObservable(observable, iResponse, cacheMap); } /** * 获取Observable对象, * 此处名称的get为获取的意思,不是数据请求方式 * @param observable * @param iResponse * @param cacheMap * @param <T> * @return */ private static <T> Observable<ResponseBody> getObservable(Observable<ResponseBody> observable, IResponse<T> iResponse, Map cacheMap) { if (cacheMap != null && cacheMap.size() > 0) { CacheManager.addData(cacheMap.get("cacheKey").toString(), observable, (int) cacheMap.get("period")); } observable.observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(new BaseObserver<>(iResponse)); return observable; } /**************************************cache**************************************/ private static <T> void cacheData(String url, Map map, final IResponse<T> iResponse, int period,boolean isGet){ String cacheKey = url + getCacheKey(map); CacheObject data = CacheManager.getData(cacheKey); if (data == null) { Map cacheMap = new HashMap(); cacheMap.put("cacheKey", cacheKey); cacheMap.put("period", period); if (isGet) { getDispose(url, map, iResponse, cacheMap); }else{ postDispose(url, map, iResponse, cacheMap); } } else { getObservable((Observable<ResponseBody>) data.getObject(), iResponse, null); } } /** * get方式请求,需要做本地cache */ public static <T> void getDisposeWithCache(String url, Map map, final IResponse<T> iResponse, int period) { cacheData(url,map,iResponse,period,true); } /** * post方式请求,需要做本地cache */ public static <T> void postDisposeWithCache(String url, Map map, final IResponse<T> iResponse, int period) { cacheData(url,map,iResponse,period,false); } private static String getCacheKey(Map param) { if (param == null) { return ""; } StringBuffer sb = new StringBuffer(""); TreeMap treeMapParams = new TreeMap(param); for (Object key : treeMapParams.keySet()) { /** * 过滤掉token,根据自己需要 */ if (!key.toString().equals("token")) { sb.append(key).append("=").append(Uri.encode(treeMapParams.get(key).toString())); } } return sb.toString(); } }
RequestUtil、BaseObserver、RetrofitFactory三者相辅相成,通过RetrofitFactory进行retrofit的一系列配置,通过RequestUtil进行get/post数据请求,最后通过BaseObserver把RequestUtil数据请求中获取到的Observer进一步处理。
CacheManager类
/** * 数据请求中对cache进行管理 * Created by Zero on 2017/5/30. */ public class CacheManager { private static Map<String, CacheObject> cacheMap = new HashMap<>(); /** * 添加到cache * @param key * @param data * @param period */ public static void addData(String key, Object data, int period) { CacheObject cacheObject = getData(key); if (cacheObject != null) { cacheObject.setPeriod(period); } else { cacheObject = new CacheObject(data, period); } cacheMap.put(key, cacheObject); } /** * 获取cache * @param key * @return */ public static CacheObject getData(String key) { CacheObject cacheObject = cacheMap.get(key); if (cacheObject != null) { if (cacheObject.isValid()) { return cacheObject; } else { removeInvalidData(key); } } return null; } /** * 移除过期的key * @param key */ public static void removeInvalidData(String key){ if(cacheMap.containsKey(key)){ cacheMap.remove(key); } } }
CacheObject类
/** * Created by Zero on 2017/5/30. */ public class CacheObject { private long timestamp; private int period = -1; private Object data; /** * @param data * @param period -1 表示永不过期,大于0表示过期的时间,单位分钟 */ public CacheObject(Object data, int period) { timestamp = System.currentTimeMillis(); this.data = data; this.period = period; } public Object getObject() { return data; } public boolean isValid() { if (period == -1 || System.currentTimeMillis() < (timestamp + period * 60000)) { return true; } return false; } public void setPeriod(int period) { this.period = period; } public int getPeriod() { return period; } }
CacheObject和CacheManager两个类,分别是对cache进行配置和管理,在数据请求中,比如获取省区县三级目录,这些都不需要多次请求的,可以加载到cache中,当以后再想使用的时候,直接从cache中获取,减轻服务器压力。
LogInterceptor类
/** * 打印网络请求时传输的字段还有返回的json数据 * Created by Zero on 2017/5/27. */ public class LogInterceptor implements Interceptor { private final static String TAG = LogInterceptor.class.getSimpleName(); @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request request = chain.request(); long t1 = System.nanoTime(); okhttp3.Response response = chain.proceed(chain.request()); long t2 = System.nanoTime(); StringBuffer sb = new StringBuffer(); sb.append(request.method()).append("\n"); String url[] = request.url().toString().split("\\?"); sb.append(url[0]).append("\n"); if (url.length == 2) { String params[] = url[1].split("&"); for (String param : params) { sb.append(Uri.decode(param)).append("\n"); } } if(request.body() instanceof FormBody){ FormBody postParams = ((FormBody) request.body()); if (postParams != null) { sb.append("post:").append("\n"); int size = postParams.size(); for (int i = 0; i < size; i++) { sb.append(postParams.encodedName(i) + "=" + java.net.URLDecoder.decode(postParams.encodedValue(i), "utf-8")).append("\n"); } } } okhttp3.MediaType mediaType = response.body().contentType(); String content = response.body().string(); Log.v(TAG, String.format(Locale.getDefault(), "%s cost %.1fms%n%s", sb.toString(), (t2 - t1) / 1e6d, format(content))); //格式化打印json // Log.v(TAG, String.format(Locale.getDefault(), "%s cost %.1fms%n%s", sb.toString(), (t2 - t1) / 1e6d, format(content))); // Log.v(TAG, String.format(Locale.getDefault(), "%s cost %.1fms%n%s", sb.toString(), (t2 - t1) / 1e6d, content)); return response.newBuilder() .body(okhttp3.ResponseBody.create(mediaType, content)) .build(); } public static String format(String jsonStr) { int level = 0; StringBuffer jsonForMatStr = new StringBuffer(); for (int i = 0; i < jsonStr.length(); i++) { char c = jsonStr.charAt(i); if (level > 0 && '\n' == jsonForMatStr.charAt(jsonForMatStr.length() - 1)) { jsonForMatStr.append(getLevelStr(level)); } switch (c) { case '{': case '[': jsonForMatStr.append(c + "\n"); level++; break; case ',': jsonForMatStr.append(c + "\n"); break; case '}': case ']': jsonForMatStr.append("\n"); level--; jsonForMatStr.append(getLevelStr(level)); jsonForMatStr.append(c); break; default: jsonForMatStr.append(c); break; } } return jsonForMatStr.toString(); } private static String getLevelStr(int level) { StringBuffer levelStr = new StringBuffer(); for (int levelI = 0; levelI < level; levelI++) { levelStr.append("\t"); } return levelStr.toString(); } }
一图胜千言,不做更多赘述。
由于项目今天刚搭建好,肯定也存在很多问题,日后会有不断完善,今天就不多做叙述了,如有什么疑问,可以留言,也可以加QQ群,如有什么错误,也请多多指正,共勉共进。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ext JS学习指南
(美)布莱兹、(美)拉姆齐、(美)弗雷德里克 / 孔纯、肖景海、张祖良 / 人民邮电出版社 / 2009-10 / 39.00元
《Ext JS学习指南》系统化地介绍了Ext JS的基础知识,从框架的下载安装到各种常用小部件的实例介绍,从如何自定义小部件到Ext JS代码复用和扩展机制,《Ext JS学习指南》覆盖了Ext JS知识的所有主要方面。作为Web 2.0时代企业应用的一把开发利器,Ext JS为企业应用开发的表现层实现提供了优秀的解决方案。 如果你掌握了HTML,并且了解一般的CSS和JavaScript的......一起来看看 《Ext JS学习指南》 这本书的介绍吧!