android主流框架的特性是什么发表时间:2020-05-03 00:44 特性
框架结构 包结构 开发须知
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); } } }
|