• 前言
  • AndroidAnnotations与注解框架对比
      • 1、ButterKnife(编译时注解)
      • 2、XUtils(运行时注解)
      • 3、AndroidAnnotations(编译时注解)
      • 4、注解框架对比
  • Retrofit+OkHttp
  • RxJava
  • MVP搭建
    • 1、Retrofit+Okhttp封装
    • 2、BaseView、BasePresenter
    • 3、使用RxJava配合Retrofit进行请求:
    • 4、使用RxJava管理请求
  • 使用
  • 总结

前言

  之前一直在qq空间和公众号发布文章,这几天才转移到博客平台。对博客平台的各种规则还不是特别熟悉,属于博客小白。但是我会坚持更新,将实用的,易懂的文章推送给大家。感谢大家的点赞和关注。
  上一篇文章 《Android Gradle统一管理打包》收到了很多点赞和评论。Gradle统一管理在项目体量较小的情况下是不必要的,但是如果体量稍大,或者使用了插件化框架。那么它的好处就会显示出来,在上一篇的基础上,继续进行探索,梳理流行框架Annotations+Retrofit+Rxjava2+okhttp3+MVP框架的搭建。
  而Annotations+Retrofit+Rxjava2+okhttp3+MVP框架又和gradle有什么关系呢?
  本来想通过一篇 Small插件化+Annotations+Retrofit+Rxjava2+okhttp3+MVP 来描述在使用插件化的情况下moudle过多的情况,但是Small插件化是一个很重要的东西,如果全部混在一起写会造成篇幅过长或者内容过于杂乱。所以本篇抛开Small插件化只描述Annotations+Retrofit+Rxjava2+okhttp3+MVP的搭建。下一篇博文再对Small插件化和此框架进行融合。

AndroidAnnotations与注解框架对比

注解(Annotation)也叫元数据。是一种代码级别的说明。它与类、接口、枚举在同一个层次。可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。说白了就是一种标记,可以用自己的注解解释器在编译时进行处理(编译时注解),或者在运行时通过反射对其进行处理(运行时注解)。注解框架有很多,常用的有ButterKnife、AndroidAnnotations、XUtils等。

1、ButterKnife(编译时注解)

ButterKnfie出自Jakewharton大神,是使用非常多的一个注解框架。
ButterKnife通过反射在编译时生成一个ViewBinder对象,类似于:

MainActivity$ViewBinder implements ButterKnife.ViewBinder

在这个类中就有使用注解声明的一些组件和监听。
因为ButterKnife在编译时通过反射一次性生成了这些需要使用到的类,所以对运行时的速度是没有什么影响的。

2、XUtils(运行时注解)

XUtils和ButterKnife类似,都使用了反射来对注解声明的字段或者对象进行赋值。
区别在于XUtils是在运行时进行反射,众所周知反射的效率相对于原生代码是较慢的。
虽然设备性能在提升,但是在组件、事件过多的情况下还是造成初始化速度降低。

3、AndroidAnnotations(编译时注解)

AndroidAnnotations和ButterKnife类似,都是编译时注解。所以在性能方面不相上下,但是两者的实现思路却不同:
ButterKnfie使用反射来对注解标记的字段赋值。而AndroidAnnotations会生成一个继承于当前类的子类来对标记的字段进行赋值。
AndroidAnnotations生成的类后面都会带有一个符号“_”,运行的时候也运行的是这个生成的子类,而不是我们的当前类。
看一下AndroidAnnotations生成的代码就能够很清楚的了解它的思路,首先我们看一下使用了注解的StartActivity中的代码。
StartActivity.java

