最新Retrofit + RxJava + MVP

标签: mvprxjavaretrofitframework
3067人阅读 评论(15) 收藏 举报
分类:

此处搭建的框架是目前最新版本,项目今天刚搭建好,(^__^) 嘻嘻……。
先撸上包:
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在解析的效率上对比其它几个第三方,还是明显偏低的。

废话不多说,直接撸代码,因为写了很多详细的注释,未来还会写几篇帖子专门分析,此处就不多做介绍。

先看下目录结构图

结构

结构2

除了删除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群,如有什么错误,也请多多指正,共勉共进。

github链接戳此CSDN下载链接戳此。

4
1

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:211300次
    • 积分:2610
    • 等级:
    • 排名:第14914名
    • 原创:44篇
    • 转载:0篇
    • 译文:0篇
    • 评论:304条
    QQ交流群

    Android路上

    611566797
    博客专栏
    我的公众号