程序员

看到如此多的MVP+Dagger2+Retrofit+Rxjava项目,轻松拿star,心动了吗?

原文地址: http://www.jianshu.com/p/4bbecd0bb027

MVPArms

概述

MVPArms是一个整合了大量主流开源项目的Android Mvp快速搭建框架,其中包含Dagger2``Retrofit,Rxjava以及Rxbinding,RxCacheRx系三方库,并且提供UI自适应方案,本框架将它们结合起来,并全部使用Dagger2管理并提供给开发者使用,使用本框架开发你的项目就意味着你已经拥有一个MVP+Dagger2+Retrofit+Rxjava项目,发布到github上可以非常轻松的拿star(在简历中展露头脚),我提供一切迭代和维护成本你只需要在Readme里面注明框架出处,还不赶快行动?

特性

  • 自动生成MVP,Dagger2相关类
  • 通用框架,适合所有类型的项目,支持大型项目的开发,Demo的包结构直接可以拿来用
  • 全部使用Dagger2管理(将所有模块使用Dagger连接起来,绝不是简单的使用)
  • 大量使用Rxjava
  • 修改包名后就可以直接使用,快速接入(老项目接入请按下面的步骤)
  • 全部UI自适应
  • 图片加载类ImageLoader使用策略模式和建造者模式,轻松切换图片加载框架和功能扩展
  • Model层提供Retrofit API和RxCache,是否使用缓存自行选择
  • 全局http Request(请求参数,headers) Response(服务器返回的结果,headers,耗时)信息监听,可解析json后根据状态码做相应的全局操作
  • 全局Rxjava错误处理,错误后自动重试,捕捉整个应用的所有错误

包结构

开发须知

  • 开发者需要具有一定的Android开发能力
  • 开发者必须有使用Dagger2,Rxjava,Retrofit的经验,没使用过也必须了解,不然很难使用

Libraries简介

  1. MvpGoogle官方出品的Mvp架构项目,含有多个不同的架构分支(此为Dagger分支).
  2. Dagger2Google根据Square的Dagger1出品的依赖注入框架,通过apt动态生成代码,性能优于用反射技术依赖注入的框架.
  3. Rxjava提供优雅的响应式Api解决异步请求.
  4. RxAndroid为Android提供响应式Api.
  5. Rxlifecycle在Android上使用rxjava都知道的一个坑,就是生命周期的解除订阅,这个框架通过绑定activity和fragment的生命周期完美解决.
  6. RxbindingJakeWharton大神的View绑定框架,优雅的处理View的响应事件.
  7. RxCache是使用注解为Retrofit加入二级缓存(内存,磁盘)的缓存库
  8. RetrofitSquare出品的网络请求库,极大的减少了http请求的代码和步骤.
  9. Okhttp同样Square出品,不多介绍,做Android都应该知道.
  10. Autolayout鸿洋大神的Android全尺寸适配框架.
  11. GsonGoogle官方的Json Convert框架.
  12. ButterknifeJakeWharton大神出品的view注入框架.
  13. Androideventbus一个轻量级使用注解的Eventbus.
  14. TimberJakeWharton大神出品Log框架,内部代码极少,但是思想非常不错.
  15. Glide此库为本框架默认封装图片加载库,可参照着例子更改为其他的库,Api和Picasso差不多,缓存机制比Picasso复杂,速度快,适合处理大型图片流,支持gfit,Fresco太大了!,在5.0一下优势很大,5.0以上系统默认使用的内存管理和Fresco类似.
  16. Realm速度和跨平台性使它成为如今最火的数据库,美中不足的就是so库太大
  17. LeakCanarySquare出品的专门用来检测AndroidJava的内存泄漏,通过通知栏提示内存泄漏信息
  18. RxErroHandler``Rxjava错误处理库,可在出现错误后重试

1 开发准备

此框架适合自己做定制修改,所有暂时不上传至JcenterMaven,请自行下载或clone

1.1 导入框架

compile project(':arms')

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.1.2'
 the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
        maven { url "https://jitpack.io" }//这里要使用rxcahche指定的仓库
    }
}

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

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

1.3 依赖Dagger2

本框架全部使用Dagger2管理,所以必须依赖Dagger2,找到app的build.gradle,加入如下代码

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'//使用apt插件

buildscript {
    repositories {
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'//使用apt
    }
}


dependencies {
      apt rootProject.ext.dependencies["dagger2-apt-compiler"]//依赖apt插件
    provided rootProject.ext.dependencies["javax.annotation"]//dagger2必须依赖jsr250 annotation
}

