原文地址: http://www.jianshu.com/p/4bbecd0bb027
Logo
MVPArms 是一个整合了大量主流开源项目的 Android MVP 快速搭建框架,其中包含 Dagger2 , Retrofit , Rxjava 以及 RxLifecycle , RxCache 等 Rx 系三方库,并且提供 UI 自适应方案,本框架将它们结合起来,并全部使用 Dagger2
管理并提供给开发者使用,使用本框架开发你的项目就意味着你已经拥有一个 MVP + Dagger2 + Retrofit + Rxjava 项目
MVPArt 是一个新的 MVP 架构,适合中小型项目,旨在解决传统 MVP 类和接口太多,并且 Presenter 和 View 通过接口通信过于繁琐,重用 Presenter 代价太大等问题
- [一键生成 app Module (使用此 **Module 级** 模板可直接生成 **Wiki** 文档第一章 **开发准备** 的所有内容, 快速并且零错误)](https://github.com/JessYanCoding/MVPArms-Module-Template)
- [一键生成 MVP, Dagger2 相关类 (使用此 **页面级** 模板可直接生成 **Wiki** 文档第二章 **快速开始** 的所有内容, 快速并且零错误)](https://github.com/JessYanCoding/MVPArmsTemplate)
扩展项目, 了解一下:
Architecture
package
Mvp
Google官方出品的Mvp
架构项目,含有多个不同的架构分支(此为Dagger分支).Dagger2
Google根据Square的Dagger1出品的依赖注入框架,通过Apt编译时生成代码,性能优于使用运行时反射技术的依赖注入框架.Rxjava
提供优雅的响应式Api解决异步请求以及事件处理.RxAndroid
为Android提供响应式Api.Rxlifecycle
在Android上使用rxjava都知道的一个坑,就是生命周期的解除订阅,这个框架通过绑定activity和fragment的生命周期完美解决.RxCache
是使用注解为Retrofit加入二级缓存(内存,磁盘)的缓存库.RxErroHandler
是 Rxjava
的错误处理库,可在出现错误后重试.RxPermissions
用于处理Android运行时权限的响应式库.Retrofit
Square出品的网络请求库,极大的减少了http请求的代码和步骤.Okhttp
同样Square出品,不多介绍,做Android都应该知道.Autolayout
鸿洋大神的Android全尺寸适配框架.Gson
Google官方的Json Convert框架.Butterknife
JakeWharton大神出品的view注入框架.Androideventbus
一个轻量级使用注解的Eventbus.Timber
JakeWharton大神出品Log框架容器,内部代码极少,但是思想非常不错.Glide
此库为本框架默认封装图片加载库,可参照着例子更改为其他的库,Api和Picasso
差不多,缓存机制比Picasso
复杂,速度快,适合处理大型图片流,支持 gif 图片,Fresco
太大了!在5.0以下优势很大,5.0以上系统默认使用的内存管理和Fresco
类似.LeakCanary
Square出品的专门用来检测Android
和Java
的内存泄漏,通过通知栏提示内存泄漏信息.<a name="1"></a>
本框架建议直接使用 Gradle 远程依赖,框架已经提供了很多用于扩展的接口,足以满足日常需求,如非必须,请不要使用依赖 Module 的方式以及修改框架源码
<a name="1.1"></a>
compile 'me.jessyan:arms:2.4.1' //使用Rxjava2, 已兼容 AndroidStudio v3.0 以及 Gradle Plugin v3.0
--------------------------------------------
compile 'me.jessyan:arms:1.6.3' //使用Rxjava1 (请注意 Rxjava1 的版本以后不再维护,建议使用 Rxjava2 版本)
<a name="1.2"></a>
本框架提供一个引用大量第三方库的config.gradle文件,用于第三方库版本管理,将config.gradle复制进根目录,并在项目的顶级build.gradle中引用它
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: "config.gradle" //这里表示引用config.gradle文件
buildscript {
repositories {
jcenter()
}
dependencies {
//classpath 'com.android.tools.build:gradle:2.3.3'//Android Studio v2.3.3
classpath 'com.android.tools.build:gradle:3.0.0' //Android Studio v3.0
}
}
allprojects {
repositories {
jcenter()
maven { url "https://jitpack.io" }//RxCache 需要 jitpack 仓库
maven { url "https://maven.google.com" }//Support-library 需要 Google 仓库
google() //AndroidStudio v3.0 可以使用 google() 替代 maven { url "https://maven.google.com" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
<a name="1.2.1"></a>
因为在顶级build.gradle中引用了它,所以在整个项目的所有build.gradle中都可以使用rootProject.xxx来使用它里面的内容
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile rootProject.ext.dependencies["junit"]
compile rootProject.ext.dependencies["support-v4"]
compile rootProject.ext.dependencies["gson"]
compile rootProject.ext.dependencies["appcompat-v7"]
compile rootProject.ext.dependencies["cardview-v7"]
compile rootProject.ext.dependencies["autolayout"]
compile rootProject.ext.dependencies["butterknife"]
compile rootProject.ext.dependencies["androideventbus"]
}
也可以使用它来管理一些项目的信息,这样有多个module也可以直接使用同一个信息
android {
compileSdkVersion rootProject.ext.android["compileSdkVersion"]
buildToolsVersion rootProject.ext.android["buildToolsVersion"]
useLibrary 'org.apache.http.legacy'
defaultConfig {
minSdkVersion rootProject.ext.android["minSdkVersion"]
targetSdkVersion rootProject.ext.android["targetSdkVersion"]
versionCode rootProject.ext.android["versionCode"]
versionName rootProject.ext.android["versionName"]
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
<a name="1.3"></a>
<a name="1.3.1"></a>
本框架全部使用Dagger2管理,所以必须依赖Dagger2,找到app的build.gradle,加入如下代码
apply plugin: 'com.android.application'
buildscript {
repositories {
jcenter()
}
}
dependencies {
annotationProcessor rootProject.ext.dependencies["butterknife-compiler"] //Butterknife 插件,很多人因为没加这个而报错,切记!!!
annotationProcessor rootProject.ext.dependencies["dagger2-compiler"]//依赖插件, annotationProcessor 是 AndroidStudio 自带并用来替换 APT
}
<a name="1.3.2"></a>
本框架的 Demo , 默认使用
Lambda
, 如你不想使用Lambda
或使用 AndroidStudio v3.0 (兼容 java8), 请忽略以下的配置
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
//lambda
classpath 'me.tatarka:gradle-retrolambda:3.6.0'
}
}
apply plugin: 'me.tatarka.retrolambda'
android {
compileOptions {//就算你使用 AndroidStuido v3.0, 也需要配置以下参数
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}
<a name="1.4"></a>
<a name="1.4.1"></a>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<a name="1.4.2"></a>
本框架想要正常运行需要使用框架提供的 BaseApplication ,当然你也可以自定义一个 Application 继承于它,也可以不用继承,直接将 BaseApplication 的代码复制到你自定义的 Application 里(里面只有几行代码),但是我并不推荐你使用后面的两种方式,因为本框架已经向开发者提供了 ConfigModule#injectAppLifecycle 方法,可以在运行时动态的向 BaseApplication 中插入任意代码,这样即使你不需要自定义 Application ,也可以做到初始化自己的业务
<application
android:name="com.jess.arms.base.BaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
</application>
<a name="1.4.3"></a>
使用 Autolayout 自适应框架必须配置 Meta 属性,即设计图的宽高,详情参考 Autolayout ,本框架并不强制你使用 AutoLayout,如果你不想使用 AutoLayout,就不要配置下面的 meta-data
//配置设计图的宽高,配合AutoLauout控件使用,在设计图尺寸以外的其它尺寸手机上,也能达到和设计图一样的效果
<meta-data
android:name="design_width"
android:value="1080"/>
<meta-data
android:name="design_height"
android:value="1920"/>
<a name="1.4.4"></a>
本框架使用和
Glide
相同的方式来配置自定义属性,需要在AndroidManifest
中声明它,详情
<!--arms配置-->
<meta-data
android:name="me.jessyan.mvparms.demo.app.GlobalConfiguration"
android:value="ConfigModule"/>
<a name="1.5"></a>
由于本框架依赖大量三方库,所以已经在 arms Module
下的 proguard-rules.pro 中提供了本框架所依赖三方库的所有规则,如果想使用它,请复制它替换 app Module
中的 proguard-rules.pro (Demo 并不能直接使用这个 proguard-rules.pro 进行混淆),混淆前务必注意将 Java Bean ,自定义组件 等必需的规则添加进 proguard-rules.pro
<a name="1.6"></a>
如果你获得本框架的方式是通过clone或者下载:
git pull origin master
拉取最新的版本并自动合并git rm --cache -r app/src/main/java/me/jessyan/mvparms
,下次拉取时就不会拉取Demo的内容如果你获得本框架的方式是通过fork到自己仓库后,clone或下载:
git remote add arms https://github.com/JessYanCoding/MVPArms.git
添加远程仓库,arms是远程仓库的代号,可自定义,以后都通过这个代号对远程仓库作操作git fetch arms
拉取远程仓库最新的版本git merge arms/master --allow-unrelated-histories
合并远程仓库到当前分支--allow-unrelated-histories
只用在第一次合并时添加git rm --cache -r app/src/main/java/me/jessyan/mvparms
,下次拉取时就不会拉取Demo的内容<a name="2"></a>
<a name="2.1"></a>
ConfigModule 用来给框架配置各种自定义属性和功能,配合 GlobalConfigModule 使用非常强大
ConfigModule
,并在 AndroidManifest 中声明public class GlobalConfiguration implements ConfigModule {
@Override
public void applyOptions(Context context, GlobalConfigModule.Builder builder) {
//使用builder可以为框架配置一些配置信息
builder.baseurl(Api.APP_DOMAIN)
.cacheFile(New File("cache"));
}
@Override
public void injectAppLifecycle(Context context, List<AppLifecycles> lifecycles) {
//向Application的生命周期中注入一些自定义逻辑
}
@Override
public void injectActivityLifecycle(Context context, List<Application.ActivityLifecycleCallbacks> lifecycles) {
//向Activity的生命周期中注入一些自定义逻辑
}
@Override
public void injectFragmentLifecycle(Context context, List<FragmentManager.FragmentLifecycleCallbacks> lifecycles) {
//向Fragment的生命周期中注入一些自定义逻辑
}
}
<application>
<!--arms配置-->
<meta-data
android:name="me.jessyan.mvparms.demo.app.GlobalConfiguration"
android:value="ConfigModule"/>
</application>
<a name="2.2"></a>
Application生命周期是和App是一样的,所以适合提供一些单例对象,本框架使用
Dagger2
管理,使用AppComponent来提供全局所有的单例对象,所以需要自定义一个Application继承自BaseApplication,即可在App的任何地方,通过BaseApplication的getAppComponent()方法,拿到AppComponent里面声明的所有单例对象
@Singleton
@Component(modules = {AppModule.class, ClientModule.class, GlobalConfigModule.class})
public interface AppComponent {
Application Application();
//用于管理网络请求层,以及数据缓存层
IRepositoryManager repositoryManager();
//Rxjava错误处理管理类
RxErrorHandler rxErrorHandler();
OkHttpClient okHttpClient();
//图片管理器,用于加载图片的管理类,默认使用glide,使用策略模式,可替换框架
ImageLoader imageLoader();
//gson
Gson gson();
//缓存文件根目录(RxCache和Glide的的缓存都已经作为子文件夹在这个目录里),应该将所有缓存放到这个根目录里,便于管理和清理,可在GlobeConfigModule里配置
File cacheFile();
//用于管理所有activity
AppManager appManager();
void inject(AppDelegate delegate);
}
<a name="2.3"></a>
RepositoryManager 用来管理网络请求层,以及数据缓存层,以后可能添加数据库请求层,专门提供给 Model 层做数据处理,在
v1.5
版本前是使用 ServiceManager 和 CacheManager 来管理,在v1.5
版本之后使用 RepositoryManager 替代
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);
}
public interface CommonCache {
@LifeCache(duration = 2, timeUnit = TimeUnit.MINUTES)
Observable<Reply<List<User>>> getUsers(Observable<List<User>> oUsers, DynamicKey idLastUserQueried, EvictProvider evictProvider);
}
public Observable<List<User>> getUsers(int lastIdQueried, boolean update) {
Observable<List<User>> users = mRepositoryManager.obtainRetrofitService(UserService.class)
.getUsers(lastIdQueried, USERS_PER_PAGE);
//使用rxcache缓存,上拉刷新则不读取缓存,加载更多读取缓存
return mRepositoryManager.obtainCacheService(CommonCache.class)
.getUsers(users
, new DynamicKey(lastIdQueried)
, new EvictDynamicKey(update))
.flatMap(new Func1<Reply<List<User>>, Observable<List<User>>>() {
@Override
public Observable<List<User>> call(Reply<List<User>> listReply) {
return Observable.just(listReply.getData());
}
});
}
<a name="2.4"></a>
定义业务逻辑MVP,继承MVP各自的基类即可,这里可以稍微粗力度的定义MVP类,即无需每个Fragment和Activity(每个页面)都定义不同的MVP类,可以按照相同的业务逻辑使用一组MVP类
<a name="2.4.1"></a>
这里根据
public interface UserContract {
//对于经常使用的关于UI的方法可以定义到IView中,如显示隐藏进度条,和显示文字消息
interface View extends IView {
void setAdapter(DefaultAdapter adapter);
void startLoadMore();
void endLoadMore();
}
//Model层定义接口,外部只需关心Model返回的数据,无需关心内部细节,即是否使用缓存
interface Model extends IModel{
Observable<List<User>> getUsers(int lastIdQueried, boolean update);
}
}
<a name="2.4.2"></a>
一般让 Activity 或 Fragment 实现 Contract 中定义的 View 接口,供 Presenter 调用对应方法操作 UI , BaseActivity 默认注入 Presenter ,如想使用 Presenter ,必须指定 Presenter 的范型(虽然只可以指定一个范型,但是可以自行生成并持有多个 Presenter ,达到重用的目的),和实现setupActivityComponent 来提供 Presenter 需要的 Component 和 Module(如这个页面逻辑简单并不需要 Presenter ,那就不指定范型,也不实现方法)
public class UserActivity extends BaseActivity<UserPresenter> implements UserContract.View {
@Override
protected void setupActivityComponent(AppComponent appComponent) {
DaggerUserComponent
.builder()
.appComponent(appComponent)
.userModule(new UserModule(this))
.build()
.inject(this);
}
@Override
public int initView(Bundle savedInstanceState) {
return R.layout.activity_user;
}
@Override
protected void initData() {
}
}
<a name="2.4.3"></a>
Model 实现 Contract 的 Model 接口,并且继承 BaseModel ,然后通过 IRepositoryManager 拿到需要的 Service 和 Cache 为 Presenter 提供需要的数据(是否使用缓存请自行选择)
@ActivityScope
public class UserModel extends BaseModel implements UserContract.Model{
@Inject
public UserModel(IRepositoryManager repositoryManager) {
super(repositoryManager);
}
@Override
public Observable<List<User>> getUsers(int lastIdQueried, boolean update) {
mRepositoryManager.obtainRetrofitService(UserService.class)
.getUsers();
}
}
<a name="2.4.4"></a>
Presenter在MVP中的大部分的作用为通过从Model层接口获取数据,在调用View层接口显示数据,首先实现BasePresenter,指定Model和View的范型,注意一定要指定Contract中定义的接口,Presenter需要的Model和View,都使用Dagger2注入,这样即解藕又方便测试,怎么注入?
@ActivityScope
public class UserPresenter extends BasePresenter<UserContract.Model, UserContract.View> {
@Inject
public UserPresenter(UserContract.Model model, UserContract.View rootView) {
super(model, rootView);
}
//这里定义业务方法,相应用户的交互
public void requestUsers(final boolean pullToRefresh) {
}
}
<a name="2.4.5"></a>
这里的Module提供当前业务逻辑对应的View和Model接口(Contract中定义的接口)的实现类,Model需要AppComponent中提供的RepositoryManager来实现网络请求和缓存,所以需要通过Component依赖AppComponent来拿到这个对象
@Module
public class UserModule {
private UserContract.View view;
//构建UserModule时,将View的实现类传进来,这样就可以提供View的实现类给presenter
public UserModule(UserContract.View view) {
this.view = view;
}
@ActivityScope
@Provides
UserContract.View provideUserView(){
return this.view;
}
@ActivityScope
@Provides
UserContract.Model provideUserModel(UserModel model){
return model;
}
}
<a name="2.4.6"></a>
这里需要注意的是此Component必须依赖AppComponent,这样才能提供Model需要的RepositoryManager,提供inject()方法就能将Module及AppComponent中提供的对象注入到对应的类中,inject()中的参数不能是接口,怎么注入?
@ActivityScope
@Component(modules = UserModule.class,dependencies = AppComponent.class)
public interface UserComponent {
void inject(UserActivity activity);
}
<a name="2.4.7"></a>
在上面的代码中 ActivityScope 大量出现在 Module 和 Component 中,Dagger2 使用 Scope 限制每个 Module 中提供的对象的生命周期, Dagger2 默认只提供一个
@Singleton
Scope 即单例,本框架提供 @ActvityScope 和 @FragmentScope ,如有其他需求请自行实现, Module 和 Component 定义相同的 Scope 后 Module 中提供的对象的生命周期会和 Component 的生命周期相绑定(即在 Component 生命周期内,如需多次使用到 Moudle 中提供的对象,但只会调用一次@Provide 注解的方法得到此对象)
<a name="2.4.8"></a>
<a name="3"></a>
<a name="3.1"></a>
GlobalConfigModule使用建造者模式将App的全局配置信息封装进Module(使用Dagger注入到需要配置信息的地方),可以配置CacheFile,Interceptor等,甚至于Retrofit,Okhttp,RxCache都可以自定义配置,因为使用的是建造者模式所以如你有其他配置信息需要使用
Dagger
注入,直接就可以添加进Builder并且不会影响到其他地方
public class GlobalConfiguration implements ConfigModule {
@Override
public void applyOptions(Context context, GlobalConfigModule.Builder builder) {
//使用builder可以为框架配置一些配置信息
builder.baseurl(Api.APP_DOMAIN)
.gsonConfiguration((context12, gsonBuilder) -> {//这里可以自己自定义配置Gson的参数
gsonBuilder
.serializeNulls()//支持序列化null的参数
.enableComplexMapKeySerialization();//支持将序列化key为object的map,默认只能序列化key为string的map
})
.retrofitConfiguration((context1, retrofitBuilder) -> {//这里可以自己自定义配置Retrofit的参数,甚至你可以替换系统配置好的okhttp对象
// retrofitBuilder.addConverterFactory(FastJsonConverterFactory.create());//比如使用fastjson替代gson
})
.okhttpConfiguration((context1, okhttpBuilder) -> {//这里可以自己自定义配置Okhttp的参数
okhttpBuilder.writeTimeout(10, TimeUnit.SECONDS);
}).rxCacheConfiguration((context1, rxCacheBuilder) -> {//这里可以自己自定义配置RxCache的参数
rxCacheBuilder.useExpiredDataIfLoaderNotAvailable(true);
}
}
<a name="3.2"></a>
在全局配置类中通过GlobalConfigModule.Builder.globalHttpHandler()方法传入GlobalHttpHandler
public class GlobalConfiguration implements ConfigModule {
@Override
public void applyOptions(Context context, GlobalConfigModule.Builder builder) {
builder.globalHttpHandler(new GlobalHttpHandler() {// 这里可以提供一个全局处理Http请求和响应结果的处理类,
// 这里可以比客户端提前一步拿到服务器返回的结果,可以做一些操作,比如token超时,重新获取
@Override
public Response onHttpResultResponse(String httpResult, Interceptor.Chain chain, Response response) {
/* 这里可以先客户端一步拿到每一次http请求的结果,可以解析成json,做一些操作,如检测到token过期后
重新请求token,并重新执行请求 */
try {
if (!TextUtils.isEmpty(httpResult) && RequestInterceptor.isJson(response.body())) {
JSONArray array = new JSONArray(httpResult);
JSONObject object = (JSONObject) array.get(0);
String login = object.getString("login");
String avatar_url = object.getString("avatar_url");
Timber.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;
}
});
}
}
<a name="3.3"></a>
如果需要使用Rxjava的全局错误处理,需在全局配置类中通过GlobalConfigModule.Builder.responseErroListener()方法传入ResponseErroListener,并在每次使用Rxjava调用subscribe时,使用ErrorHandleSubscriber,并传入AppComponent中提供的RxErrorHandler,此Subscribe,默认已经实现OnError方法,如想自定义可以重写OnError方法
public class GlobalConfiguration implements ConfigModule {
@Override
public void applyOptions(Context context, GlobalConfigModule.Builder builder) {
builder.responseErrorListener((context1, e) -> {
/* 用来提供处理所有错误的监听
rxjava必要要使用ErrorHandleSubscriber(默认实现Subscriber的onError方法),此监听才生效 */
Timber.w("------------>" + e.getMessage());
ArmsUtils.SnackbarText("net error");
});
}
}
Observable
.just(1)
.retryWhen(new RetryWithDelay(3,2))//遇到错误时重试,第一个参数为重试几次,第二个参数为重试的间隔
.subscribe(new ErrorHandleSubscriber<Integer>(mErrorHandler) {
@Override
public void onNext(Integer Integer) {
}
});
<a name="3.4"></a>
本框架默认使用
Glide
实现图片加载功能,使用 ImageLoader 提供统一的接口, ImageLoader 使用策略模式和建造者模式,可以动态切换图片请求框架(比如说切换成Picasso
),并且加载图片时传入的参数也可以随意扩展( loadImage 方法在需要扩展参数时,调用端也不需要改动,全部通过 Builder 扩展,比如你想让内部的图片加载框架,清除缓存你只需要定义个 boolean 字段,内部根据这个字段 if|else,其他操作同理,当需要切换图片请求框架或图片请求框架升级后变更了 Api 时,这里可以将影响范围降到最低,所以封装 ImageLoader 是为了屏蔽这个风险)
public class PicassoImageLoaderStrategy implements BaseImageLoaderStrategy<PicassoImageConfig> {
@Override
public void loadImage(Context ctx, PicassoImageConfig config) {
Picasso.with(ctx)
.load(config.getUrl())
.into(config.getImageView());
}
}
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);
}
}
}
Glide
)方法一: 通过GlobalConfigModule传入
public class GlobalConfiguration implements ConfigModule {
@Override
public void applyOptions(Context context, GlobalConfigModule.Builder builder) {
builder.imageLoaderStrategy(new PicassoImageLoaderStrategy);
}
}
方法二: 拿到AppComponent中的 ImageLoader,通过方法传入
mApplication
.getAppComponent()
.imageLoader()
.setLoadImgStrategy(new PicassoImageLoaderStrategy());
使用方法
mApplication
.getAppComponent()
.imageLoader()
.loadImage(mApplication, PicassoImageConfig
.builder()
.url(data.getAvatarUrl())
.imagerView(mAvater)
.build());
<a name="3.5"></a>
本框架使用 AndroidEventBus 实现事件总线,此框架使用注解标记目标方法,统一将 Tag 的常量写到 EventBusTag 接口中,便于管理,如果要在当前对象中使用 AndroidEventBus 请在需要使用的 Activity , Fragment , Presenter 中重写 useEventBus() ,返回 true 代表使用,默认返回 true ,为什么 MVPArms 使用 AndroidEventBus 而不是 greenrobot 的 EventBus ,请看这里 我的回答
<a name="3.6"></a>
本框架使用AutoLayout框架,实现控件自适应,此框架要让组件自适应,必须让它的父控件,重新测量,和重写LayoutParams,而官方只默认提供了三个ViewGroup,AutoRelativeLayout,AutoLinearLayout,AutoFrameLayout实现了这些操作,为了方便开发者使用,本框架提供了一些常用的AutoLayout组件,在框架的widget包下的autolayout包中,在
xml
中引用即可使子控件自适应,并且还提供一个 Template(在最后面)用于生成自适应所需要的的Auto系列View,如需要使ScrollView的子控件自适应,使用此Template输入ScrollView,即可生成AutoScrollView,在xml
中引用即可
<a name="3.7"></a>
框架提供一个使用建造者模式的自定义 PopupWindow 组件: CustomPopupWindow,自己实现布局后就可以直接使用这个类实现 PopupWindow ,使用建造者模式,随意扩展自定义参数
<a name="3.8"></a>
本框架提供DefaultAdapter和BaseHolder基类快速实现Recycleview.
<a name="3.9"></a>
本框架使用RxPermissions用于权限管理(适配android6.0),并提供PermissionUtil工具类一行代码实现权限请求.适配Android6.0权限管理详解
PermissionUtil.launchCamera(new PermissionUtil.RequestPermission() {
@Override
public void onRequestPermissionSuccess() {
launchCapture();//请求权限成功后做一些操作
}
@Override
public void onRequestPermissionFailure() {
mRootView.showMessage("Request permissons failure");
}
}, mRxPermissions, mErrorHandler);
<a name="3.10"></a>
在主项目(app)的build.gradle中配置是否开启打印Log或则是否使用
LeakCanary
,等调试工具
android {
buildTypes {
debug {
//这两个变量是自定义的,自己也可以自定义字段,他会默认配置到BuildConfig中,app中可以根据这些字段执行一些操作
buildConfigField "boolean", "LOG_DEBUG", "true"
buildConfigField "boolean", "USE_CANARY", "true"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
buildConfigField "boolean", "LOG_DEBUG", "false"
buildConfigField "boolean", "USE_CANARY", "false"
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
@Override
public void injectAppLifecycle(Context context, List<AppLifecycles> lifecycles) {
lifecycles.add(new AppLifecycles() {
@Override
public void onCreate(Application application) {
if (BuildConfig.LOG_DEBUG) {//Timber日志打印
Timber.plant(new Timber.DebugTree());
}
if (BuildConfig.USE_CANARY) {//leakCanary内存泄露检查
LeakCanary.install(this);
}
}
});
}
<a name="3.11"></a>
AppManager用于管理所有的Activity,内部持有一个含有所有存活的Activity(未调用onDestroy)的List,和一个当前在最前端的Activity(未调用onPause),AppManager封装有多种方法,可以很方便的对它们进行操作,也可以在未持有AppManager的情况下,通过
EventBus
远程遥控它的所有方法,这样我们可以在整个app的任何地方对任何Activity进行全局操作,比如在app请求网络超时时让最前端的Activity显示连接超时的交互页面(这个逻辑不用写到当前请求的Activity里,可以在一个单例类里做全局的统一操作,因为可以随时通过AppManager拿到当前的Activity)
@Override
public void injectAppLifecycle(Context context, List<AppLifecycles> lifecycles) {
lifecycles.add(new AppLifecycles() {
@Override
public void onCreate(Application application) {
ArmsUtils.obtainAppComponentFromContext(application).appManager().setHandleListener(new AppManager.HandleListener() {
@Override
public void handleMessage(AppManager appManager, Message message) {
switch (message.what) {
//case 0:
//do something ...
// break;
}
}
});
}
});
}
Message msg = new Message();
msg.what = 0;
AppManager.post(msg); //like EventBus
<a name="3.12"></a>
AppDelegate 可以代理 Application的生命周期,在对应的生命周期,执行对应的逻辑,因为 Java 只能单继承,所以当遇到某些三方库需要继承于它的 Application 的时候,就只有自定义 Application 并继承于三方库的 Application,这时就不用再继承 BaseApplication,只用在自定义 Application 中对应的生命周期调用 AppDelegate 对应的方法(Application 一定要实现 APP 接口),框架就能照常运行,并且 Application 中对应的生命周期可使用以下方式扩展
public class GlobalConfiguration implements ConfigModule {
@Override
public void injectAppLifecycle(Context context, List<AppLifecycles> lifecycles) {
// AppLifecycles 的所有方法都会在基类Application对应的生命周期中被调用,所以在对应的方法中可以扩展一些自己需要的逻辑
lifecycles.add(new AppLifecycles() {
private RefWatcher mRefWatcher;//leakCanary观察器
@Override
public void onCreate(Application application) {
if (BuildConfig.LOG_DEBUG) {//Timber日志打印
Timber.plant(new Timber.DebugTree());
}
//leakCanary内存泄露检查
this.mRefWatcher = BuildConfig.USE_CANARY ? LeakCanary.install(application) : RefWatcher.DISABLED;
}
@Override
public void onTerminate(Application application) {
this.mRefWatcher = null;
}
});
}
}
<a name="3.13"></a>
这里实现的思想太牛逼,所以请看我写的 文章
<a name="3.14"></a>