搜索
您的当前位置:首页正文

看到如此多的MVP+Dagger2+Retrofit+Rxjav

来源:知库网
Logo

概述

MVPArms 是一个整合了大量主流开源项目的 Android MVP 快速搭建框架,其中包含 Dagger2 , Retrofit , Rxjava 以及 RxLifecycle , RxCacheRx 系三方库,并且提供 UI 自适应方案,本框架将它们结合起来,并全部使用 Dagger2
管理并提供给开发者使用,使用本框架开发你的项目就意味着你已经拥有一个 MVP + Dagger2 + Retrofit + Rxjava 项目

通知

  • 一键生成模板, 开发神器, 不看后悔! (MVPArms 的所有规范现已整合到以下两种模板中, 让您无需理解 Wiki 文档便可快速开启 MVPArms 的世界)

扩展项目, 了解一下:

特性

  • 通用框架, 适合所有类型的项目, 支持大型项目的开发, 兼容组件化开发, 可作为组件化的 Base

  • Base 基类(BaseActivity, BaseFragment, BaseApplication ...)

  • MVP 基类(IModel, IVIew, IPresenter ...)

  • 独创的 RxLifeCycle 应用方式, 可在不继承 RxLifeCycle 提供的 ActivityFragment 的情况下, 正常使用 RxLifeCycle 的所有功能, 且使用方式不变

  • 独创的建造者模式 Module (GlobalConfigModule), 可实现使用 Dagger2 向框架任意位置注入自定义参数, 可轻松扩展任意自定义参数

  • 全局使用 Dagger2 管理 (将所有模块使用 Dagger2 连接起来, 绝不是简单的使用)

  • 全局监听整个 App 所有 Activity 以及 Fragment 的生命周期 (包括三方库), 并可向其生命周期内插入任意代码

  • 全局监听 Http Request(请求参数, Headers ...), Response (服务器返回的结果, Headers, 耗时 ...)等信息(包括 Glide 的请求), 可解析 json 后根据状态码做相应的全局操作以及数据加密, Cookie 管理等操作

  • 全局管理所有 Activity (包括三方库的 Activity), 可实现在整个 App 任意位置, 退出所有 Activity, 以及拿到前台 Activity 做相应的操作(如您可以在 App 任何位置做弹出 Dialog 的操作)

  • 全局 Rxjava 错误处理, 错误后自动重试, 捕捉整个应用的所有错误

  • 全局 UI 自适应

  • 图片加载类 ImageLoader 使用策略模式和建造者模式, 轻松切换图片加载框架, 方便功能扩展

  • 网络请求日志打印封装(提供解析后的服务器的请求信息和服务器的响应信息, 按可自定义的任意格式输出打印日志, 内置一个漂亮的打印格式模板)

  • 框架内自有组件的缓存机制封装(框架内可缓存内容的组件都提供有接口供外部开发者自定义缓存机制)

  • 代码生成插件(MVPArms 全家桶一键生成所需要的所有类文件)

  • Demo 修改包名后就可以直接使用, 快速接入(老项目接入请按下面的步骤)

框架结构

Architecture

包结构

package

开发须知

  • 开发者需要具有一定的 Android 开发能力,以及自我解决问题的能力
  • 开发者必须有使用 Dagger2 , Rxjava , Retrofit 的经验,没使用过也必须了解,不然很难上手
  • 本框架为作者用业余时间维护,作者并没有义务为开发者做任何事,使用时或提问时请保持对作者以及维护者起码的 敬畏尊重

Libraries简介

<a name="1"></a>

1 开发准备

本框架建议直接使用 Gradle 远程依赖,框架已经提供了很多用于扩展的接口,足以满足日常需求,如非必须,请不要使用依赖 Module 的方式以及修改框架源码

<a name="1.1"></a>

1.1 导入框架

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>

1.2 引用config.build