1.4 配置AndroidManifest

1.4.1 添加权限

    
    
    

1.4.2 配置Autolayout Meta

使用Autolayout 自适应框架必须配置Meta属性及设计图的宽高,详情参考Autolayout

//将设计图的宽高配置后,配合AutoLauout控件使用,在设计图尺寸以外的其它尺寸手机上,也能达到和设计图一样的显示效果
        
        

1.4.3 引用Glide自定义属性

本框架默认使用Glide加载图片,但提供一个管理器ImageLoader提供统一接口,使用策略者模式可轻松替换图片加载框架,本框架默认提供Glide的自定义缓存配置信息,使用它之前先引用它的自定义配置信息

 
        

1.5 混淆

由于本框架依赖大量三方库,所以已经在arms Module下的proguard-rules.pro中提供了所有规则,如果想使用它,请复制它替换app Module中的proguard-rules.pro,混淆时可以根据自己的需求修改或添加规则,混淆前务必注意将Java Bean,自定义组件添加进规则

2 快速开始

2.1 继承BaseApplication

新建项目的Application继承自BaseApplication,并在AndroidManifest中声明

    //BaseApplication为抽象类,必须实现getBaseUrl,这里返回Retrofit需要的baseurl
  @Override
    public String getBaseUrl() {
        return Api.APP_DOMAIN;
    }

2.1.1 AppComponent

Application生命周期是和App是一样的,所以是适合提供一些单例对象,本框架使用Dagger2管理,所以使用AppComponent来提供全局所有的单例对象

  • 创建AppComponent接口
@Singleton
@Component(modules = {AppModule.class, ClientModule.class, ServiceModule.class, ImageModule.class, CacheModule.class})
public interface AppComponent {
    Application Application();

    //服务管理器,retrofitApi
    ServiceManager serviceManager();

    //缓存管理器
    CacheManager cacheManager();

    //Rxjava错误处理管理类
    RxErrorHandler rxErrorHandler();

    OkHttpClient okHttpClient();

    //图片管理器,用于加载图片的管理类,默认使用glide,使用策略模式,可替换框架
    ImageLoader imageLoader();

    //gson
    Gson gson();
}
  • 构造AppComponent对象
@Override
    public void onCreate() {
        super.onCreate();
 mAppComponent = DaggerAppComponent
                .builder()
                .appModule(getAppModule())//baseApplication提供
                .clientModule(getClientModule())//baseApplication提供
                .imageModule(getImageModule())//baseApplication提供
                .serviceModule(new ServiceModule())//需自行创建
                .cacheModule(new CacheModule())//需自行创建
                .build();
 }
 //将AppComponent返回出去,供其它地方使用, AppComponent接口中声明的方法返回的实例, 在getAppComponent()拿到对象后都可以直接使用
  public AppComponent getAppComponent() {
        return mAppComponent;
    }

2.1.2 ServiceModule

ServiceModule提供RetrofitApi对应的Service,这些Service对象在AppComponent中注入ServiceManager(需继承BaseServiceManager)中统一管理

  • 自行定义Retrofit Service如下,熟练Retrofit请忽略
public interface CommonService {

    String HEADER_API_VERSION = "Accept: application/vnd.github.v3+json";

    @Headers({HEADER_API_VERSION})
    @GET("/users")
    Observable> getUsers(@Query("since") int lastIdQueried, @Query("per_page") int perPage);
}
  • 定义ServiceModule,这里使用Retrofit对象(ClientModule提供)实例化Service接口,提供所有Service对象(可以根据不同的逻辑划分多个Service接口)
@Module
public class ServiceModule {

    @Singleton
    @Provides
    CommonService provideCommonService(Retrofit retrofit) {
        return retrofit.create(CommonService.class);
    }

}
  • AppComponent将所有的Service注入到ServiceManager中,所有Model层都可以拿到此对象,意味着每个Model都可以请求任意Api
@Singleton
public class ServiceManager implements BaseServiceManager {
    private CommonService mCommonService;

//如果需要添加service只需在构造方法中添加对应的service,
在提供get方法返回出去,只要在ServiceModule提供了该service Dagger2会自行注入
    @Inject public ServiceManager(CommonService commonService){
        this.mCommonService = commonService;
    }

    public CommonService getCommonService() {
        return mCommonService;
    }
}

2.1.3 CacheModule

