此处搭建的框架是目前最新版本,项目今天刚搭建好,(^__^) 嘻嘻……。
先撸上包:
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 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 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 MethodHandler(Type[] types) {
List 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
* @return
*/
public static T createRetrofitService(final Class clazz) {
return getInstanceRetrofit().create(clazz);
}
}
RequestUtil类
/**
* 请求的封装入口
* Created by Zero on 2017/5/25.
*/
public class RequestUtil {
/**
* get方式处理
*
* @param url
* @param map
* @param iResponse
* @param
*/
public static Observable getDispose(String url, Map map, final IResponse iResponse) {
Observable observable = RetrofitFactory.getInstance().executeGet(url, map);
return getObservable(observable, iResponse, null);
}
private static Observable getDispose(String url, Map map, final IResponse iResponse, Map cacheMap) {
Observable observable = RetrofitFactory.getInstance().executeGet(url, map);
return getObservable(observable, iResponse, cacheMap);
}
/**
* 自定义ApiService
*
* @param clazz
* @param
* @return
*/
public static T getCutomService(Class clazz) {
return RetrofitFactory.createRetrofitService(clazz);
}
/********************************post********************************/
public static void postDispose(String url, Map map, final IResponse iResponse) {
Observable observable = RetrofitFactory.getInstance().executePost(url, map);
observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new BaseObserver<>(iResponse));
}
private static Observable postDispose(String url, Map map, final IResponse iResponse, Map cacheMap) {
Observable observable = RetrofitFactory.getInstance().executePost(url, map);
return getObservable(observable, iResponse, cacheMap);
}
/**
* 获取Observable对象,
* 此处名称的get为获取的意思,不是数据请求方式
* @param observable
* @param iResponse
* @param cacheMap
* @param
* @return
*/
private static Observable getObservable(Observable observable, IResponse 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 void cacheData(String url, Map map, final IResponse 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) data.getObject(), iResponse, null);
}
}
/**
* get方式请求,需要做本地cache
*/
public static void getDisposeWithCache(String url, Map map, final IResponse iResponse, int period) {
cacheData(url,map,iResponse,period,true);
}
/**
* post方式请求,需要做本地cache
*/
public static void postDisposeWithCache(String url, Map map, final IResponse 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 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群,如有什么错误,也请多多指正,共勉共进。
github链接戳此、CSDN下载链接戳此。