本框架提供一个引用大量第三方库的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  }//Support-library 需要 Google 仓库
        google() //AndroidStudio v3.0 可以使用 google() 替代 maven { url  }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

<a name="1.2.1"></a>

1.2.1 使用config.build

因为在顶级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>

1.3 配置Build.gradle

<a name="1.3.1"></a>

1.3.1 依赖Dagger2

本框架全部使用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>

1.3.2 使用Lambda

本框架的 Demo , 默认使用 Lambda, 如你不想使用 Lambda 或使用 AndroidStudio v3.0 (兼容 java8), 请忽略以下的配置

  • 在项目根目录的 Build.gradle 中依赖 Lambda插件
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
        //lambda
        classpath 'me.tatarka:gradle-retrolambda:3.6.0'
    }
}

  • appBuild.gradle 中引入 Lambda 插件
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>

1.4 配置 AndroidManifest

<a name="1.4.1"></a>

1.4.1 添加权限

    <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>

1.4.2 指定 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>

1.4.3 配置 Autolayout

//配置设计图的宽高,配合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>

1.4.4 配置框架自定义属性

<!--arms配置-->
        <meta-data
            android:name="me.jessyan.mvparms.demo.app.GlobalConfiguration"
            android:value="ConfigModule"/>

<a name="1.5"></a>

1.5 混淆

<a name="1.6"></a>

1.6 版本更新

  • 如通过 Gradle 远程依赖本框架请忽略

如果你获得本框架的方式是通过clone或者下载:

  1. 直接可以通过命令行git pull origin master拉取最新的版本并自动合并
  2. 如果你修改了包名还得执行命令git rm --cache -r app/src/main/java/me/jessyan/mvparms,下次拉取时就不会拉取Demo的内容

如果你获得本框架的方式是通过fork到自己仓库后,clone或下载:

  1. git remote add arms 添加远程仓库,arms是远程仓库的代号,可自定义,以后都通过这个代号对远程仓库作操作
  2. git fetch arms拉取远程仓库最新的版本
  3. git merge arms/master --allow-unrelated-histories合并远程仓库到当前分支
  4. 后面如果本框架有更新就只用重复2,3步,--allow-unrelated-histories只用在第一次合并时添加
  5. 如果你修改了包名还得执行命令git rm --cache -r app/src/main/java/me/jessyan/mvparms,下次拉取时就不会拉取Demo的内容

<a name="2"></a>

2 快速开始

<a name="2.1"></a>

2.1 ConfigModule

  • 新建一个类继承自 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>

2.2 AppComponent

Application生命周期是和App是一样的,所以适合提供一些单例对象,本框架使用Dagger2管理,使用AppComponent来提供全局所有的单例对象,所以需要自定义一个Application继承自BaseApplication,即可在App的任何地方,通过BaseApplicationgetAppComponent()方法,拿到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>

2.3 RepositoryManager

RepositoryManager 用来管理网络请求层,以及数据缓存层,以后可能添加数据库请求层,专门提供给 Model 层做数据处理,在 v1.5 版本前是使用 ServiceManagerCacheManager 来管理,在v1.5版本之后使用 RepositoryManager 替代

  • 自行定义 Retrofit Service 如下,熟练 请忽略
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);
}
  • 自行定义 RxCache Provider 如下,熟练 请忽略
public interface CommonCache {

    @LifeCache(duration = 2, timeUnit = TimeUnit.MINUTES)
    Observable<Reply<List<User>>> getUsers(Observable<List<User>> oUsers, DynamicKey idLastUserQueried, EvictProvider evictProvider);

}

  • Model 中通过 RepositoryManager#obtainRetrofitService()RepositoryManager#obtainCacheService() 使用这些服务
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>

2.4 MVP实战

定义业务逻辑MVP,继承MVP各自的基类即可,这里可以稍微粗力度的定义MVP类,即无需每个FragmentActivity(每个页面)都定义不同的MVP类,可以按照相同的业务逻辑使用一组MVP