Cache层默认使用RxCache,CacheModule提供RetrofitApi对应的Cache对象,这些Cache对象在AppComponent中注入CacheManager(需继承BaseCacheManager)中统一管理

  • 自行定义RxCache Provider如下,熟练RxCache请忽略
public interface CommonCache {

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

}
  • 定义CacheModule,这里使用RxCache对象(ClientModule提供)实例化所有Cache接口,提供所有Cache对象
@Module
public class CacheModule {

    @Singleton
    @Provides
    CommonCache provideCommonService(RxCache rxCache) {
        return rxCache.using(CommonCache.class);
    }


}
  • AppComponent将所有的Cache注入到CacheManager中,所有Model层都可以拿到所有的Cache对象
@Singleton
public class CacheManager implements BaseCacheManager{
    private CommonCache mCommonCache;

//如果需要添加Cache只需在构造方法中添加对应的Cache,
在提供get方法返回出去,只要在CacheModule提供了该Cache Dagger2会自行注入
    @Inject public CacheManager(CommonCache commonCache) {
        this.mCommonCache = commonCache;
    }

    public CommonCache getCommonCache() {
        return mCommonCache;
    }
}

2.2 继承BaseActivity

让项目的基类Activity继承BaseActivity,BaseActivity默认注入Presenter,所以如果要使用Presenter必须指定对应的范型,并且提供注入Presenter所需要的Component

public abstract class WEActivity

extends BaseActivity

