MVPArms MVP快速集成框架

Logo

前言

  • 今年的Android技术圈中MVP,Dagger2,Rxjava,Retrofit这些词汇非常火,随便打开一个技术论坛都充斥着大量的关于这些技术的文章,Github也充斥着各种以基于MVP+Retrofit+RxJava+Dagger2+MaterialDesign开发的xxxx为标题的开源项目或Demo.

  • 但是大家这么热心的开源此类项目,一直重复的做着同样的事教授大家使用的方式和技巧有没有想过依赖一个第三方库,就可以快速的搭建此类框架?

What is MVPArms?

  • MVPArms是一个集成了大量Android主流框架,并且全部使用Dagger2管理,以及提供API将所有库连接起来方便使用,还提供详细的Wiki文档.

  • 它可以使开发后面的所有项目都不用重复的复制粘贴(用过此类框架的朋友应该知道,这些库都依赖其它的库,就算一个build.gradle都会浪费很多时间),一个依赖省去很多烦恼,而且对于新手来说这些框架难的不仅仅是API的使用,更难的是怎么把它们结合到一起,应对各种场景的使用.

  • 对于一个新的Android项目,特别是熟练使用Dagger2Rxjava的开发者,你们只需要将此项目Clone下来,Demo只实现了一个页面,将此页面删除掉,添加所需要的Retrofit API,你的框架就搭建好了,你就可以直接使用Demo进行后续的开发,包结构也适合后面的扩展.

Notice

扩展项目, 了解一下:

Feature

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

  • Base 基类(BaseActivity, BaseFragment, BaseApplication …)
  • MVP 基类(IModel, IVIew, IPresenter …)
  • 框架高度可自定义化 (ConfigModule), 可在不修改框架源码的情况下对 Retoift, Okhttp, RxCache, Gson 等框架的特有属性进行自定义化配置, 可在不修改框架源码的情况下向 BaseApplication, BaseActivity, BaseFragment 的对应生命周期中插入任意代码, 并且框架独有的 ConfigModule 配置类, 可在不修改框架源码的情况下为框架轻松扩展任何新增功能
  • 独创的 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 修改包名后就可以直接使用, 快速接入(老项目接入请按下面的步骤)

Where?

MVPArms欢迎StarFork

Architectural

Package Structure

How?

Wiki

详细用法请参照Wiki,下面只是简单的介绍下MVP

Contract

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

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

View

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

public class UserActivity extends WEActivity<UserPresenter> 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() {
       }
}

Model

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

public class UserModel extends BaseModel<ServiceManager,CacheManager> 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<List<User>> getUsers(int lastIdQueried, boolean update) {
    
    }
  
}

Presenter

PresenterMVP中的大部分的作用为通过从Model层接口获取数据,在调用View层接口显示数据,首先实现BasePresenter,指定ModelView的范型,注意一定要指定Contract中定义的接口,Presenter需要的ModelView,都使用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) {
    
}

Acknowledgement

感谢本框架所使用到的所有三方库的Author,以及所有为Open Sourece做无私贡献的DeveloperOrganizations,使我们能更好的工作和学习,本人也会将业余时间回报给开源社区

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

Hello 我叫Jessyan,如果您喜欢我的文章,可以在以下平台关注我😘

Android布局性能优化

前言

  • 我们每天都在写各种各样的布局,对于一些简单的布局我们不会考虑性能的问题,但是对于一些复杂且层级嵌套较深的布局有没有想过可能存在的性能问题呢?接下来介绍几个Layout标签轻松优化布局性能

1.Include标签

介绍:

  • include标签的作用就是把一些复杂或者可重用的的页面抽取出来单独放到一个xml文件中,然后使用此标签导入需要到需要使用的布局中

用法:

<include
android:id="@+id/include"
layout="@layout/引用的布局文件" />

注意:

  • include标签若指定了ID属性,而你的layout也定义了ID,则你的layout的ID会被覆盖
  • include标签中所有的Android:layout_*都是有效的,前提是必须要写layout_width和layout_height两个属性。

2.Merge标签

介绍:

  • merge标签的作用是合并UI布局,减少视图层级,多用于替换FrameLayout(Activity`的根布局为`FrameLayout)或者当一个布局包含另一个布局时(布局的根元素必须一致)

用法:

  • 因为Activity的根元素是FrameLayout,如果你以前这样写Activity的布局文件:
< FrameLayout   xmlns:android = "http://schemas.android.com/apk/res/android"
     android:layout_width = "fill_parent"
     android:layout_height = "fill_parent" >
     < ImageView
         android:layout_width = "fill_parent"
         android:layout_height = "fill_parent"  
         android:scaleType = "center"
         android:src = "@drawable/sample"   />
     < TextView
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:text = "sample"   />
</ FrameLayout >
  • 现在你可以把根元素FrameLayout换成merge,像这样:
< merge   xmlns:android = "http://schemas.android.com/apk/res/android" >
     < ImageView
         android:layout_width = "fill_parent"
         android:layout_height = "fill_parent"  
         android:scaleType = "center"
         android:src = "@drawable/sample"   />
     < TextView
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:text = "sample"   />
</ merge >
  • 这时通过HierarchyViewer工具,你可以看到FrameLayout已经不见了

注意:

  • include进来的布局文件可以使用merge标签

  • 使用merge作为布局的根元素的时候,我们原来被merge替代的顶节点是不能有多余的属性的,否则merge实现不了.
  • merge只能用于xml layout文件的根元素
  • 在代码中inflate一个以merge为根元素的 布局文件时候,你需要指定一个ViewGroup作为其容器,并且要设置attachToRoottrue

3.ViewStub

介绍:

  • ViewStub是一个用于在运行时加载布局资源、不可见、宽高为0的View(未inflate时,隐藏、不绘制、宽高为0,不会消耗程序资源)
  • 多用于在程序运行时根据条件来决定显示/隐藏哪个视图

用法:

  • xml使用ViewStub占位
<ViewStub 
	android:layout="@layout/text" 
	android:layout_width="match_parent"
	android:layout_height="wrap_content"
	android:id="@+id/viewStub" />
  • 在需要时inflate
//方法1
ViewStub stub = (ViewStub)findViewById(R.id.viewStub);
stub.inflate(); //显示布局
//方法2
ViewStub stub = (ViewStub)findViewById(R.id.viewStub);
stub.setVisibility(View.VISIBLE);

注意:

  • 当调用inflate()函数的时候,ViewStub被引用的资源替代,并且返回引用的view,这样程序可以直接得到引用的view而不用再次调用函数findViewById()来查找了。

  • ViewStub只能够被inflate一次,一旦加载后ViewStub对象就会被置为空;
  • ViewStub目前不支持merge

4.Space

介绍:

  • Space用于在布局文件中占位(如分割线),不绘制(未实现onDraw方法),但是有宽高,用于解决过渡绘制

用法:

 <Space
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

注意:

  • Space只能设置宽高,因为他没有重写onDraw方法,所有就算设置了android:background也是无效的