//使用EActivity注解加载布局@EActivity(R.layout.activity_start)public class StartActivity extends BaseActivity implements StartContract.View {    private StartContract.Presenter mPresenter;        //使用ViewById注解查找组件    @ViewById(R.id.cdpv_start)    CountDownProgressView mCountDownView;            //倒计时组件    private static final long mDelayTimeLong = 2000l;//倒计时毫秒    //加载布局之前调用方法的注解    @AfterInject    void afterInject() {        new StartPresenter(this);        StatusBarUtil.immersive(this);    }        //加载布局之后调用方法的注解    @AfterViews    void afterViews() {        mCountDownView.setTimeMillis(mDelayTimeLong);        mCountDownView.start();        countDown();    }        //运行在子线程的注解    @Background(delay = mDelayTimeLong, id = "delay_to_main")    void countDown() {        startMain();    }    //运行在主线程的注解    @UiThread    void startMain() {        if (UserStateUtil.getInstance().isLoggedOn()) {            MainActivity_.intent(getContext()).start();        } else {            LoginActivity_.intent(getContext()).start();        }        finish();    }        //点击事件监听注解    @Click(R.id.cdpv_start)    void onCountDownClick() {        BackgroundExecutor.cancelAll("delay_to_main", true);        startMain();    }    @Override    public void setPresenter(StartContract.Presenter presenter) {        mPresenter = Null.checkNotNull(presenter);    }    @Override    public Activity getContext() {        return this;    }    @Override    protected void onDestroy() {        super.onDestroy();        BackgroundExecutor.cancelAll("delay_to_main", true);    }}

上面的代码是启动页本类的实现,可以看到使用了AndroidAnnotations的一些常用注解。
而这些注解都会通过框架生成对应的代码,下面是通过本类生成的StartActivity_.java子类的部分代码:
StartActivity_ . java 部分代码

public final class StartActivity_        extends StartActivity        implements HasViews, OnViewChangedListener {    private final OnViewChangedNotifier onViewChangedNotifier_ = new OnViewChangedNotifier();    @Override    public void onCreate(Bundle savedInstanceState) {        OnViewChangedNotifier previousNotifier = OnViewChangedNotifier.replaceNotifier(onViewChangedNotifier_);        init_(savedInstanceState);//此处调用了AfterInject        super.onCreate(savedInstanceState);        OnViewChangedNotifier.replaceNotifier(previousNotifier);        setContentView(R.layout.activity_start);//此处通过EActivity加载    }        private void init_(Bundle savedInstanceState) {        OnViewChangedNotifier.registerOnViewChangedListener(this);        afterInject();    }    @Override    public <T extends View> T internalFindViewById(int id) {        return ((T) this.findViewById(id));    }    @Override    public void setContentView(int layoutResID) {        super.setContentView(layoutResID);        onViewChangedNotifier_.notifyViewChanged(this);    }        //查找组件设置监听    @Override    public void onViewChanged(HasViews hasViews) {        this.mCountDownView = hasViews.internalFindViewById(R.id.cdpv_start);        if (this.mCountDownView != null) {            this.mCountDownView.setOnClickListener(new OnClickListener() {                        @Override                        public void onClick(View view) {                           StartActivity_.this.onCountDownClick();                        }                   }            );        }        afterViews();    }        //创建UI线程    @Override    void startMain() {        UiThreadExecutor.runTask("", new Runnable() {                    @Override                    public void run() {                        StartActivity_.super.startMain();                    }                }                , 0L);    }        //创建子线程    @Override    void countDown() {        BackgroundExecutor.execute(new BackgroundExecutor.Task("delay_to_main", 2000L, "") {                   @Override                   public void execute() {                       try {                           StartActivity_.super.countDown();                       } catch (final Throwable e) {                           Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);                       }                   }               }        );    }}

从上面的代码可以看到,生成的StartActivity_子类中有本类定义的方法,还有对注解事件的实现。
原理大概了解了之后,就是AndroidAnnotations的使用了,使用AndroidAnnotations需要导入:

//AndroidAnnotationsimplementation 'org.androidannotations:androidannotations:4.3.1'

在AndroidManifest中注册使用@EActivity标记的Activity需要使用框架生成的Activity,如:

<activity    android:name=".moudle.login.LoginActivity_"    android:screenOrientation="portrait" />**

同样,使用@EApplication标记的Application也需要使用框架生成的子类,如:

android:name=".application.XXXXApplication_"

Service也同样类似,都需要使用框架生成的子类。
AndroidAnnotations全部的注解文档在其GitHub上可以查阅:
https://github.com/androidannotations/androidannotations/wiki/AvailableAnnotations

4、注解框架对比


这张图片引用自CSDN,Annotations使用的复杂度稍高于另外两个框架,并不是指接入特别困难。

Retrofit+OkHttp

Retrofit是一个请求框架,和Okhttp出自同一个团队,OkHttp负责进行底层的网络请求,Retrofit负责对请求事件进行装配,对请求结果进行解析。Retrofit对请求进行封装,很大程度上降低了代码的耦合程度。
引入:

