前言
今年的Android技术圈中MVP,Dagger2,Rxjava,Retrofit这些词汇非常火,随便打开一个技术论坛都充斥着大量的关于这些技术的文章,Github也充斥着各种以基于MVP+Retrofit+RxJava+Dagger2+MaterialDesign开发的xxxx为标题的开源项目或Demo.
但是大家这么热心的开源此类项目,一直重复的做着同样的事教授大家使用的方式和技巧有没有想过依赖一个第三方库,就可以快速的搭建此类框架?
特性
框架结构
包结构
开发须知
Libraries简介
1 开发准备
此框架适合自己做定制修改,所有暂时不上传至Jcenter或Maven,请自行下载或clone
1.1 导入框架
- compile project(':arms')
1.2 引用config.build
本框架提供一个引用大量第三方库的config.gradle文件,用于第三方库版本管理,将config.gradle复制进根目录,并在项目的***build.gradle中引用它
1.2.1 使用config.build
因为在***build.gradle中引用了它,所以在整个项目的所有build.gradle中都可以使用rootProject.xxx来使用它里面的内容
也可以使用它来管理一些项目的信息,这样有多个module也可以直接使用一个信息
1.3 依赖Dagger2
本框架全部使用Dagger2管理,所以必须依赖Dagger2,找到app的build.gradle,加入如下代码
1.4 配置AndroidManifest
1.4.1 添加权限
1.4.2 配置Autolayout Meta
使用Autolayout 自适应框架必须配置Meta属性及设计图的宽高,详情参考Autolayout
1.4.3 引用Glide自定义属性
本框架默认使用Glide加载图片,但提供一个管理器ImageLoader提供统一接口,使用策略者模式可轻松替换图片加载框架,本框架默认提供Glide的自定义缓存配置信息,使用它之前先引用它的自定义配置信息
- <!--glide配置-->
- <meta-data
- android:name="com.jess.arms.widget.imageloader.glide.GlideConfiguration"
- android:value="GlideModule"/>
1.5 混淆
由于本框架依赖大量三方库,所以已经在arms Module下的proguard-rules.pro中提供了所有规则,如果想使用它,请复制它替换app Module中的proguard-rules.pro,混淆时可以根据自己的需求修改或添加规则,混淆前务必注意将Java Bean,自定义组件添加进规则
1.6 版本更新
! 一定不能修改common包的包名,旧版本需要找到和common包下同名的类并删除,然后重
新引用common包里的类
如果你获得本框架的方式是通过clone或者下载:
如果你获得本框架的方式是通过fork到自己仓库后,clone或下载:
2 快速开始
2.1 继承BaseApplication
新建项目的Application继承自BaseApplication,并在AndroidManifest中声明
2.1.1 AppComponent
Application生命周期是和App是一样的,所以是适合提供一些单例对象,本框架使用Dagger2管理,所以使用AppComponent来提供全局所有的单例对象
创建AppComponent接口
2.1.2 ServiceModule
ServiceModule提供RetrofitApi对应的Service,这些Service对象在AppComponent中注入ServiceManager(需继承BaseServiceManager)中统一管理
- public interface CommonService {
- String HEADER_API_VERSION = "Accept: application/vnd.github.v3+json";
- @Headers({HEADER_API_VERSION})
- @GET("/users")
- Observable<List<User>> getUsers(@Query("since") int lastIdQueried, @Query("per_page") int perPage);
- }
- @Module
- public class ServiceModule {
- @Singleton
- @Provides
- CommonService provideCommonService(Retrofit retrofit) {
- return retrofit.create(CommonService.class);
- }
- }
2.1.3 CacheModule
Cache层默认使用RxCache,CacheModule提供RetrofitApi对应的Cache对象,这些Cache对象在AppComponent中注入CacheManager(需继承BaseCacheManager)中统一管理
- public interface CommonCache {
- @LifeCache(duration = 2, timeUnit = TimeUnit.MINUTES)
- Observable<Reply<List<User>>> getUsers(Observable<List<User>> oUsers, DynamicKey idLastUserQueried, EvictProvider evictProvider);
- }
- @Module
- public class CacheModule {
- @Singleton
- @Provides
- CommonCache provideCommonService(RxCache rxCache) {
- return rxCache.using(CommonCache.class);
- }
- }
2.2 继承BaseActivity
让项目的基类Activity继承BaseActivity,BaseActivity默认注入Presenter,所以如果要使用Presenter必须指定对应的范型,并且提供注入Presenter所需要的Component
2.3 继承BaseFragment
让项目的基类Fragment继承BaseFragment,BaseFragment默认注入Presenter,所以如果要使用Presenter必须指定对应的范型,并且提供注入Presenter所需要的Component
2.4 MVP实战
定义业务逻辑MVP,继承MVP各自的基类即可,这里可以稍微粗力度的定义MVP类,即无需每个Fragment和Activity(每个页面)都定义不同的MVP类,可以按照相同的业务逻辑使用一组MVP类
2.4.1 Contract
这里根据Google官方的MVP项目,可以在Contract中定义MVP的接口,便于管理,此框架无需定义Presenter接口,所以Contract只定义Model和View的接口
2.4.2 View
一般让Activity或Fragment实现Contract中定义的View接口,供Presenter调用对应方法操作UI,BaseActivity默认注入Presenter,如想使用Presenter,必须指定Presenter的范型,和实现setupActivityComponent来提供Presenter需要的Component和Module
2.4.3 Model
Model实现Contract的Model接口,并且继承BaseModel,指定范型为,上面定义的ServiceManager和CacheManager,然后通过两个Manager拿到需要的Service和Cache为Presenter提供需要的数据(是否使用缓存请自行选择)
2.4.4 Presenter
Presenter在MVP中的大部分的作用为通过从Model层接口获取数据,在调用View层接口显示数据,首先实现BasePresenter,指定Model和View的范型,注意一定要指定Contract中定义的接口,Presenter需要的Model和View,都使用Dagger2注入,这样即解藕又方便测试,怎么注入?
2.4.5 MVP Module
这里的Module提供当前业务逻辑对应的View和Model接口(Contract中定义的接口)的实现类,Model需要AppComponent中提供的ServiceManager和CacheManager来实现网络请求和缓存,所以需要通过Component依赖AppComponent拿到这两个Manager
2.4.6 MVP Component
这里需要注意的是此Component必须依赖AppComponent,这样才能提供Model需要的ServiceManager和CacheManager,提供inject()方法就能将Module及AppComponent中提供的对象注入到对应的类中,inject()中的参数不能是接口,怎么注入?
- @ActivityScope
- @Component(modules = UserModule.class,dependencies = AppComponent.class)
- public interface UserComponent {
- void inject(UserActivity activity);
- }
2.4.7 Dagger Scope
在上面的代码中ActivityScope大量出现在Module和Component中,Dagger2使用Scope限制每个Module中提供的对象的生命,Dagger2默认只提供一个@SingletonScope即单例,本框架提供@ActvityScope和@FragmentScope,如有其他需求请自行实现,Module和Component定义相同的Scope后Module中提供的对象的生命周期会和Component中一样(即在Component生命周期内,如需使用到Moudle中提供的对象,只会调用一次@Provide注解的方法得到此对象)
2.4.8 MVP总结
以后每个业务逻辑都重复构造这些类,只是换个名字而已,值得注意的是MVP刚开始用时确实会觉得平白无故多了很多类,非常繁琐麻烦,但是等页面代码逻辑越来多时,你会发现其中的好处,逻辑清晰,解耦,便于团队协作,测试容易,错误好定位,所以现在本框架提供Template自动生成代码解决这个痛点,让开发者更加愉快的使用本框架
3 功能使用
3.1 App全局配置信息(使用Dagger注入)
GlobeConfigModule使用建造者模式将App的全局配置信息封装进Module(使用Dagger注入到需要配置信息的地方),可以配置CacheFile,InterCeptor等,因为使用的是建造者模式所以如你有其他配置信息需要使用Dagger注入,直接就可以添加进Builder并且不会影响到其他地方
- //如需添加个Boolean字段提供给Log工具类,来判断是否打印Log
- @Module
- public class GlobeConfigModule {
- private Boolean isLog;
- private GlobeConfigModule(Buidler buidler) {
- this.isLog = builder.isLog
- }
- public static Buidler buidler() {
- return new Buidler();
- }
- public static final class Buidler {
- private Boolean isLog;
- private Buidler() {}
- //1.给Builder中添加个方法接受isLog字段
- public Buidler isLog(Boolean isLog) {
- this.isLog = isLog;
- return this;
- }
- public GlobeConfigModule build() {
- return new GlobeConfigModule(this);
- }
- }
- //2.使用@Provides,将isLog返回出去,供Dagger注入到Log工具类
- @Singleton
- @Provides
- Boolean provideIsLog() {
- return isLog;
- }
- }
3.2 全局捕捉Http请求和响应
通过GlobeConfigModule.globeHttpHandler()方法传入GlobeHttpHandler
- @Override
- protected GlobeConfigModule getGlobeConfigModule() {
- return GlobeConfigModule
- .buidler()
- .baseurl(Api.APP_DOMAIN)
- .globeHttpHandler(new GlobeHttpHandler() {// 这里可以提供一个全局处理http响应结果的处理类,
- // 这里可以比客户端提前一步拿到服务器返回的结果,可以做一些操作,比如token超时,重新获取
- @Override
- public Response onHttpResultResponse(String httpResult, Interceptor.Chain chain, Response response) {
- //这里可以先客户端一步拿到每一次http请求的结果,可以解析成json,做一些操作,如检测到token过期后
- //重新请求token,并重新执行请求
- try {
- if (!TextUtils.isEmpty(httpResult)) {
- JSONArray array = new JSONArray(httpResult);
- JSONObject object = (JSONObject) array.get(0);
- String login = object.getString("login");
- String avatar_url = object.getString("avatar_url");
- Timber.tag(TAG).w("result ------>" + login + " || avatar_url------>" + avatar_url);
- }
- } catch (JSONException e) {
- e.printStackTrace();
- return response;
- }
- //这里如果发现token过期,可以先请求***的token,然后在拿新的token放入request里去重新请求
- //注意在这个回调之前已经调用过proceed,所以这里必须自己去建立网络请求,如使用okhttp使用新的request去请求
- // create a new request and modify it accordingly using the new token
- // Request newRequest = chain.request().newBuilder().header("token", newToken)
- // .build();
- // // retry the request
- //
- // response.body().close();
- //如果使用okhttp将新的请求,请求成功后,将返回的response return出去即可
- //如果不需要返回新的结果,则直接把response参数返回出去
- return response;
- }
- // 这里可以在请求服务器之前可以拿到request,做一些操作比如给request统一添加token或者header
- @Override
- public Request onHttpRequestBefore(Interceptor.Chain chain, Request request) {
- //如果需要再请求服务器之前做一些操作,则重新返回一个做过操作的的requeat如增加header,不做操作则返回request
- //return chain.request().newBuilder().header("token", tokenId)
- // .build();
- return request;
- }
- })
- .build();
- }
3.3 全局错误处理及发生错误时重新执行
如果需要使用Rxjava的全局错误处理,需通过GlobeConfigModule.responseErroListener()方法传入ResponseErroListener,并在每次使用Rxjava调用subscribe时,使用ErrorHandleSubscriber,并传入AppComponent中提供的RxErrorHandler,此Subscribe,默认已经实现OnError方法,如想自定义可以重写OnError方法
3.4 切换图片请求框架
本框架默认使用Glide实现图片加载功能,使用ImagerLoader提供统一的接口,ImagerLoader使用策略模式和建造者模式,可以动态切换图片框架(比如说切换成Picasso),并且加载图片时传入的参数也可以随意扩展(loadImage方法在需要扩展参数时,也不需要改动,全部通过Builder扩展,比如你想让内部的图片加载框架,清除缓存你只需要定义个boolean字段,内部根据这个字段if|else,其他操作同理)
- public class PicassoImageConfig extends ImageConfig{
- private PicassoImageConfig(Buidler builder) {
- this.url = builder.url;
- this.imageView = builder.imageView;
- this.placeholder = builder.placeholder;
- this.errorPic = builder.errorPic;
- }
- public static Buidler builder() {
- return new Buidler();
- }
- public static final class Buidler {
- private String url;
- private ImageView imageView;
- private int placeholder;
- protected int errorPic;
- private Buidler() {
- }
- public Buidler url(String url) {
- this.url = url;
- return this;
- }
- public Buidler placeholder(int placeholder) {
- this.placeholder = placeholder;
- return this;
- }
- public Buidler errorPic(int errorPic){
- this.errorPic = errorPic;
- return this;
- }
- public Buidler imagerView(ImageView imageView) {
- this.imageView = imageView;
- return this;
- }
- public PicassoImageConfig build() {
- if (url == null) throw new IllegalStateException("url is required");
- if (imageView == null) throw new IllegalStateException("imageview is required");
- return new PicassoImageConfig(this);
- }
- }
- }
3.***ndroidEventBus Tag
本框架使用AndroidEventBus实现事件总线,此框架使用注解标记目标方法,统一将Tag的常量写到EventBusTag接口中,便于管理,如果要在当前对象中使用AndroidEventBus请在需要使用的Activity,Fragment,Presenter中重写useEventBus(),返回true代表使用,默认返回true
3.6 AutoLayout组件
本框架使用AutoLayout框架,实现控件自适应,此框架要让组件自适应,必须让它的父控件,重新测量,和重写LayoutParams,而官方只默认提供了三个ViewGroup,AutoRelativeLayout,AutoLinearLayout,AutoFrameLayout实现了这些操作,为了方便开发者使用,本框架提供了一些常用的AutoLayout组件,在框架的widget包下的autolayout包中,在xml中引用即可使子控件自适应,并且还提供一个 Template(在***面)用于生成自适应所需要的的Auto系列View,如需要使ScrollView的子控件自适应,使用此Template输入ScrollView,即可生成AutoScrollView,在xml中引用即可
3.7 自定义PopupWindow
框架提供一个建造者模式的自定义PopupWindow组件CustomPopupWindow,自己实现布局后就可以直接使用这个实现PopupWindow,使用建造者模式,随意扩展自定义参数
3.8 快速实现RecycleView
本框架提供DefaultAdapter和BaseHolder基类快速实现Recycleview.
3.9 权限管理(适配Android6.0权限管理)
本框架使用RxPermissions用于权限管理(适配android6.0),并提供PermissionUtil工具类一行代码实现权限请求.适配Android6.0权限管理详解
- PermissionUtil.launchCamera(new RequestPermission() {
- @Override
- public void onRequestPermissionSuccess() {
- launchCapture();//请求权限成功后做一些操作
- }
- }, mRxPermissions, mRootView, mErrorHandler);
3.10 Gradle配置启动DeBug模式
在主项目(app)的build.gradle中配置是否开启打印Log或则是否使用LeakCanary,等调试工具
3.11 AppManager(管理所有的Activity)
AppManager用于管理所有的Activity,内部持有一个含有所有存活的Activity(未调用onDestroy)的List,和一个当前在最前端的Activity(未调用onPause),AppManager封装有多种方法,可以很方便的对它们进行操作,也可以在未持有AppManager的情况下,通过EventBus远程遥控它的所有方法,这样我们可以在整个app的任何地方对任何Activity进行全局操作,比如在app请求网络超时时让最前端的Activity显示连接超时的交互页面(这个逻辑不用写到当前请求的Activity里,可以在一个单例类里做全局的统一操作,因为可以随时通过AppManager拿到当前的Activity)
远程遥控通过EventBuspost Message实现,通过不同的what区分不同的方法和Handler同理,可以根据自己的需求适当的在AppManager中添加对应的方法
总结
如果你是构建一个全新的项目,直接将整个项目clone(或者下载)下来,直接将Demo当成主Module,再将项目包名改成自己的包名,Demo包含可以直接使用的包结构,一个主流的MVP+Dagger2+Retrofit+Rxjava框架就这样轻松的构建成功了,现在你参考Demo Mvp包下的UserActivity的格式,使用Template在对应包下自动生成MVP,Dagger2相关类,配合查阅Wiki文档慢慢掌握本框架,看再多文章不如早点在项目中使用它,在实践中学习总是最快的.
国内三大运营商到底给2G,3G,4G和5G使用了哪些频谱,分配了多少带宽?这直接决定...
引 言 机器学习/深度学习是一个广阔的研究领域,说来并不年轻,但又朝气蓬勃,似...
10月26日,在中国科协学会服务中心支持下,由北京软件和信息服务业协会(以下简称...
最近一款叫做 Remove ChinaApps的应用在印度迅速走红。 它并不是靠什么独特的页...
前段时间,中国电信发布了一个叫做 量子安全通话 的业务。 看到新闻的时候【主语...
我们都知道提前接种疫苗有益身体健康,但是许多人不清楚疫苗怎么预约,疫苗网上...
2019年,人脸识别进校园的案例密集出现,有一些学校引进了可以分析学生情绪的人...
我们今天所知的局域网(LAN)起步很简单,只需一台交换机将一台计算机连接到网络即...
引言 本文转载自网络,原文链接:https://www.toutiao.com/a6940140889599705611...
近日,市场研究公司Dell'Oro Group研究总监Baron Fung在一篇博客文章中就2021年...