Android组件化+MVP+Dragger2+RxJava+EventBus

Android开发规范


参考文章

学习Android开发的规范,主要学习到的点:
1、包名划分采用PBF进行分包的好处,因为同一功能代码在同一包中,所以容易删除功能,并且降低了package耦合;拥有私有作用域,一个功能不能访问另一功能的任何东西;包大小体现出功能的问题,包太大说明此功能需要进行重构。
2、命名规范主要需要记住的是静态字段命名以s开头,非静态字段以m开头,其他的规范都比较熟悉。
3、代码样式规范主要学习到Activities 和 Fragments 的传参,直接使用AndroidStudio写好的Live Templates,输入starter或者newInstance来生成启动器。
4、版本统一规范,可以在build.gradle文件中使用ext来定义全局变量

MVP


Google官方出品的架构项目,属于MVC的演化版本,MVC中Activity的作用既像View又像Controller,演化之后,出现了Presenter,将Activity完全视为View层,Presenter则负责View和Model层之间的交互。MVC中Controller没有完全让Model和View断开联系,Model和View可以进行交互,MVP则让两者完全解耦。
MVP简化了Activity的代码,将业务逻辑的相关代码提取到Presenter层中进行处理。同时MVP中虽然增加了很多的类,但是使代码变得很清晰。

组件化开发


参考文章
组件化架构包括三层,基础层主要是封装一些常用的操作,不同的组件都可以进行引用;组件层包含各种功能组件,每个组件都是一个module;应用层来引用不同的组件实现最终的业务功能。
组件化开发需要解决的问题:
1.每个组件都是一个整体,开发过程中需要满足组件单独运行和调试的要求。解决方法是:
在组件module中添加gradle.properties配置文件,在文件中添加一个boolean类型的变量,在build.gradle中通过这个布尔值来判断需要单独调试运行还是集成调试,然后修改apply plugin的值、是否需要配置applicationId,以及使用的Manifest文件的调整。
2、组件之间的相互调用和数据传递,界面跳转
创建ComponentBase模块,供基础层依赖,在其中定义组件中需要对外提供访问的Service接口和空实现的类,然后在组件中进行具体实现,并将实现这些方法的类的对象添加到ComponentBase提供的ServiceFactory中,其他组件就可以通过调用ServiceFactory获取想要调用的方法和数据。
组件间的界面跳转使用第三方的ARouter来实现组件间的路由功能,在Application将它初始化,就可以通过配置的路由来实现界面跳转。
3、主项目访问组件中的Fragment
一种方式可以直接通过反射来进行Fragment的初始化,并传递给Activity。
另一种方式和上述ServiceFactory方式相同,在Service接口中添加获取Fragment的方法,并在组件的实现类中返回Fragment对象,这样就可以通过ServiceFactory来获取Fragment。
4、集成调试时,如果依赖多个组件,如何实现依赖其中一部分就编译通过
这个问题通过上边问题的解决已经得到了解决,组件间没有直接的关联,都是通过ComponentBase的Service接口来实现,由于其中默认提供了空实现,所以即使被调用的组件没有初始化,调用也不会出现异常,只是调用了一个空的实现。

Dagger2


参考文章
Dagger2是一个依赖注入框架,注入方式是通过apt插件在编译阶段生成对应的注入代码。依赖注入的目的是为了降低程序的耦合,耦合产生的原因就是因为类之间的依赖,通过依赖注入来解决类之间的依赖问题。
如何引入Dagger2就不详细说明了,首先引入一段简单的在MVP中实现了依赖注入代码,虽然看起来比直接实例化更加复杂,但是这种方式通过添加一些辅助类解决了程序的耦合问题,去除了类之间的直接依赖。

public class MainActivity extends AppCompatActivity implements MainContract.View {
    @Inject
    MainPresenter mainPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         //初始化
         DaggerMainComponent.builder()
                .mainModule(new MainModule(this))
                .build()
                .inject(this);
        //调用Presenter方法加载数据
         mainPresenter.loadData();
    }

}

public class MainPresenter {
    private MainContract.View mView;
    
    @Inject
    MainPresenter(MainContract.View view) {
        mView = view;
    }    
    public void loadData() {
        //调用model层方法,加载数据
    }

@Module
public class MainModule {
    private final MainContract.View mView;

    public MainModule(MainContract.View view) {
        mView = view;
    }

    @Provides
    MainView provideMainView() {
        return mView;
    }
}

@Component(modules = MainModule.class)
public interface MainComponent {
    void inject(MainActivity activity);
}

public interface MainContract {
    interface View extends IView {
    }

    interface Presenter extends IPresenter {
        void loadData();
    }

    interface Model extends IModel {
    }
}

然后我们rebuild一下项目,通过生成的DaggerMainComponent来实现注入,在MainActivity添加如下代码:

DaggerMainComponent.builder()
               .mainModule(new MainModule(this))
               .build()
               .inject(this);

看完可能一脸懵,为什么MainActivity用@Inject注解就完成了mainPresenter的实例化,他们究竟是怎么产生的关系。
首先有几个注解需要了解一下:

  • @Inject 带有此注解的属性或构造方法将参与到依赖注入中,Dagger2会实例化有此注解的类
  • @Module 带有此注解的类,用来提供依赖,里面定义一些用@Provides注解的以provide开头的方法,这些方法就是所提供的依赖,Dagger2会在该类中寻找实例化某个类所需要的依赖。
  • @Component 用来将@Inject和@Module联系起来的桥梁,从@Module中获取依赖并将依赖注入给@Inject

理解了这几个含义就容易理解他们之间的关系,首先MainActivity想要依赖MainPresenter,然后发现MainPresenter构造函数有@Inject注解,可以实现实例化,但是里边有一个参数MainContract.View,因此需要通过MainModule中的provideMainView来提供依赖,作为MainModule的构造函数参数传入。
想要更深的理解,可以看上方提供的参考文章的源码讲解,讲解的很清晰,通过生成的类之间的关系,实现了注入的过程。
最核心的部分就是DaggerMainComponent.builder中的initialize方法

@SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideMainViewProvider = MainModule_ProvideMainViewFactory.create(builder.mainModule);

    this.mainPresenterProvider = MainPresenter_Factory.create(provideMainViewProvider);

    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(mainPresenterProvider);
  }

以及MainActivity_MembersInjector的injectMembers方法

@Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.mainPresenter = mainPresenterProvider.get();
  }

MainPresenter_Factory通过MainModule_ProvideMainViewFactory的get方法获取到MainContract.View对象,MainModule_ProvideMainViewFactory的get方法则是通过传入的mainModule的provideMainView方法来获取。
最后在DaggerMainComponent 的inject方法的实现中调用MainActivity_MembersInjector的injectMembers的方法中,injectMembers方法实现了将MainPresenter_Factory 的get方法实例化的mainPresenter赋值给MainActivity中的mainPresenter,从而实现了mainPresenter的实例化。

查找带有@Inject注解的参数是否可以注入,有两种方式。
第一种方式是直接查看构造函数,如果有@Inject注解则直接实例化;
第二种是 通过@Module注解的类中的@Provides看是否有需要的依赖。
注:如果有@Inject的构造函数中还需要其他参数,同样按照上述两种方式进行查找。

不看源码解释的确实有点绕口,感兴趣的可以去参考文章中查看更为详细的说明。

RxJava


RxJava是一个基于事件流,实现异步操作的库,逻辑简洁,实现优雅,使用简单等都是RxJava的优点。
Rxjava的原理是基于一种扩展的观察者模式,总共包括四个角色:

角色 作用
被观察者(Observable) 产生事件
观察者(Observer) 接收事件,并给出响应动作
订阅(Subscribe) 连接 被观察者 & 观察者
事件(Event) 被观察者 & 观察者沟通的载体

被观察者通过订阅将事件发送给观察者,观察者接收到事件并执行相应操作。
Observable中常用到的操作符:

  • observeOn:主要功能是指定观察者(Observer)在哪个线程执行,多次执行的话都会进行切换。
  • subscribeOn:指定自身(Observable)在哪个线程执行。如果多次调用,只有第一次调用生效,之后的调用不再切换,与调用的位置没有关系。除了指定自身的执行位置还可以指定doOnSubcribe执行的位置。
  • doOnSubscribe:事件被订阅之前会调用的方法,如果调用此方法之后又调用了subscribeOn方法,那么它也会切换到新的线程中执行。
  • doOnNext:观察者被通知之前的回调方法,执行onNext()前调用。
  • compose:对当前被观察者进行操作,并返回一个新的被观察者。和map不同,map知识改变被观察者发布的事件和序列,compose则是直接对当前Observable进行操作。
  • map:将被观察者,转换成新的被观察者对象,并且发送给观察者,观察者会收到新的被观察者并处理。
  • subcribe:连接观察者和被观察者,观察者如果想要收到被观察者发送的数据,就必须要使用subcribe来订阅被观察者。里边可以实现观察者用来组合的一些方法,包括:onNext,onError,onCompleted,如果不传参数,将只发生订阅,观察者接收不到任何数据和通知。

EventBus


EventBus主要功能是简化组件之间的通信,同样是使用观察者模式,有效的将事件的发送和接收进行分离,实现解耦。
主要包括事件的订阅者(接收方),事件的发布者(发送发),通过传递事件实现两者的通信。
基本的用法就是:
首先自定义一个事件类,用来传递事件;
其次需要在订阅者和发布者中都进行注册,并且在页面销毁等不需要的时候取消注册;
发送事件通过调用post方法,将发送的事件保存到事件队列;
在接收方通过@Subscribe注解来接收事件类的对象,注解中需要指定线程模型,并进行相应的处理。
线程模型总共分为四种:

  • POSTING (默认) 表示事件处理函数的线程跟发布事件的线程在同一个线程。
  • MAIN 表示事件处理函数的线程在主线程,因此在这里不能进行耗时操作。
  • BACKGROUND 表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程,那么事件处理函数将会开启一个后台线程,如果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
  • ASYNC 表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个线程运行,同样不能进行UI操作。

如果想要在事件发送之后订阅还可以接收到事件,那就需要用到粘性事件,粘性事件需要通过postSticky方法来发送,接收是需要在注解中添加sticky=true,粘性事件需要进行手动移除,也可以通过检查,看是否存在此事件。

以上就是最新学习的一些知识,希望能给大家提供帮助。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容