跨线程通信
Pipes
管道流(pipeStream)是种特殊的流,用于在不同线程间单向传送数据,一个线程一端发送数据到管道,另外一个线程从输入管道读取
管道流是Java IO提供的进程间通信方案,不是Android平台专有的。
管道流有两种:
- 字符流:PipedReader、PipedWriter
- 字节流:PipedInputStrean、PipedOutputStram
1 | package com.iflytek.timesdemo; |
Shared Memory
Shared Memory即共享变量,要求是final变量,用的较多,且较简单。从略。
锁机制也是共享变量的一种。
BlockingQueue
BlockingQueue队列和平常队列一样都可以用来作为存储数据的容器,但有时候在线程当中涉及到数据存储的时候就会出现问题,BlockingQueue是空的话,如果一个线程要从BlockingQueue里取数据的时候,该线程将会被阻断,并进入等待状态,直到BlockingQueue里面有数据存入了后,就会唤醒线程进行数据的去除。若BlockingQueue是满的,如果一个线程要将数据存入BlockQueue,该线程将会被阻断,并进入等待状态,直到BlcokQueue里面的数据被取出有空间后,线程被唤醒后在将数据存入
BlockingQueue 方法以四种形式出现,对于不能立即满足但可能在将来某一时刻可以满足的操作,这四种形式的处理方式不同:第一种是抛出一个异常,第二种是返回一个特殊值(null 或 false,具体取决于操作),第三种是在操作可以成功前,无限期地阻塞当前线程,第四种是在放弃前只在给定的最大时间限制内阻塞。下表中总结了这些方法:
- add(anObject):把anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则报异常
- offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.
- put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.
- poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null
- take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止
BlockingQueue有四个具体的实现类,根据不同需求,选择不同的实现类 :
- ArrayBlockingQueue:规定大小的BlockingQueue,其构造函数必须带一个int参数来指明其大小.其所含的对象是以FIFO(先入先出)顺序排序的.
- LinkedBlockingQueue:大小不定的BlockingQueue,若其构造函数带一个规定大小的参数,生成的 BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定.其所含 的对象是以FIFO(先入先出)顺序排序的
- PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序.
- SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的.
LinkedBlockingQueue和ArrayBlockingQueue比较起来,它们背后所用的数据结构不一样,导致 LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量很大时其性能的可预见性低于 ArrayBlockingQueue.
1 | class Producer implements Runnable { |
Handler
Handler机制比较常见,从略
补充:IdleHandler
在android的MessageQueue中有一个static的接口IdleHandler,这个接口用于在MessageQueue中没有可处理的Message的时候回调,这样就可以在UI线程中处理完所有的view事务之后,回调一些额外的操作而不会block UI线程。1
2
3
4
5
6
7mHandler.getLooper().getQueue().addIdleHandler(new MessageQueue.IdleHandler() {
public boolean queueIdle() {
Log.e(TAG, "----I am idle");
return false;
}
});
UI Thread
在线程中更新UI线程,还有以下几种常用方案:
new Handler(Looper.getMainLooper()).post(task);
runOnUiThread(new Runnable() { … });
myView.post(new Runnable());
跨进程通信
Binder
Binder通信的四个角色:
- Client进程:使用服务的进程。
- Server进程:提供服务的进程。
- ServiceManager进程:ServiceManager的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
- Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
AIDL
AIDL实现过程是:定义接口,实现Sever,实现Client。从略。
补充:
- AIDL客户端调用默认是阻塞的,即如果不做线程处理,可能会发生ANR。
- oneway 修饰可将AIDL调用变成非阻塞的回调方式,但是有一定的限制:接口方法只能返回void,不能有 in 或者 out 修饰参数
1 |
|
Messenger
Messenger 对 AIDL 进行了封装,也就是对 Binder 的封装,我们可以使用它的实现来完成基于消息的跨进程通信,就和使用 Handler 一样简单。
使用步骤:
- 客户端创建一个 Messenger,传递一个 Handler 处理消息
服务端也一样 - 如果需要双向通信,给 Message 设置一个用于回信的 Messenger 即可:message.replyTo = mClientMessenger;
-客户端在调用send()方法之后,就会走 Binder 跨进程通信机制 ,最后到服务端的 Handler 中得到处理。
1 |
|
Other
Android平台下,常用跨进程通信还可以采用以下方案:
- Service
- startActivityForResult()
- ContentProvider
- Shared File
Android异步编程方案
Thread
常用从略。特别说明一下Runnable和Callable的区别:
Runnable接口1
2
3public interface Runnable {
void run();
}
Callable接口1
2
3public interface Callable<V> {
V call() throws Exception;
}
- Runnable执行方法是run(),Callable是call()
- 实现Runnable接口的任务线程无返回值;实现Callable接口的任务线程能返回执行结果
- call方法可以抛出异常,run方法若有异常只能在内部消化
- Callable接口支持返回执行结果,需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取结果;当不调用此方法时,主线程不会阻塞!
- 如果线程出现异常,Future.get()会抛出throws InterruptedException或者ExecutionException;如果线程已经取消,会抛出CancellationException
HandlerThread
HandlerThread的本质:继承Thread类 & 封装Handler类
HandlerThread的使用步骤:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler( handlerThread.getLooper() ) {
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
// 步骤5:结束线程,即停止线程的消息循环
mHandlerThread.quit();
Executor Framework
AsyncTask
详见:http://xhrong.github.io/2017/01/08/AsycTask%E6%BA%90%E7%A0%81%E6%8E%A2%E7%A9%B6/
IntentService
- IntentService 是继承自 Service 并处理异步请求的一个类,在 IntentService 内有一个工作线程来处理耗时操作。
- 当任务执行完后,IntentService 会自动停止,不需要我们去手动结束。
如果启动 IntentService 多次,那么每一个耗时操作会以工作队列的方式在 IntentService 的 onHandleIntent 回调方法中执行,依次去执行,使用串行的方式,执行完自动结束。
IntentService 源码中的 onBind() 默认返回 null;不适合 bindService() 启动服务,如果你执意要 bindService() 来启动 IntentService,可能因为你想通过 Binder 或 Messenger 使得 IntentService 和 Activity 可以通信,这样那么 onHandleIntent() 不会被回调,相当于在你使用 Service 而不是 IntentService。
IntentService 中使用的 Handler、Looper、MessageQueue 机制把消息发送到线程中去执行的,所以多次启动 IntentService 不会重新创建新的服务和新的线程,只是把消息加入消息队列中等待执行,而如果服务停止,会清除消息队列中的消息,后续的事件得不到执行。
AsyncQueryHandler
- AsyncQueryHandler继续于android.os.Handler,另开了一个线程来进行数据的操作
- AsyncQueryHandler异步对数据库进行增加、删除、更新、查询操作。避免在主线程中直接调用数据库相关操作导致的ANR异常。
1 | final String[] from=new String[] { "_id","id" }; |
Loader
![enter description here](./images/1563263141674.png
在应用中使用加载器时,可能会涉及到多个类和接口。 下表汇总了这些类和接口:
上表中的类和接口是您在应用中用于实现加载器的基本组件。 并非您创建的每个加载器都要用到上述所有类和接口。但是,为了初始化加载器以及实现一个 Loader 类(如 CursorLoader),您始终需要要引用 LoaderManager。下文将为您展示如何在应用中使用这些类和
1 | public static class CursorLoaderListFragment extends ListFragment |
参考资源
1、https://blog.csdn.net/liuyifeng1920/article/details/53368103
2、https://developer.android.google.cn/reference/java/util/concurrent/BlockingQueue.html
3、https://www.cnblogs.com/ganchuanpu/p/7758485.html
4、https://www.cnblogs.com/makaruila/p/4869912.html
5、https://www.jianshu.com/p/9c10beaa1c95
6、https://www.jianshu.com/p/be97093783d6
7、书籍:《Efficient Anrdoid Threading》
8、https://blog.csdn.net/Wtoria/article/details/51983584