    implementation 'com.squareup.okhttp3:okhttp:3.8.1'              //okhttp    implementation 'com.squareup.okhttp3:logging-interceptor:3.8.1' //okhttp拦截器    implementation 'com.squareup.retrofit2:retrofit:2.0.2'          //retrofit    implementation 'com.squareup.retrofit2:converter-gson:2.0.2'    //retrofit的Gson解析器    implementation 'com.squareup.retrofit2:adapter-rxjava:2.0.2'    //retrofit的RxJava适配器支持    implementation 'com.google.code.gson:gson:2.7'                  //Gson

RxJava

RxJava是一个很火的线程调度框架,这样描述或许不太准确,想要深入了解的可以打开传送门:
《给 Android 开发者的 RxJava 详解》
《RxJava 与 Retrofit 结合的最佳实践》
引入:

    implementation 'io.reactivex:rxandroid:1.0.1'                   //RxAndroid    implementation 'io.reactivex.rxjava2:rxjava:2.1.1'              //RxJava

MVP搭建

使用AndroidAnnotations和Retrofit+Rxjava2+okhttp3都简化了代码的逻辑,降低了代码的耦合度,再引入MVP的架构,则会让项目更加清晰明了。MVP的概念已经很早就诞生了,经过了很长时间的发展,依然被很多人采用,存在即是合理,接下来就整个框架进行展示。

1、Retrofit+Okhttp封装

首先,需要对Retrofit和Okhttp进行初始化:

/** * @author WuYang * @version v1.0 设置网络请求 */public abstract class RetrofitSingleton {    protected static ApiInterface apiService = null;    public static Retrofit retrofit = null;    public static OkHttpClient okHttpClient = null;        //此处的ApiInterface就是通过注解定义请求接口的类    public static void init() {        initOkHttp();        initRetrofit();        if (retrofit != null)            apiService = retrofit.create(ApiInterface.class);    }        //初始化OkHttp    private static void initOkHttp() {        OkHttpClient.Builder builder = new OkHttpClient.Builder();        //如果不是发布版本,则初始化logging拦截器        if (!TzcmApplication_.isRelease) {            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);            builder.addInterceptor(loggingInterceptor);        } else {            Log.i("温馨提示", "反编译及攻击本软件将承担法律风险!请勿以身试法!");        }        //是否强制取消代理来防止抓包        if (TzcmApplication_.isCancelProxy) {            builder.proxy(Proxy.NO_PROXY);        }        //设置超时        builder.connectTimeout(90, TimeUnit.SECONDS);        builder.readTimeout(90, TimeUnit.SECONDS);        builder.writeTimeout(90, TimeUnit.SECONDS);        //错误重连、断线重连        builder.retryOnConnectionFailure(false);        //添加自定义请求头捕获器,如token等        builder.addInterceptor(new AddCookiesInterceptor());        okHttpClient = builder.build();    }        //初始化Retrofit    private static void initRetrofit() {        retrofit = new Retrofit.Builder()                .baseUrl(XXXXApplication_.isRelease ? ApiInterface.API_HOST_ONLINE : ApiInterface.API_HOST_OFFLINE)                .client(okHttpClient)                .addConverterFactory(GsonConverterFactory.create())                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())                .build();    }        //拦截器,添加一些固定的参数。如:token、deviceId、cookie等    public static class AddCookiesInterceptor implements Interceptor {        @Override        public Response intercept(Chain chain) throws IOException {            Request.Builder builder = chain.request().newBuilder();            //添加token            if (UserStateUtil.getInstance().isLoggedOn()) {                builder.addHeader(CommonKey.ApiParams.TOKEN, UserStateUtil.getInstance().getToken());            }            return chain.proceed(builder.build());        }    }}

接下来就创建ApiInterface:

/** * @author WuYang * @version v1.0 API接口 */public interface ApiInterface {    String API_HOST_OFFLINE = "http://www.xxxxxxxx.com/";//线下的api 地址    String API_HOST_ONLINE = "http://www.xxxxxxxx.com/";//线上地址    String API_LOGIN = "ChujiMallServer/app/appDelivery/api/deliveryLogin";    //用户登录    @FormUrlEncoded    @POST(API_LOGIN)    Observable<UserInfoResponse> mLoginAPI(@FieldMap Map<String, Object> params);

定义好接口之后需要对接口声明的方法进行实现:

public class RetrofitApi extends RetrofitSingleton {    private volatile static RetrofitApi instance;    /**     * Returns singleton class instance     * 使用单例模式中的双重加锁     */    public static RetrofitApi getInstance() {        if (instance == null) {            synchronized (RetrofitApi.class) {                if (instance == null) {                    instance = new RetrofitApi();                }            }        }        return instance;    }    /**     * 登录     * @param account  账号,必填     * @param password 密码,必填     */    public Observable login(String account, String password) {        HashMap<String, Object> params = new HashMap<>();        params.put(CommonKey.ApiParams.LOGIN_ID, account);        params.put(CommonKey.ApiParams.PASSWORD, password);        return apiService.mLoginAPI(params);    }}

然后再Application中进行init:

@EApplicationpublic class XXXXApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        initRetrofit();    }    private void initRetrofit() {        RetrofitSingleton.init();    }}

2、BaseView、BasePresenter

定义接口BaseView和BasePresenter。

public interface BaseView<T extends BasePresenter> {    //设置Presenter    void setPresenter(T presenter);    //获取上下文    Activity getContext();    //可以添加其余在View层的方法    ... ...}public interface BasePresenter {    //提供一个公用的开始方法    void start();    //可以添加一些其余公用的方法,如showLoading(),dismissLoading()等。    ... ...}

3、使用RxJava配合Retrofit进行请求:

/** * @author YangWu * @description 请求接口基类 */public abstract class OnRequestCallback<T extends BaseResponse> extends Subscriber<T> {    public abstract void onFailed(int code, String respMsg, String respCode, T response);    public abstract void onException(Throwable e);    public abstract void onResponse(T response);    public abstract void onFinish();    public void onStart() {        if (!RxNetTool.isAvailable(TzcmApplication_.getInstance())) {            onFailed(-1, "网络连接异常,请检查网络连接", "E99999", null);            onFinish();            unsubscribe();            return;        }    }    public final void onCompleted() {        onFinish();    }    public final void onError(Throwable e) {        try {            onException(e);            onFinish();        } catch (Exception ex) {            e.printStackTrace();        }    }    public final void onNext(T response) {        if (!Null.isNull(response) && response.isSuccess()) {            onResponse(response);        } else {            onFailed(response.code, response.respMsg, response.respCode, response);        }    }}/** * @author YangWu * @description 请求接口 */public abstract class OnSimpleRequestCallback<T extends BaseResponse> extends OnRequestCallback<T> {    private Context mContext;    public OnSimpleRequestCallback(Context context) {        this.mContext = context;    }    @Override    public void onStart() {        super.onStart();    }    @Override    public void onFinish() {    }    @Override    public void onFailed(int code, String respMsg, String respCode, T response) {        ToastUtils.show(respMsg);        XPopupManager.dismissLoading();    }    @Override    public void onException(Throwable e) {        Log.e(mContext.getPackageName(), e.getMessage());        XPopupManager.dismissLoading();    }}

定义一个BaseResponse来统一接口请求的数据格式:

/** * @author YangWu * @description 请求体基类 */public class BaseResponse implements Serializable {    public String respMsg;    public String respCode;    public int code;    public boolean isSuccess() {        return code == 200;    }}

4、使用RxJava管理请求

到这里,框架需要的基本东西已经搭建完成了。接下来使用RxJava对请求的队列进行管理:

/** * @author YangWu * @description RxJavaActionManager 请求队列操作接口 */public interface RxActionManager<T> {    void add(T tag, Subscription subscription); //添加请求    void remove(T tag);                         //移除请求    void cancel(T tag);                         //取消请求    void cancelAll();                           //取消所有请求}/** * @author YangWu * @description RxApi管理器 */public class RxApiManager implements RxActionManager<Object> {    private static RxApiManager sInstance = null;    private ArrayMap<Object, Subscription> maps;    public static RxApiManager get() {        if (sInstance == null) {            synchronized (RxApiManager.class) {                if (sInstance == null) {                    sInstance = new RxApiManager();                }            }        }        return sInstance;    }    @TargetApi(Build.VERSION_CODES.KITKAT)    private RxApiManager() {        maps = new ArrayMap<>();    }    @TargetApi(Build.VERSION_CODES.KITKAT)    @Override    public void add(Object tag, Subscription subscription) {        maps.put(tag, subscription);    }    @TargetApi(Build.VERSION_CODES.KITKAT)    @Override    public void remove(Object tag) {        if (!maps.isEmpty()) {            maps.remove(tag);        }    }    @TargetApi(Build.VERSION_CODES.KITKAT)    public void removeAll() {        if (!maps.isEmpty()) {            maps.clear();        }    }    @TargetApi(Build.VERSION_CODES.KITKAT)    @Override    public void cancel(Object tag) {        if (maps.isEmpty()) {            return;        }        if (maps.get(tag) == null) {            return;        }        if (!maps.get(tag).isUnsubscribed()) {            maps.get(tag).unsubscribe();            maps.remove(tag);        }    }    @TargetApi(Build.VERSION_CODES.KITKAT)    @Override    public void cancelAll() {        if (maps.isEmpty()) {            return;        }        Set<Object> keys = maps.keySet();        for (Object apiKey : keys) {            cancel(apiKey);        }    }}

使用

还是以Login为例,首先实现一个LoginContract对View层和Presenter层的方法进行管理。

public class LoginContract {    //LoginActivity中需要实现的方法LoginSuccess();    interface View extends BaseView<Presenter> {        void loginSuccess();    }    //LoginPresenter中需要实现的方法login();    interface Presenter extends BasePresenter {        void login(String number, String password);    }}

然后对LoginActivity进行编写:

@EActivity(R.layout.activity_login)public class LoginActivity extends BaseActivity implements LoginContract.View {    private LoginContract.Presenter mPresenter;        //查找组件    @ViewById(R.id.til_login_account)    TextInputLayout mAccountTil;    @ViewById(R.id.et_login_account)    EditText mAccountEt;    @ViewById(R.id.til_login_password)    TextInputLayout mPasswordTil;    @ViewById(R.id.et_login_password)    EditText mPasswordEt;    @ViewById(R.id.tv_login)    TextView mLoginTv;        //依赖注入后设置StatusBar模式为沉浸式,并且实例化Presenter    @AfterInject    void afterInject() {        StatusBarUtil.immersive(this);        new LoginPresenter(this);    }    @Click(R.id.tv_login)    void onLoginTvClick() {        String account = mAccountEt.getText().toString().trim();        String password = mPasswordEt.getText().toString().trim();        if (PublicUtils.isEmpty(account)) {            AnimationUtils.startShakeByViewIsNeedVibrate(mAccountTil, true);            mAccountEt.setError("请输入账号");            return;        }        if (PublicUtils.isEmpty(password)) {            AnimationUtils.startShakeByViewIsNeedVibrate(mPasswordTil, true);            mPasswordEt.setError("请输入密码");            return;        }        mPresenter.login(account, password);    }        //View层方法,登录成功后在Presenter层会回调此方法    @Override    public void loginSuccess() {        MainActivity_.intent(getContext()).start();        finish();    }    @Override    public void onBackPressed() {        if (RxTool.isFastClick(2000)) {            TzcmApplication_.getInstance().exitApp();        } else {            ToastUtils.show("再按一次退出程序");        }    }        //将Presenter绑定到View层    @Override    public void setPresenter(LoginContract.Presenter presenter) {        mPresenter = Null.checkNotNull(presenter);    }        //View层实现的获取上下文的方法    @Override    public Activity getContext() {        return this;    }    @Override    protected void onDestroy() {        super.onDestroy();        //在页面销毁时通过RxJava取消登录请求        RxApiManager.get().cancel(ApiInterface.API_LOGIN);    }}

可以看到,在点击登录按钮时调用了mPresenter.login(account, password);方法,这个方法在P层进行处理:

/** * @author: WuYang * @describe: 登录Presenter */public class LoginPresenter implements LoginContract.Presenter {    private LoginContract.View mView;        //绑定View层    public LoginPresenter(@NonNull LoginContract.View view) {        mView = Null.checkNotNull(view);        mView.setPresenter(this);    }        //继承自Presenter的方法,可以处理一些页面初始化的逻辑    @Override    public void start() {    }       /**    * 在View层调用login方法    * 通过Retrofit的subscribe观察者模式进行请求,使用上面的RxJava的请求框架OnSimpleRequestCallback进行请求    * 接收的UserInfoResponse是继承于BaseResponse的子类,根据业务逻辑具有自己的属性    * 请求成功后通过mView.loginSuccess();方法回调到View层进行处理    */    @Override    public void login(String number, String password) {        XPopupManager.showLoading(mView.getContext(), "正在登录", ApiInterface.API_LOGIN);        Subscriber subscriber = new OnSimpleRequestCallback<UserInfoResponse>(mView.getContext()) {            @Override            public void onResponse(UserInfoResponse response) {                UserStateUtil.getInstance().updateUser(response.data);                XPopupManager.dismissLoading();                mView.loginSuccess();            }        };        RxApiManager.get().add(ApiInterface.API_LOGIN, subscriber);        RetrofitApi.getInstance().login(number, password)                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(subscriber);    }}

总结

  通过这样的构建方式可以很好的将代码解耦,便于项目后期的维护和开发。接下来我会将Small插件化框架再融合进行,然后通过Gradle统一管理。后续我会将搭建的这个框架代码上传至github,应该就是这几天,要从qq空间和公众号转移过来的东西有点多。


长路漫漫,菜不是原罪,堕落才是原罪。
我的CSDN:https://blog.csdn.net/wuyangyang_2000
我的简书:https://www.jianshu.com/u/20c2f2c3560a
我的掘金:https://juejin.im/user/58009b94a0bb9f00586bb8a0
我的GitHub:https://github.com/wuyang2000
个人网站:http://www.xiyangkeji.cn
个人app(茜茜)蒲公英连接:https://www.pgyer.com/KMdT
我的微信公众号:茜洋 (定期推送优质技术文章,欢迎关注)
Android技术交流群:691174792

以上文章均可转载,转载请注明原创。


更多相关文章

  1. Android之Volley框架加载网络图片
  2. [置顶] android 框架
  3. 【Android多媒体】Android5.0 NuPlayer多媒体框架【1】
  4. 【开源项目2】Android推送框架 androidpn
  5. Retrofit2 ,Dagger2等常用框架注解功能介绍
  6. 【Android(安卓)- 框架】之可悬浮列表StickyHeadersRecyclerView
  7. Android缓存机制&一个缓存框架推荐
  8. 使用java语言中的注解生成器生成代码
  9. Android游戏框架libgdx——BitmapFont的构造

随机推荐

  1. ShareSDK Android常见问题汇总
  2. Android(安卓)studio button 按钮 四种绑
  3. android音视频采集参考
  4. Android与HTML5交互模版
  5. Dalvik opcodes 【转过来 备份】
  6. Android(安卓)Bluetooth Stack: Bluedroi
  7. 安卓加载网页
  8. Android(安卓)recyclerview 支持网格布局
  9. android 检查SD卡是否存在
  10. Android(安卓)使用Room 生成不了数据库文