{ protected WEApplication mWeApplication; @Override protected void ComponentInject() { mWeApplication = (WEApplication) getApplication(); setupActivityComponent(mWeApplication.getAppComponent()); } //提供AppComponent(提供所有的单例对象)给子类,进行Component依赖 protected abstract void setupActivityComponent(AppComponent appComponent); }

2.3 继承BaseFragment

让项目的基类Fragment继承BaseFragment,BaseFragment默认注入Presenter,所以如果要使用Presenter必须指定对应的范型,并且提供注入Presenter所需要的Component

public abstract class WEFragment

extends BaseFragment

{ protected WEApplication mWeApplication; @Override protected void ComponentInject() { mWeApplication = (WEApplication)mActivity.getApplication(); setupFragmentComponent(mWeApplication.getAppComponent()); } //提供AppComponent(提供所有的单例对象)给子类,进行Component依赖 protected abstract void setupFragmentComponent(AppComponent appComponent); }

2.4 MVP实战

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

2.4.1 Contract

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

public interface UserContract {
    //对于经常使用的关于UI的方法可以定义到BaseView中,如显示隐藏进度条,和显示文字消息
    interface View extends BaseView {
        void setAdapter(DefaultAdapter adapter);
        void startLoadMore();
        void endLoadMore();
    }
    //Model层定义接口,外部只需关心model返回的数据,无需关心内部细节,及是否使用缓存
    interface Model extends IModel{
        Observable> getUsers(int lastIdQueried, boolean update);
    }
}

2.4.2 View

一般让ActivityFragment实现Contract中定义的View接口,供Presenter调用对应方法操作UI,BaseActivity默认注入Presenter,如想使用Presenter,必须指定Presenter的范型,和实现setupActivityComponent来提供Presenter需要的ComponentModule

public class UserActivity extends WEActivity implements UserContract.View {

    @Override
    protected void setupActivityComponent(AppComponent appComponent) {
        DaggerUserComponent
                .builder()
                .appComponent(appComponent)
                .userModule(new UserModule(this))
                .build()
                .inject(this);

    }

    @Override
    protected View initView() {
        return LayoutInflater.from(this).inflate(R.layout.activity_user, null, false);
    }

    @Override
    protected void initData() {
       }
}

2.4.3 Model

Model实现ContractModel接口,并且继承BaseModel,指定范型为,上面定义的ServiceManagerCacheManager,然后通过两个Manager拿到需要的ServiceCachePresenter提供需要的数据(是否使用缓存请自行选择)

public class UserModel extends BaseModel implements UserContract.Model{
    private CommonService mCommonService;
    private CommonCache mCommonCache;

    public UserModel(ServiceManager serviceManager, CacheManager cacheManager) {
        super(serviceManager, cacheManager);
        this.mCommonService = mServiceManager.getCommonService();
        this.mCommonCache = mCacheManager.getCommonCache();
    }

    @Override
    public Observable> getUsers(int lastIdQueried, boolean update) {

    }

}

2.4.4 Presenter

PresenterMVP中的大部分的作用为通过从Model层接口获取数据,在调用View层接口显示数据,首先实现BasePresenter,指定ModelView的范型,注意一定要指定Contract中定义的接口,Presenter需要的ModelView,都使用Dagger2注入,这样即解藕又方便测试,怎么注入?

@ActivityScope
public class UserPresenter extends BasePresenter {

    @Inject
    public UserPresenter(UserContract.Model model, UserContract.View rootView) {
        super(model, rootView);
    }
    //这里定义业务方法,相应用户的交互
    public void requestUsers(final boolean pullToRefresh) {
    }
}

2.4.5 MVP Module

这里的Module提供当前业务逻辑对应的ViewModel接口(Contract中定义的接口)的实现类,Model需要AppComponent中提供的ServiceManagerCacheManager来实现网络请求和缓存,所以需要通过Component依赖AppComponent拿到这两个Manager

@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(ServiceManager serviceManager, CacheManager cacheManager){
        return new UserModel(serviceManager,cacheManager);
    }
}

2.4.6 MVP Component

这里需要注意的是此Component必须依赖AppComponent,这样才能提供Model需要的ServiceManagerCacheManager,提供inject()方法就能将ModuleAppComponent中提供的对象注入到对应的类中,inject()中的参数不能是接口,怎么注入?

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

2.4.7 Dagger Scope

在上面的代码中ActivityScope大量出现在ModuleComponent中,Dagger2使用Scope限制每个Module中提供的对象的生命,Dagger2默认只提供一个@SingletonScope即单例,本框架提供@ActvityScope@FragmentScope,如需有需求要自行实现,ModuleComponent定义相同的ScopeModule中提供的对象的生命周期会和Component中一样

2.4.8 MVP总结

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

3 功能使用

3.1 全局捕捉Http请求和响应

继承BaseApplication时重写getHttpResultHandler方法,

 /**
     * 这里可以提供一个全局处理http请求和响应的处理类,
     * 这里可以在请求服务器之前可以拿到request,做一些操作比如给request统一添加token或者header
     * 这里可以比客户端提前一步拿到服务器返回的结果,可以做一些操作,比如token超时,重新获取
     * 默认不实现,如果有需求可以重写此方法
     *
     * @return
     */
    @Override
    public GlobeHttpHandler getHttpHandler() {
        return new GlobeHttpHandler() {
            @Override
            public Response onHttpResultResponse(String httpResult, Interceptor.Chain chain, Response response) {
                //这里可以先客户端一步拿到每一次http请求的结果,可以解析成json,做一些操作,如检测到token过期后
                //重新请求token,并重新执行请求
                try {
                    JSONObject object = new JSONObject(httpResult);
                    String code = object.getString("code");

               if(code.equals(TOKEN_EXPIRE))
                //这里如果发现token过期,可以先请求最新的token,然后在拿新的token去重新请求之前的http请求
                // create a new request and modify it accordingly using the new token
                Request newRequest = chain.request().newBuilder().header("token", newToken)
                        .build();

                // retry the request

                originalResponse.body().close();
                return chain.proceed(newRequest);
                //如果需要返回新的结果,则直接把response参数返回出去

                 } catch (JSONException e) {
                    e.printStackTrace();
                    //如果不做其它请求则返回参数中的response
                    return response;
                }

            }

            @Override
            public Request onHttpRequestBefore(Interceptor.Chain chain, Request request) {
                //如果需要再请求服务器之前做一些操作,则重新返回一个做过操作的的requeat如增加header,不做操作则返回request

                //return chain.request().newBuilder().header("token", tokenId)
//                .build();
                return request;
            }

        };
    }

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

如果需要使用Rxjava的全局错误处理,需要在继承BaseApplication时,重写getResponseErroListener,并在每次使用Rxjava调用subscribe时,使用ErrorHandleSubscriber,并传入AppComponent中提供的RxErrorHandler,此Subscribe,默认已经实现OnError方法,如想自定义可以重写OnError方法

 @Override
    protected ResponseErroListener getResponseErroListener() {
        return new ResponseErroListener() {
            @Override
            public void handleResponseError(Context context, Exception e) {
                Timber.tag(TAG).w("------------>" + e.getMessage());
                UiUtils.SnackbarText("net error");
            }
        };
    }
  • Rxjava中使用
            Observable
                .just(1)
                .retryWhen(new RetryWithDelay(3,2))//遇到错误时重试,第一个参数为重试几次,第二个参数为重试的间隔
                .subscribe(new ErrorHandleSubscriber(mErrorHandler) {
            @Override
            public void onNext(Integer Integer) {

            }
        });

3.3 为Okhttp添加自定义的Intercepter

继承BaseApplication时,重写getInterceptors即可为Okhttp提供多个Intercepter

protected Interceptor[] getInterceptors(){
        Intercepter[] intercepters = new Intercepter[1];
        intercepters[0] = new Interceptor();
        return intercepters;
}

3.4 切换图片请求框架

本框架默认使用Glide实现图片加载功能,使用ImagerLoader提供统一的接口,ImagerLoader使用策略模式和建造者模式,可以动态切换图片框架(比如说切换成Picasso),并且加载图片时传入的参数也可以随意扩展

  • 使用ImageLoader必须传入一个实现了BaseImageLoaderStrategy接口的图片加载实现类从而实现动态切换,所以首先要实现BaseImageLoaderStrategy,实现时必须指定一个继承自ImageConfig的实现类,使用建造者模式,可以储存一些信息,比如URL,
    ImageView,Placeholder等,可以不断的扩展,供图片加载框架使用
public class PicassoImageLoaderStrategy implements BaseImageLoaderStrategy {
     @Override
    public void loadImage(Context ctx, PicassoImageConfig config) {
    Picasso.with(ctx)
                .load(config.getUrl())
                .into(config.getImageView());
    }
}
  • 实现ImageCofig使用建造者模式
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);
        }
    }
}
  • ImageLoader构造时可以传入PicassoImageLoaderStrategy(),也可以通过AppComponent拿到ImageLoader对象后,setLoadImgStrategy(new PicassoImageLoaderStrategy)替换之前的实现(默认使用Glide)
