要点:如果不是必须, 用系统控件
RxJava
线程切换需要注意的地方
RxJava 内置的线程调度器的确可以让我们的线程切换得心应手,但其中也有些需要注意的地方。
- 简单地说,
subscribeOn()
指定的就是发射事件的线程,observerOn()
指定的就是订阅者接收事件的线程。
- 多次指定发射事件的线程只有第一次指定的有效,也就是说多次调用
subscribeOn()
只有第一次的有效,其余的会被忽略。
- 但多次指定订阅者接收线程是可以的,也就是说每调用一次
observerOn()
,下游的线程就会切换一次。
RxJava 中,已经内置了很多线程选项供我们选择,例如有:
Schedulers.io()
代表io操作的线程, 通常用于网络,读写文件等 IO 密集型的操作;
Schedulers.computation()
代表CPU计算密集型的操作, 例如需要大量计算的操作;
Schedulers.newThread()
代表一个常规的新线程;
RxAndroid
AndroidSchedulers.mainThread():运行在应用程序的主线程。
AndroidSchedulers.from(Looper looper):运行在该looper对应的线程当中。
不要打破链式调用!一个极低成本的 RxJava 全局 Error 处理方案 - 掘金
https://juejin.im/post/5be7bb9f6fb9a049f069c706
EventBus
EventBus 是一款针对 Android 优化的发布-订阅事件总线。它简化了应用程序内各组件间、组件与后台线程间的通信。其优点是开销小,代码更优雅,以及将发送者和接收者解耦。如果 Activity 和 Activity 进行交互还好说,但如果 Fragment 和 Fragment 进行交互则着实令人头疼。这时我们会使用广播来处理,但是使用广播略嫌麻烦并且效率也不高。如果传递的数据是实体类,需要序列化,那么传递的成本会有点高。
使用 EventBus 在讲到 EventBus 的基本用法之前,我们需要了解EventBus的三要素以及它的4种 ThreadMode。EventBus 的三要素如下。
- Event:事件。可以是任意类型的对象。
- Subscriber:事件订阅者。在 EventBus3.0 之前消息处理的方法只能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,它们分别代表4种线程模型。而在 EventBus3.0 之后,事件处理的方法可以随便取名,但是需要添加一个注解@Subscribe,并且要指定线程模型(默认为POSTING)。4种线程模型下面会讲到。
- Publisher:事件发布者。可以在任意线程任意位置发送事件,直接调用EventBus的post(Object)方法。可以自己实例化 EventBus 对象,但一般使用EventBus.getDefault()就可以。根据post函数参数的类型,会自动调用订阅相应类型事件的函数。
EventBus 的 4种 ThreadMode(线程模型)如下。
- POSTING(默认):如果使用事件处理函数指定了线程模型为POSTING,那么该事件是在哪个线程发布出来的,事件处理函数就会在哪个线程中运行,也就是说发布事件和接收事件在同一个线程中。在线程模型为POSTING的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起 ANR。
- MAIN:事件的处理会在 UI 线程中执行。事件处理的时间不能太长,长了会导致ANR。
- BACKGROUND:如果事件是在 UI 线程中发布出来的,那么该事件处理函数就会在新的线程中运行;如果事件本来就是在子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
- ASYNC:无论事件在哪个线程中发布,该事件处理函数都会在新建的子线程中执行;同样,此事件处理函数中禁止进行UI更新操作。
square/otto 已过时
This project is deprecated in favor of RxJava and RxAndroid. These projects permit the same event-driven programming model as Otto, but they’re more capable and offer better control of threading.
If you’re looking for guidance on migrating from Otto to Rx, this post is a good start.
Dagger 2
Dagger2 注解
开篇我们就提到Dagger2是基于Java注解来实现依赖注入的,那么在正式使用之前我们需要先了解下Dagger2中的注解。Dagger2使用过程中我们通常接触到的注解主要包括:@Inject, @Module, @Provides, @Component, @Qulifier, @Scope, @Singleten。
- @Inject:@Inject有两个作用,一是用来标记需要依赖的变量,以此告诉Dagger2为它提供依赖;二是用来标记构造函数,Dagger2通过@Inject注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,以此来为被@Inject标记了的变量提供依赖;
- @Module:@Module用于标注提供依赖的类。你可能会有点困惑,上面不是提到用@Inject标记构造函数就可以提供依赖了么,为什么还需要@Module?很多时候我们需要提供依赖的构造函数是第三方库的,我们没法给它加上@Inject注解,又比如说提供以来的构造函数是带参数的,如果我们之所简单的使用@Inject标记它,那么他的参数又怎么来呢?@Module正是帮我们解决这些问题的。
- @Provides:@Provides用于标注Module所标注的类中的方法,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值;
- @Component:@Component用于标注接口,是依赖需求方和依赖提供方之间的桥梁。被Component标注的接口在编译时会生成该接口的实现类(如果@Component标注的接口为CarComponent,则编译期生成的实现类为DaggerCarComponent),我们通过调用这个实现类的方法完成注入;
- @Qulifier:@Qulifier用于自定义注解,也就是说@Qulifier就如同Java提供的几种基本元注解一样用来标记注解类。我们在使用@Module来标注提供依赖的方法时,方法名我们是可以随便定义的(虽然我们定义方法名一般以provide开头,但这并不是强制的,只是为了增加可读性而已)。那么Dagger2怎么知道这个方法是为谁提供依赖呢?答案就是返回值的类型,Dagger2根据返回值的类型来决定为哪个被@Inject标记了的变量赋值。但是问题来了,一旦有多个一样的返回类型Dagger2就懵逼了。@Qulifier的存在正式为了解决这个问题,我们使用@Qulifier来定义自己的注解,然后通过自定义的注解去标注提供依赖的方法和依赖需求方(也就是被@Inject标注的变量),这样Dagger2就知道为谁提供依赖了。----一个更为精简的定义:当类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示;
- @Scope:@Scope同样用于自定义注解,我能可以通过@Scope自定义的注解来限定注解作用域,实现局部的单例;
- @Singleton:@Singleton其实就是一个通过@Scope定义的注解,我们一般通过它来实现全局单例。但实际上它并不能提前全局单例,是否能提供全局单例还要取决于对应的Component是否为一个全局对象。
google/dagger: A fast dependency injector for Android and Java.
https://github.com/google/dagger
Volley
private final Listener<String> mListener; @Override protected void deliverResponse(String response) { mListener.onResponse(response); } // 应该对服务器响应的数据进行解析
@Override protected Response<String> parseNetworkResponse(NetworkResponse response) { String parsed; try { parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch (UnsupportedEncodingException e) { parsed = new String(response.data); } return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); } ···