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,粘性事件需要进行手动移除,也可以通过检查,看是否存在此事件。
以上就是最新学习的一些知识,希望能给大家提供帮助。