<a name="2.4.1"></a>

2.4.1 Contract

这里根据Google官方的MVP项目,可以在Contract中定义MVP的接口,便于管理,此框架无需定义Presenter接口,所以Contract只定义ModelView的接口

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>

2.4.2 View

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>

2.4.3 Model

@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>

2.4.4 Presenter

@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>

2.4.5 MVP Module

@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>

2.4.6 MVP Component

@ActivityScope
@Component(modules = UserModule.class,dependencies = AppComponent.class)
public interface UserComponent {
    void inject(UserActivity activity);
}

<a name="2.4.7"></a>

2.4.7 Dagger Scope

在上面的代码中 ActivityScope 大量出现在 ModuleComponent 中,Dagger2 使用 Scope 限制每个 Module 中提供的对象的生命周期, Dagger2 默认只提供一个 @Singleton Scope 即单例,本框架提供 @ActvityScope@FragmentScope ,如有其他需求请自行实现, ModuleComponent 定义相同的 ScopeModule 中提供的对象的生命周期会和 Component 的生命周期相绑定(即在 Component 生命周期内,如需多次使用到 Moudle 中提供的对象,但只会调用一次@Provide 注解的方法得到此对象)

<a name="2.4.8"></a>

2.4.8 MVP总结

  • 以后每个业务逻辑都重复构造这些类,只是换个名字而已,值得注意的是MVP刚开始用时确实会觉得平白无故多了很多类,非常繁琐麻烦,但是等页面代码逻辑越来多时,你会发现其中的好处,逻辑清晰,解耦,便于团队协作,测试容易,错误好定位,所以现在本框架提供解决这个痛点,让开发者更加愉快的使用本框架

<a name="3"></a>

3 功能使用

<a name="3.1"></a>

3.1 App全局配置信息(使用Dagger注入)

  • GlobalConfigModule 需依赖于 使用
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>

3.2 全局捕捉Http请求和响应

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>

3.3 全局错误处理及发生错误时重新执行

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");
                });
    }
}
  • Rxjava中使用
Observable
.just(1)
.retryWhen(new RetryWithDelay(3,2))//遇到错误时重试,第一个参数为重试几次,第二个参数为重试的间隔
.subscribe(new ErrorHandleSubscriber<Integer>(mErrorHandler) {
     @Override
     public void onNext(Integer Integer) {
 
     }
});

<a name="3.4"></a>

3.4 ImageLoader 如何扩展以及切换图片请求框架

本框架默认使用 Glide 实现图片加载功能,使用 ImageLoader 提供统一的接口, ImageLoader 使用策略模式和建造者模式,可以动态切换图片请求框架(比如说切换成 Picasso ),并且加载图片时传入的参数也可以随意扩展( loadImage 方法在需要扩展参数时,调用端也不需要改动,全部通过 Builder 扩展,比如你想让内部的图片加载框架,清除缓存你只需要定义个 boolean 字段,内部根据这个字段 if|else,其他操作同理,当需要切换图片请求框架或图片请求框架升级后变更了 Api 时,这里可以将影响范围降到最低,所以封装 ImageLoader 是为了屏蔽这个风险)

  • ! 本框架默认提供了 GlideImageLoaderStrategyImageConfigImpl 简单实现了图片加载逻辑,方便快速使用,但开发中难免会遇到复杂的使用场景,所以本框架推荐即使不切换图片请求框架继续使用 Glide ,也请按照下面的方法,自行实现图片加载策略,因为默认实现的 GlideImageLoaderStrategy 是直接打包进框架的,如果是远程依赖,当遇到满足不了需求的情况,你将不能扩展里面的逻辑

  • 使用 ImageLoader 必须传入一个实现了 BaseImageLoaderStrategy 接口的图片加载实现类从而实现动态切换,所以首先要实现BaseImageLoaderStrategy ,实现时必须指定一个继承自 ImageConfig 的实现类,使用建造者模式,可以储存一些信息,比如 URL ,
    ImageView , Placeholder 等,可以不断的扩展,供图片加载框架使用