方法一 在ImageModule中返回PicassoImageLoaderStrategy
@Module
public class ImageModule {

    @Singleton
    @Provides
    public BaseImageLoaderStrategy provideImageLoaderStrategy() {
        return new PicassoImageLoaderStrategy();
    }

    @Singleton
    @Provides
    public ImageLoader provideImageLoader(BaseImageLoaderStrategy strategy) {
        return new ImageLoader(strategy);
    }
}

方法2 拿到AppComponent中的 ImagerLoader
mApplication
    .getAppComponent()
    .imageLoader()
    .setLoadImgStrategy(new PicassoImageLoaderStrategy());


使用方法

mApplication
    .getAppComponent()
    .imageLoader()
    .loadImage(mApplication, GlideImageConfig
                .builder()
                .url(data.getAvatarUrl())
                .imagerView(mAvater)
                .build());

3.5 AndroidEventBus Tag

本框架使用AndroidEventBus实现事件总线,此框架使用注解标记目标方法,统一将Tag的常量写到EventBusTag接口中,便于管理,如果要在当前对象中使用AndroidEventBus请在需要使用的Activity,Fragment,Presenter中重写useEventBus(),返回true代表使用,默认返回true

3.6 AutoLayout组件

本框架使用AutoLayout框架,实现控件自适应,此框架要让组件自适应,必须让它的父控件,重绘,和重写LayoutParams,而官方只默认提供了三个ViewGroup,AutoRelativeLayout,AutoLinearLayout,AutoFrameLayout实现了这些操作,为了方便开发者使用,作者提供了一些常用的AutoLayout组件,在框架的widget包下的autolauout包中,引用即可使子组件自适应

3.7 自定义PopupWindow

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

3.8 快速实现RecycleView

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

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

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,等调试工具

  • 在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'
        }
    }
  • 在代码中使用(比如在application中做一些初始化设置)
public class WEApplication extends BaseApplication {

@Override
    public void onCreate() {
        if (BuildConfig.LOG_DEBUG) {//Timber日志打印
                    Timber.plant(new Timber.DebugTree());
                }
        if (BuildConfig.USE_CANARY) {//leakCanary内存泄露检查
                    LeakCanary.install(this);
                }
     } 
}

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中添加对应的方法
   /**
     * 通过eventbus post事件,远程遥控执行对应方法
     */
    @Subscriber(tag = APPMANAGER_MESSAGE, mode = ThreadMode.MAIN)
    public void onReceive(Message message) {
        switch (message.what) {
            case START_ACTIVITY:
                dispatchStart(message);
                break;
            case SHOW_SNACKBAR:
                showSnackbar((String) message.obj, message.arg1 == 0 ? false : true);
                break;
            case KILL_ALL:
                killAll();
                break;
            case APP_EXIT:
                AppExit();
                break;
        }
    }