public class PicassoImageLoaderStrategy implements BaseImageLoaderStrategy<PicassoImageConfig> {
     @Override
    public void loadImage(Context ctx, PicassoImageConfig config) {
                        Picasso.with(ctx)
                .load(config.getUrl())
                .into(config.getImageView());
    }
}
  • 实现 ImageCofig 使用建造者模式(创建新的 PicassoImageConfig 适用于新项目,如果想重构之前的项目,使用其他图片加载框架,为了避免影响之前的代码,请继续使用默认提供的 ImageConfigImpl 或者你之前自行实现的 ImageConfig ,并可继续扩展里面的属性)
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);
        }
    }
}
  • App 刚刚启动初始化时通过 传入上面扩展的 PicassoImageLoaderStrategy ,也可以在 App 运行期间通过 AppComponent 拿到 ImageLoader 对象后, setLoadImgStrategy(new PicassoImageLoaderStrategy) 替换之前的实现(默认使用 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>

3.5 AndroidEventBus Tag

<a name="3.6"></a>

3.6 AutoLayout组件

<a name="3.7"></a>

3.7 自定义PopupWindow

框架提供一个使用建造者模式的自定义 PopupWindow 组件: CustomPopupWindow,自己实现布局后就可以直接使用这个类实现 PopupWindow ,使用建造者模式,随意扩展自定义参数

<a name="3.8"></a>

3.8 快速实现RecycleView

本框架提供DefaultAdapterBaseHolder基类快速实现Recycleview.

  • BaseHolder默认初始化了ButterKnifeAutoLayout,继承后不仅可以直接注入View,布局还可以自适应屏幕
  • RecycleView默认是不提供Item的点击事件的,使用DefaultAdapter调用setOnItemClickListener可以实现Item的点击事件

<a name="3.9"></a>

3.9 权限管理(适配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>

3.10 Gradle配置启动DeBug模式

在主项目(app)的build.gradle中配置是否开启打印Log或则是否使用LeakCanary,等调试工具

  • 在build.gradle中配置
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'
        }
    }
    
  • 在代码中使用(比如在 App 初始化时做一些初始化的设置)
@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>

3.11 AppManager(管理所有的Activity)

AppManager用于管理所有的Activity,内部持有一个含有所有存活的Activity(未调用onDestroy)的List,和一个当前在最前端的Activity(未调用onPause),AppManager封装有多种方法,可以很方便的对它们进行操作,也可以在未持有AppManager的情况下,通过EventBus远程遥控它的所有方法,这样我们可以在整个app的任何地方对任何Activity进行全局操作,比如在app请求网络超时时让最前端的Activity显示连接超时的交互页面(这个逻辑不用写到当前请求的Activity里,可以在一个单例类里做全局的统一操作,因为可以随时通过AppManager拿到当前的Activity)

  • 远程遥控通过 AppManager.post(Message) 实现,通过 Message 对象中不同的 what 区分不同的方法和 Handler 同理,并且可以根据自己的需求不断扩展

  • 如何扩展自己的需求? 在 ConfigModule#injectAppLifecycle(Context, List) 中通过 AppLifecycles#onCreate(Application),在 App 初始化时,配置满足自己需求的 HandleListener

   @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;
                        }
                    }
                });
            }
        });
    }
  • 如何使用? 通过 AppManager.post(Message) 发送不同 whatMessage 对象即可
   Message msg = new Message();
   msg.what = 0;
   AppManager.post(msg); //like EventBus

<a name="3.12"></a>

3.12 AppDelegate(代理 Application 生命周期)

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>

3.13 ActivityDelegate 和 FragmentDelegate

<a name="3.14"></a>

3.14 框架中 RxLifecycle 的使用

Top