// 步骤1:自定义线程类(继承自Thread类)
class MyThread extends Thread{
// 步骤2:复写run(),定义线程的行为
@override
public void run(){...//定义线程行为
}
}
// 步骤3:实例化线程对象
MyThread mt = new MyThread("thread_1");
// 步骤4:通过线程对象控制线程状态,如运行start、睡眠sleep、停止stop……
mt.start(); // 开启线程
// 步骤1:创建线程辅助类,实现Runnable接口
class MyThread implements Runnable{
// 步骤2:复写run(),定义线程行为
public void run(){}
}
// 步骤3:创建线程辅助对象
MyThread mt = new MyThread();
// 步骤4:创建线程对象,并传入线程辅助类对象
// Runnable接口没有对线程的支持,必须创建线程Thread类的实例,由Thread创建的线程执行线程行为
Thread td = new Thread(mt);
// 步骤5:通过线程对象控制线程状态,如运行start、睡眠sleep、停止stop……
// 当调用start()方法时,线程对象会自动回调线程辅助类对象的run()
td.start();
public class CallableThreadTest implements Callable<Integer> {
public static void main(String[] args) {
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<>(ctt);
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
if (i == 20) {
new Thread(ft, "有返回值的线程").start();
}
}
try {
System.out.println("子线程的返回值:" + ft.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception {
int i = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
return i;
}
}
线程的创建和销毁,都涉及到系统调用,消耗系统资源,所以就引入了线程池技术,避免频繁的线程创建和销毁
Java使用Executors接口表示线程池,具体实现类是ThreadPoolExecutor
// 线程池的构造
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
ExecutorService service = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
// 线程池的使用
// 可以通过execute和submit两种方式来向线程池提交一个任务。
// execute()方法
// 使用execute来提交任务时,由于execute方法没有返回值,所以说我们也就无法判定任务是否被线程池执行成功。
service.execute(new Runnable() {
public void run() {
System.out.println("execute方式");
}
});
// submit()方法
// 使用submit来提交任务时,它会返回一个future,我们就可以通过这个future来判断任务是否执行成功,还可以通过future的get方法来获取返回值。
// 如果子线程任务没有完成,get方法会阻塞住直到任务完成
// 而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时候有可能任务并没有执行完。
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("submit方式");
return 2;
}
});
try {
Integer number = future.get();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 线程池关闭
// 调用线程池的shutdown()或shutdownNow()方法来关闭线程池
// shutdown原理:将线程池状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。
// shutdownNow原理:将线程池的状态设置成STOP状态,然后中断所有任务(包括正在执行的)的线程,并返回等待执行任务的列表。
线程池的工作原理与源码解读
【细谈Java并发】谈谈线程池:ThreadPoolExecutor
核心参数
ThreadPoolExecutor参数最全的构造方法(根据需求配置参数)
// 构造函数源码分析
public ThreadPoolExecutor (int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory
RejectedExecutionHandler handler)
参数 | 定义 | 说明 |
---|---|---|
corePoolSize | 核心线程数 | 线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corePoolSize,则新建的是非核心线程 核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态) |
maximumPoolSize | 线程池所能容纳最大线程数 | 线程总数 = 核心线程数 + 非核心线程数 当线程总数达到该数值之后,新任务会被阻塞 |
keepAliveTime | 非核心线程 限制超时时长 | 一个非核心线程,如果不干活(闲置状态)的时长超过这个参数所设定的时长,就会被销毁 |
unit | 指定keepAliveTime参数的时间单位 | 枚举类型,keepAliveTime的单位,常用TimeUnit.MILLSECONDS毫秒、TimeUnit.SECOND秒、TimeUnit.MINUTE分 |
workQueue | 任务队列 | 维护着等待执行的Runnable对象 当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务 |
threadFactory | 线程工厂 | 在线程池创建新线程的方式,这是一个接口。 实例化时需要实现他的Thread newThread(Runnable r)方法 |
handler | 用于抛出异常 |
源码分析
/**
* 在未来的某个时刻执行给定的任务。这个任务用一个新线程执行,或者用一个线程池中已经存在的线程执行
* 如果任务无法被提交执行,要么是因为这个Executor已经被shutdown关闭,要么是已经达到其容量上限,任务会被当前的RejectedExecutionHandler处理
*/
public void execute(Runnable command) {
int c = ctl.get();
// 1. 如果当前运行线程数< corePoolSize,则开启一个线程执行命令Command
// Command为该线程执行的第一个命令
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 如果当前线程数>= corePoolSize,则将任务添加到workQueue
if (isRunning(c) && workQueue.offer(command)) {
// 线程入队成功,再次检验校验位(线程池在入队后状态可能会发生变化)
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);// shutdown,则线程池不再接受新任务
else if (workerCountOf(recheck) == 0)
addWorker(null, false);// 当前运行线程数< corePoolSize
}
// 3. 如果放入workQueue失败(队列满了),则开启一个新的线程执行任务
else if (!addWorker(command, false))
// 4. 创建线程失败(当前线程数>= maxmumPoolSize || shutdown),调用reject拒绝接受任务
reject(command);
}
/* 补充 */
// addWorker
// addWorker方法的主要工作是在线程池中创建一个新的线程并执行(如果满足线程池状态和界限),firstTask参数 用于指定新增的线程执行的第一个任务。
private boolean addWorker(Runnable firstTask, boolean core){
...
w = new Worker(firstTask);
final Thread t = w.thread;
...
t.start(); //启动时会调用Worker类中的run方法,Worker本身实现了Runnable接口,所以一个Worker类型的对象也是一个线程。
}
// Worker.class
// 线程池中的每一个线程被封装成一个Worker对象,ThreadPool维护的其实就是一组Worker对象
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
...
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
// 从execute方法开始,Worker使用ThreadFactory创建新的工作线程
this.thread = getThreadFactory().newThread(this);
}
public void run() {
// 调用runWorker方法执行
runWorker(this);
}
...
}
// runWorker
final void runWorker(Worker w) {
// runWorker通过getTask不断从 阻塞队列WorkQueue 获取任务,然后执行任务
// 如果getTask返回null,进入processWorkerExit方法,整个线程结束
...
while (task != null || (task = getTask()) != null){
....
task.run();
}
}
总结:
菜鸟教程——AsyncTask
Android面试系列文章2018之Android部分AsyncTask机制篇
一个Android已封装好的轻量级异步类,属于抽象类,使用时需要实现子类。
它本质上是一个封装了 线程池 和 Handler 的异步框架。
线程池:缓存线程+复用线程,避免频繁创建 & 销毁线程 所带来的系统开销
用于:
public abstract class AsyncTask<Params, Progress, Result> {
...
}
参数 | 说明 |
---|---|
Params | 开始异步任务执行时传入的参数类型,对应execute(params)中传递的参数 |
Progress | 异步任务执行过程中,返回下载进度值的类型 |
Result | 异步任务执行完成后,返回的结果类型,与doInBackground()的返回值类型保持一致 |
不需要指定类型时可以写成void
2. 四个方法
在主线程中执行异步任务时myAsyncTask.execute(params)时,AsyncTask会按照如下四个步骤分别执行
方法名 | 作用 | 调用时期 | 所在线程 | 说明 |
---|---|---|---|---|
onPreExecute() | 执行异步任务前的操作 | 执行 异步任务前自动调用 | 主线程 | 用于UI组件初始化操作,如显示进度条对话框 |
*doInBackground(Params params) | 执行异步任务(接收输入参数并返回异步任务执行结果) | onPreExecute执行结束后,开始执行 异步任务时自动调用 | 子线程(后台线程池中开启一个工作线程执行) | 执行网络请求等耗时操作 |
onProgressUpdate(Progress values) | 在主线程中显示 工作线程任务执行的进度 | 当任务状态发生变化时(通过publishProgress方法)自动调用 | 主线程 | 在doInBackground中调用publishProgress(Progress) 的方法来将我们的进度实时传递给 onProgressUpdate 方法来更新 |
onPostExecute(Result result) | 接收异步任务执行结果,并将结果显示到UI组件 | 异步任务执行结束时自动调用 | 主线程 | 显示异步任务处理结果 |
private class MyTask extends AsyncTask<String, Integer, String> {
// 方法1:onPreExecute()
// 作用:执行 线程任务前的操作
@Override
protected void onPreExecute() {
text.setText("加载中");
// 执行前显示提示
}
// 方法2:doInBackground()
// 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
// 此处通过计算从而模拟“加载进度”的情况
@Override
protected String doInBackground(String... params) {
try {
int count = 0;
int length = 1;
while (count<99) {
count += length;
// 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
publishProgress(count);
// 模拟耗时任务
Thread.sleep(50);
}
}catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
// 方法3:onProgressUpdate()
// 作用:在主线程 显示线程任务执行的进度
@Override
protected void onProgressUpdate(Integer... progresses) {
progressBar.setProgress(progresses[0]);
text.setText("loading..." + progresses[0] + "%");
}
// 方法4:onPostExecute()
// 作用:接收线程任务执行结果、将执行结果显示到UI组件
@Override
protected void onPostExecute(String result) {
// 执行完毕后,则更新UI
text.setText("加载完毕");
}
// 方法5:onCancelled()
// 作用:将异步任务设置为:取消状态
@Override
protected void onCancelled() {
text.setText("已取消");
progressBar.setProgress(0);
}
}
MyTask mTask = new MyTask();
mTask.execute();
黑马视频:AsyncTask 源码
Carson_Ho:AsyncTask的原理 及其源码分析
AsyncTask基本使用
public class MyAsyncTask extends AsyncTask<String,Integer,Bitmap>{
@Override
protected void onPreExecute() {
super.onPreExecute();
//这里是在异步操作之前执行,运行在UI线程,一般显示给用户:此时即将要去加载图片了
}
@Override
protected Object doInBackground(java.lang.String... strings) {
//这里执行耗时操作,运行在子线程,如网络请求图片的操作
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
//此方法运行于UI线程,一般用来更新进度
//progressBar.setProgress(values[0]);
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Object result) {
//此方法中result是doInBackground执行完成后返回的,而且此方法运行在UI线程,更新UI
super.onPostExecute(result);
}
}
MyAsyncTask.execute("params"); // AsyncTask调用execute,开始执行异步任务
手动调用execute(Params… params),开始执行异步任务
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
// sDefaultExecutor = 任务队列 线程池类(SerialExecutor)的对象
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
// execute只能调用一次,若要多次执行任务,需创建新的AsyncTask
// 1. 判断AsyncTask当前执行状态
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
// 2.onPreExecute() 方法在主线程中运行
// 主线程中调用execute方法,execute中调用onPreExecute(),因此该方法也在主线程中运行
onPreExecute();
// 3.添加参数到任务中
// mWorker.mParams 保存了execute(params)方法中的参数
// *补充1 mWorker 可理解为当前任务对象
// mWorker 为 Callable 类型对象,实例化时复写call()方法,调用doInBackground(params)
// mWorker 实例化 --> Async构造方法
mWorker.mParams = params;
// 4.执行任务
// #补充2:mFutrue 可理解为当前任务的包装对象
// mFuture 继承自 FutureTask
// FutureTask 为Runnable 类型对象,保存callable类型变量(mWorker)
// 实例化复写run方法,执行mWorker.call(),调用doInBackground(params)
// 即在线程池的子线程中执行doInBackground,因此doInBackground在子线程中执行
// *补充3:
// 此处的exec = sDefaultExecutor = 任务队列 线程池类(SerialExecutor)的对象
// 从线程池中取线程 mFuture 并执行mFuture的run方法
exec.execute(mFuture);
return this;
}
// 补充1 WorkerRunnable类的构造函数
// private final WorkerRunnable mWorker;
// private static abstract class WorkerRunnable implements Callable {
// // Callable也是任务;与Runnable区别:Callable存在返回值=其泛型
// Params[] mParams;
// }
// 补充2 FutureTask类的构造函数(一个包装任务的包装类)
// private final FutureTask mFuture;
// public class FutureTask implements RunnableFuture {
// public FutureTask(Callable callable) {
// if (callable == null)
// throw new NullPointerException();
// this.callable = callable;
// this.state = NEW;
// }
// public void run() {
// ... 调用callable(mWorker)的call方法,执行doInBackground,并活动返回值
// result = c.call();
// ... 处理返回值,set(result)内部最终会调用FutureTask的done()
// set(result);
// }
// public interface RunnableFuture extends Runnable, Future
// 补充3:
/**
* 分析1:exec.execute(mFuture)
* 说明:属于任务队列 线程池类(SerialExecutor)的方法
*/
private static class SerialExecutor implements Executor {
// SerialExecutor = 静态内部类
// 即 是所有实例化的AsyncTask对象公有的
// SerialExecutor 内部维持了1个双向队列;
// 容量根据元素数量调节
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
// execute()被同步锁synchronized修饰
// 即说明:通过锁使得该队列保证AsyncTask中的任务是串行执行的
// 即 多个任务需1个个加到该队列中;然后 执行完队列头部的再执行下一个,以此类推
public synchronized void execute(final Runnable r) {
// 将实例化后的FutureTask类 的实例对象传入
// 即相当于:向队列中加入一个新的任务
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();->>分析2
}
}
});
// 若当前无任务执行,则去队列中取出1个执行
if (mActive == null) {
scheduleNext();
}
}
// 分析2
protected synchronized void scheduleNext() {
// 1. 取出队列头部任务
if ((mActive = mTasks.poll()) != null) {
// 2. 执行取出的队列头部任务
// 即 调用执行任务线程池类(THREAD_POOL_EXECUTOR)
/**
* 源码分析:THREAD_POOL_EXECUTOR.execute()
* 说明:
* a. THREAD_POOL_EXECUTOR实际上是1个已配置好的可执行并行任务的线程池
* b. 调用THREAD_POOL_EXECUTOR.execute()实际上是调用线程池的execute()去执行具体耗时任务
* c. 而该耗时任务则是初始化WorkerRunnable实例对象时复写的call()
*/
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
AsyncTask构造方法
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
// 1. 初始化WorkerRunnable变量mWorker = 一个可存储参数的Callable对象
// 复写call()方法,调用doInBackground(params)
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 执行异步操作 = 耗时操作,即 我们使用过程中复写的耗时任务
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
// 2. 初始化FutureTask变量 = 1个FutureTask
mFuture = new FutureTask<Result>(mWorker) {
// 结束doInBackground(params)后获得返回值会调用done
// done()简介:FutureTask内的Callable执行完后的调用方法
@Override
protected void done() {
try {
// get()函数获取返回值result
// *postResultIfNotInvoked调用postResult(result)
// 即将执行完成的任务结果通过InternalHandler传递到UI进程
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
// 任务的结果result和任务标识MESSAGE_POST_RESULT通过InternalHandler传递到UI线程
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
// 创建Handler对象 ->> 源自InternalHandler类
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
// 发送消息到Handler中
message.sendToTarget();
return result;
}
InternalHandler 接收 子线程 发送消息
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
// Handler 实现
// onPostExecute(result)运行在主线程
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// 若收到的消息 = MESSAGE_POST_RESULT
// 则通过finish() 将结果通过Handler传递到主线程
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
// 若收到的消息 = MESSAGE_POST_PROGRESS
// 则回调onProgressUpdate()通知主线程更新进度的操作
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
为什么不能在子线程中更新UI?
子线程可以更新UI(在系统还未检测当前更新UI的线程是否是UI线程之前执行操作)
谷歌提出:“UI更新一定要在UI线程里实现” 这一规则原因如下:
目的在于提高移动端更新UI的效率和和安全性,以此带来流畅的体验。原因是:
Android的UI访问是没有加锁的,多个线程可以同时访问更新操作同一个UI控件。也就是说访问UI的时候,android系统当中的控件都不是线程安全的,这将导致在多线程模式下,当多个线程共同访问更新操作同一个UI控件时容易发生不可控的错误,而这是致命的。
所以Android中规定只能在UI线程中访问UI,这相当于从另一个角度给Android的UI访问加上锁,一个伪锁。
概念 | 定义 | 作用 | 说明 |
---|---|---|---|
主线程/UI线程 Main Thread |
当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程 | 用于处理UI相关的事件 | Android OS中,一个进程被创建之后,同时会自动开启一条主线程(当前Activity),主线程创建一个Looper和一个MessageQueue |
子线程/工作线程 Work Thread |
手动开启的线程 | 用于执行耗时操作,如网络请求、数据加载等 | |
消息 Message |
线程间通讯的数据单元(Handler 发送 & 响应的消息对象) | 存储子线程发送给UI线程的通信信息 | |
消息队列 Message Queue |
用来存放Message对象的数据结构 | 用来存放Handler发送过来的消息,不按照FIFO规则执行,而是将Message以单链表的方式串联起来的(适用于插入消息MessageQueue.enqueue和取出MessageQueue.next),等待Looper的抽取 维护所有顶层应用对象(Activities, Broadcast receivers等)以及主线程创建的窗口 |
MessageQueue对象不需要手动创建,而是由Looper对象对其进行管理,一个线程最多只可以拥有一个MessageQueue |
循环器 Looper |
MessageQueue的管理者 MessageQueue与Handler通信媒介 |
消息循环,包括 消息获取:循环取出消息队列MessageQueue中消息 消息分发:将取出的消息发送给对应的处理者Handler |
在一个线程中,如果存在Looper对象,则必定存在MessageQueue对象,并且只存在一个Looper对象和一个MessageQueue对象。在Android系统中,除了主线程有默认的Looper对象,其它线程默认是没有Looper对象。如果想让我们新创建的线程拥有Looper对象时,我们首先应调用Looper.prepare()方法,然后再调用Looper.loop()方法 |
处理者 Handler |
消息的处理者 主线程与子线程的通信媒介 |
Handler的作用是把消息加入特定(主线程)的消息队列中:Handler.sendMessage 处理Looper分发过来的消息:Handler.dispatchMessage |
MessageQueue,Handler和Looper三者之间的关系
一个线程Thread绑定一个循环器Looper和一个消息队列MessageQueue,对应多个处理者Handler。MessageQueue可以存放来自不同Handler发送的消息,Looper可以将消息分发给对应的Handler进行处理。
步骤1:(自定义)新创建Handler子类(继承Handler类) & 复写handleMessage()方法
步骤2:在主线程中创建Handler实例
步骤3:创建工作线程(AsyncTask、Thread、Runnable)处理耗时操作,并创建需要发送的消息对象Message,并通过引用主线程的Handler发送
步骤4:开启工作线程
Handler使用方式 因发送消息到消息队列的方式不同而不同,共分为2种:使用Handler.sendMessage()、使用Handler.post()
public class MainActivity extends AppCompatActivity {
public TextView mTextView;
public Handler mHandler;
// 步骤1:(自定义)新创建Handler子类(继承Handler类) & 复写handleMessage()方法
class Mhandler extends Handler {
// 通过复写handlerMessage() 从而确定更新UI的操作
@Override
public void handleMessage(Message msg) {
// 根据不同线程发送过来的消息,执行不同的UI操作
// 根据 Message对象的what属性 标识不同的消息
switch (msg.what) {
case 1:
mTextView.setText("执行了线程1的UI操作");
break;
case 2:
mTextView.setText("执行了线程2的UI操作");
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.show);
// 步骤2:在主线程中创建Handler实例
mHandler = new Mhandler();
// 采用继承Thread类实现多线程演示
new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 步骤3:创建所需的消息对象
Message msg = Message.obtain();
msg.what = 1; // 消息标识
msg.obj = "A"; // 消息内存存放
// 步骤4:在工作线程中 通过Handler发送消息到消息队列中
mHandler.sendMessage(msg);
}
}.start();
// 步骤5:开启工作线程(同时启动了Handler)
// 此处用2个工作线程展示
new Thread() {
@Override
public void run() {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 通过sendMessage()发送
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
mHandler.sendMessage(msg);
}
}.start();
}
}
public class MainActivity extends AppCompatActivity {
public TextView mTextView;
public Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.show);
// 步骤1:在主线程中创建Handler实例
mHandler = new Handler();
// 步骤2:在工作线程中 发送消息到消息队列中 & 指定操作UI内容
new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 通过psot()发送,需传入1个Runnable对象
mHandler.post(new Runnable() {
@Override
public void run() {
// 指定操作UI内容
mTextView.setText("执行了线程1的UI操作");
}
});
}
}.start();
// 步骤3:开启工作线程(同时启动了Handler)
// 此处用2个工作线程展示
new Thread() {
@Override
public void run() {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(new Runnable() {
@Override
public void run() {
mTextView.setText("执行了线程2的UI操作");
}
});
}
}.start();
}
}
步骤 | 说明 | 备注 |
---|---|---|
异步通信准备 | 在主线程中创建 循环器Looper 对象、消息队列MessageQueue 对象、Handler 对象 | 三者均位于主线程 当MessageQueue创建后,Looper自动进入消息循环 此时Handler 自动绑定了 Looper 和 MessageQueue |
消息发送 | 工作线程 通过Handler 发送消息Message 到消息队列MessageQueue中 | 消息内容一般是UI操作 发送消息通过Handler.sendMessage(Message msg)和Handler.post(Runnable r)发送 入队一般通过MessageQueue.enqueueMessage(Message)处理 |
消息循环 | 包括 消息出队 和 消息分发 两个步骤 消息出队:Looper循环取出消息队列MessageQueue中的消息Message 消息分发:Looper将取出的消息Message发送给创建消息的处理者Handler |
如果在消息循环的过程中,消息队列MessageQueue为空队列时,线程阻塞 |
消息处理 | 消息处理者Handler 接受 Looper 发送过来的消息Message,并根据Messge进行UI操作 |
/**
* 此处以 匿名内部类 的使用方式为例
*/
// 步骤1:在主线程中 通过匿名内部类 创建Handler类对象
private Handler mhandler = new Handler(){
// 通过复写handlerMessage()从而确定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需执行的UI操作
}
};
// 步骤2:创建消息对象
Message msg = Message.obtain(); // 实例化消息对象
msg.what = 1; // 消息标识
msg.obj = "AA"; // 消息内容存放
// 步骤3:在工作线程中 通过Handler发送消息到消息队列中
// 多线程可采用AsyncTask、继承Thread类、实现Runnable
mHandler.sendMessage(msg)
/**
* 具体使用
*/
private Handler mhandler = new Handler(){
// 通过复写handlerMessage()从而确定更新UI的操作
@Override
public void handleMessage(Message msg) {
...// 需执行的UI操作
}
};
/**
* 源码分析:Handler的构造方法
* 作用:初始化Handler对象 & 绑定线程
* 注:
* a. Handler需绑定 线程才能使用;绑定后,Handler的消息处理会在绑定的线程中执行
* b. 绑定方式 = 先指定Looper对象,从而绑定了 Looper对象所绑定的线程(因为Looper对象本已绑定了对应线程)
* c. 即:指定了Handler对象的 Looper对象 = 绑定到了Looper对象所在的线程
*/
public Handler() {
this(null, false);
// ->>分析1
}
/**
* 分析1:this(null, false) = Handler(null,false)
*/
public Handler(Callback callback, boolean async) {
...// 仅贴出关键代码
// 1. 指定Looper对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// Looper.myLooper()作用:获取当前线程的Looper对象;若线程无Looper对象则抛出异常
// 即 :若线程中无创建Looper对象,则也无法创建Handler对象(并抛出异常)
// 故 若需在子线程中创建Handler对象,则需先创建Looper对象
// 主线程中会自动创建Looper对象
// 2. 绑定消息队列对象(MessageQueue)
mQueue = mLooper.mQueue;
// 获取该Looper对象中保存的消息队列对象(MessageQueue)
// 至此,保证了handler对象 关联上 Looper对象中MessageQueue
}
/**
* 源码分析1:Looper.prepare()
* 作用:为当前线程(子线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue)
* 注:需在子线程中手动调用该方法
*/
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 1. 判断sThreadLocal是否为null,否则抛出异常
//即 Looper.prepare()方法不能被调用两次 = 1个线程中只能对应1个Looper实例
// 注:sThreadLocal = 1个ThreadLocal对象,是线程本地存储区,用于存储线程的变量
sThreadLocal.set(new Looper(true));
// 2. 若为初次Looper.prepare(),则创建Looper对象 & 存放在ThreadLocal变量中
// 注:Looper对象是存放在Thread线程里的
// 源码分析Looper的构造方法->>分析a
}
/**
* 分析a:Looper的构造方法
* 创建Looper 同时会自动创建一个消息队列对象MessageQueue
**/
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
// 1. 创建1个消息队列对象(MessageQueue)
// 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)
mRun = true;
mThread = Thread.currentThread();
}
/**
* 源码分析2:Looper.prepareMainLooper()
* 作用:为 主线程(UI线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue)
* 注:该方法在主线程(UI线程)创建时自动调用,即 主线程的Looper对象自动生成,不需手动生成
*/
// 在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫UI线程)
// 创建时,会自动调用ActivityThread的1个静态的main()方法 = 应用程序的入口
// main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象
/**
* 源码分析:main()
**/
public static void main(String[] args) {
... // 仅贴出关键代码
Looper.prepareMainLooper();
// 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
// 方法逻辑类似Looper.prepare()
// 注:prepare():为子线程中创建1个Looper对象
ActivityThread thread = new ActivityThread();
// 2. 创建主线程
Looper.loop();
// 3. 自动开启 消息循环 ->>下面将详细分析
}
- 主线程的Looper对象自动生成,不需手动生成;而子线程的Looper对象则需手动通过Looper.prepare()创建
- 在子线程若不手动创建Looper对象 则无法生成Handler对象
- 根据Handler的作用(在主线程更新UI),故Handler实例的创建场景 主要在主线程
/**
* 源码分析: Looper.loop()
* 作用:消息循环,即不断从消息队列MessageQueue中获取消息Message、并分发消息到Handler,知道消息为空时退出循环
* 特别注意:
* a. 主线程的消息循环不允许退出,即无限循环
* b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
*/
public static void loop() {
...// 仅贴出关键代码
// 1. 获取当前Looper的消息队列(MessageQueue)
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
// 2. 消息循环(通过for循环 => 死循环:主线程保持不退出原因)
for (;;) {
// 2.1 从消息队列中取出消息
// 消息队列的顺序维护使用单链表形式维护的,将消息队列中第一条数据取出来,并将第二条数据编程第一条
Message msg = queue.next();
if (msg == null) {
// 若取出的消息为空,则线程阻塞,退出循环
return;
}
// 2.2 获取msg的目标Handler,分发消息Message到对应的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派发给消息对象msg的target属性(1个handler对象)
// handler对象调用其回调方法对消息进行处理 ->>分析1
// 3. 释放消息占据的资源
msg.recycle();
}
}
/**
* 分析1:dispatchMessage(msg)
* 定义:属于处理者类(Handler)中的方法
* 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
*/
public void dispatchMessage(Message msg) {
// 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
// 则执行handleCallback(msg),即回调Runnable对象里复写的run()->> 分析2
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
// 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息(即此处需讨论的)
// 则执行handleMessage(msg),即回调复写的handleMessage(msg) ->> 分析3
handleMessage(msg);
}
}
/**
* 分析2:handleCallback(msg)
**/
private static void handleCallback(Message message) {
message.callback.run();
// Message对象的callback属性 = 传入的Runnable对象
// 即回调Runnable对象里复写的run()
}
/**
* 分析3:handleMessage(msg)
* 注:该方法 = 空方法,在创建Handler实例时复写 = 自定义消息处理方式
**/
public void handleMessage(Message msg) {
... // 创建Handler实例时复写
}
特别注意:在进行消息分发时(dispatchMessage(msg)),会进行1次发送方式的判断:
- 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run()
- 若msg.callback属性为空,则代表使用了sendMessage(Message
msg)发送消息,则回调复写的handleMessage(msg)
/**
* 具体使用
*/
Message msg = Message.obtain(); // 实例化消息对象
msg.what = 1; // 消息标识
msg.obj = "AA"; // 消息内容存放
/**
* 源码分析:Message.obtain()
* 作用:创建消息对象
* 注:创建Message对象可用关键字new 或 Message.obtain()
*/
public static Message obtain() {
// Message内部维护了1个Message池,用于Message消息对象的复用
// 使用obtain()则是直接从池内获取
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
// 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存
}
// 若池内无消息对象可复用,则还是用关键字new创建
return new Message();
}
/**
* 具体使用
*/
mHandler.sendMessage(msg);
/**
* 源码分析:mHandler.sendMessage(msg)
* 定义:属于处理器类(Handler)的方法
* 作用:将消息 发送 到消息队列中(Message ->> MessageQueue)
*/
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
// ->> 最终调用 sendMessageAtTime(msg, uptimeMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// 1. 获取对应的消息队列对象(MessageQueue)
MessageQueue queue = mQueue;
// 2. 调用了enqueueMessage方法 ->>分析1
return enqueueMessage(queue, msg, uptimeMillis);
}
/**
* 分析1:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 1. 将msg.target赋值为this,即 :把 当前的Handler实例对象作为msg的target属性(Message实例保存了发送该msg的Handler信息)
msg.target = this;
// 因此,当Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
// 实际上则是将该消息派发给对应的Handler实例
// 2. 调用消息队列的enqueueMessage()
// 即:Handler发送的消息,最终是保存到消息队列
// queue.enqueueMessage(msg, uptimeMillis)属于消息队列类(MessageQueue)的方法,入队,即 将消息 根据时间 放入到消息队列中
// 采用单链表实现:提高插入消息、删除消息的效率
return queue.enqueueMessage(msg, uptimeMillis);
}
// 之后,随着Looper对象的无限消息循环
// 不断从消息队列中取出Handler发送的消息 & 分发到对应Handler
// 最终回调Handler.handleMessage()处理消息
总结
步骤 | 核心方法 | 说明 |
---|---|---|
主线程创建时 | Looper.prepare() Looper.loop() |
在ActivityThread.java主线程入口类,自动创建1个Looper,1个MessageQueue 并进入消息循环(不断从消息队列中取出消息Message并分发给相应的处理器msg.target.dispatchMessage(msg)) |
创建Handler实例 | Handler构造方法 Handler.handleMessage() |
绑定当前线程(Looper & MessageQueue) 复写回调方法对Looper分发的消息进行处理 |
创建消息对象 | Message.obtain() | 从Message池获取或新建Message对象 |
通过Handler发送消息到消息队列中 | Handler.sendMessage() | Message.target保存Handler信息 调用MessageQueue.enqueueMessage()将消息放入消息队列中 |
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:";
private Handler showhandler;
// 主线程创建时便自动创建Looper & 对应的MessageQueue
// 之后执行Loop()进入消息循环
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1. 实例化自定义的Handler类对象->>分析1
//注:
// a. 此处并无指定Looper,故自动绑定当前线程(主线程)的Looper、MessageQueue;
// b. 定义时需传入持有的Activity实例(弱引用)
showhandler = new FHandler(this);
// 2. 启动子线程1
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 1;// 消息标识
msg.obj = "AA";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
// 3. 启动子线程2
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2;// 消息标识
msg.obj = "BB";// 消息存放
// b. 传入主线程的Handler & 向其MessageQueue发送消息
showhandler.sendMessage(msg);
}
}.start();
}
// 分析1:自定义Handler子类
// 设置为:静态内部类
private static class FHandler extends Handler{
// 定义 弱引用实例
private WeakReference<Activity> reference;
// 在构造方法中传入需持有的Activity实例
public FHandler(Activity activity) {
// 使用WeakReference弱引用持有Activity实例
reference = new WeakReference<Activity>(activity); }
// 通过复写handlerMessage() 从而确定更新UI的操作
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
Log.d(TAG, "收到线程1的消息");
break;
case 2:
Log.d(TAG, " 收到线程2的消息");
break;
}
}
}
}
一个Android 已封装好的轻量级异步通信类。
用于实现多线程(开启工作线程执行耗时操作),异步通信与消息传递(工作线程与主线程之间通信)
本质上是通过继承Thread类和封装Handler类的使用,从而使得创建新线程和与其他线程进行通信变得更加方便易用
// 步骤1:创建HandlerThread实例对象
// 传入参数 = 线程名字,作用 = 标记该线程
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
// 步骤2:启动线程
mHandlerThread.start();
// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
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();
内部原理 = Thread类 + Handler类机制,即:
/**
* 具体使用
* 传入参数 = 线程名字,作用 = 标记该线程
*/
HandlerThread mHandlerThread = new HandlerThread("handlerThread");
/**
* 源码分析:HandlerThread类的构造方法
*/
public class HandlerThread extends Thread {
// 继承自Thread类
int mPriority; // 线程优先级
int mTid = -1; // 当前线程id
Looper mLooper; // 当前线程持有的Looper对象
// HandlerThread类有2个构造方法
// 区别在于:设置当前线程的优先级参数,即可自定义设置 or 使用默认优先级
// 方式1. 默认优先级
public HandlerThread(String name) {
// 通过调用父类默认的方法创建线程
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
// 方法2. 自定义设置优先级
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
...
}
/**
* 具体使用
*/
mHandlerThread.start();
/**
* 源码分析:此处调用的是父类(Thread类)的start(),最终回调HandlerThread的run()
*/
@Override
public void run() {
// 1. 获得当前线程的id
mTid = Process.myTid();
// 2. 创建1个Looper对象 & MessageQueue对象
Looper.prepare();
// 3. 通过持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
// 发出通知:当前线程已经创建mLooper对象成功
// 此处主要是通知getLooper()中的wait()
notifyAll();
// 此处使用持有锁机制 + notifyAll() 是为了保证后面获得Looper对象前就已创建好Looper对象
}
// 4. 设置当前线程的优先级
Process.setThreadPriority(mPriority);
// 5. 在线程循环前做一些准备工作
// 该方法实现体是空的,子类可实现 / 不实现该方法
onLooperPrepared();
// 6. 进行消息循环,即不断从MessageQueue中取消息 & 派发消息
Looper.loop();
mTid = -1;
}
}
/**
* 具体使用
* 作用:将Handler关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
* 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
*/
Handler workHandler = new Handler( handlerThread.getLooper() ) {
@Override
public boolean handleMessage(Message msg) {
...//消息处理
return true;
}
});
/**
* 源码分析:handlerThread.getLooper()
* 作用:获得当前HandlerThread线程中的Looper对象
*/
public Looper getLooper() {
// 若线程不是存活的,则直接返回null
if (!isAlive()) {
return null;
}
// 若当前线程存活,再判断线程的成员变量mLooper是否为null
// 直到线程创建完Looper对象后才能获得Looper对象,若Looper对象未创建成功,则阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
// 此处会调用wait方法去等待
wait();
} catch (InterruptedException e) {
}
}
}
// 上述步骤run()使用 持有锁机制 + notifyAll() 获得Looper对象后
// 则通知当前线程的wait()结束等待 & 跳出循环
// 最终getLooper()返回的是在run()中创建的mLooper对象
return mLooper;
}
/**
* 具体使用
* 作用:在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作
* 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行
*/
// a. 定义要发送的消息
Message msg = Message.obtain();
msg.what = 2; //消息的标识
msg.obj = "B"; // 消息的存放
// b. 通过Handler发送消息到其绑定的消息队列
workHandler.sendMessage(msg);
/**
* 源码分析:workHandler.sendMessage(msg)
* 此处的源码即Handler的源码,故不作过多描述
*/
带你了解android的IPC机制
Android 中的 IPC 方式
Android Binder机制及AIDL使用
方式 | 说明 | 特点 |
---|---|---|
Bundle | 在Android中三大组件(Activity,Service,Receiver)都支持在Intent中传递Bundle数据,由于Bundle实现了Parcelable接口(一种特有的序列化方法),所以它可以很方便的在不同的进程之间进行传输 | 四大组件间的进程间通信方式,简单易用,但只能是单方向的简单数据传输,使用有一定的局限性 |
文件共享 | 将对象序列化之后保存到文件中,在通过反序列,将对象从文件中读取出来。此方式对文件的格式没有具体的要求,可以是文件、XML、JSON等 | 文件共享方式也存在着很大的局限性,如并发读/写问题,如读取的数据不完整或者读取的数据不是最新的。不适合高并发场景,并且无法做到进程间的及时通信 |
Messenger | 通过Messenger来进行进程间通信,在Messenger中放入我们需要传递的数据,实现进程间数据传递。Messenger只能传递Message对象,Messenger是一种轻量级的IPC方案,它的底层实现是AIDL | Messenger内部消息处理使用Handler实现的,所以它是以串行的方式处理客服端发送过来的消息的,如果有大量的消息发送给服务器端,服务器端只能一个一个处理,如果并发量大的话用Messenger就不合适了,而且Messenger的主要作用就是为了传递消息,很多时候我们需要跨进程调用服务器端的方法,这种需求Messenger就无法做到了。 |
AIDL | 用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码 | 如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。AIDL是IPC的一个轻量级实现,Android也提供了一个工具,可以自动创建Stub(类架构,类骨架) |
ContentProvider | ContentProvider(内容提供者)是Android中的四大组件之一,为了在应用程序之间进行数据交换,Android提供了ContentProvider,ContentProvider是不同应用之间进行数据交换的API,一旦某个应用程序通过ContentProvider暴露了自己的数据操作的接口,那么不管该应用程序是否启动,其他的应用程序都可以通过接口来操作接口内的数据,包括数据的增、删、改、查等操作 | 使用受限,只能根据特定规则访问数据 |
Socket | Socket也是实现进程间通信的一种方式,Socket也称为“套接字”(网络通信中概念),通过Socket也可以实现跨进程通信,Socaket主要还是应用在网络通信中 |
carson_ho:Android跨进程通信:图文详解 Binder机制 原理
Binder 驱动创建一块接收缓存区。实现Service 进程用户空间 与 Client内核缓存区的地址映射。
因此用户通过系统调用(copy_from_user)发送数据到内核缓冲区时,也相当于发送到了Server进程的用户空间;
同理,Server将执行的结果写入共享的接受缓存区时,也相当于发送到了内核缓存区,用户通过系统调用(copy_to_user)从内核缓存区接受Server进程返回的数据。
Binder只需要通过一次数据拷贝便可实现进程间的数据传递。
//获取WindowManager服务引用
WindowManager wm = (WindowManager) getSystemService(getApplication().WINDOW_SERVICE);
//布局参数layoutParams相关设置略...
View view = LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);
(3)activity.bindService()
interface IService {
// 定义暴露的方法
void callMethodService();
}
Make Project后,系统会自动生成IService.java文件(位于gen/package目录下)
该接口会自动生成Stub类,继承了Binder 类,同时实现了IService接口。Stub是AIDL自动生成一个实现AIDL接口的专门用于进程间通信的中间人(IBinder)类。
public interface IService extends android.os.IInterface{
public static abstract class Stub extends android.os.Binder implements com.itheima.remoteservice.IService{
private static final java.lang.String DESCRIPTION = "com.itheima.remoteservice.IService";
public Stub(){
this.attachInterface(this,DESCRIPTION);
}
}
// 将IBinder 转换为 Iservice 类型
// 可用于生成一个中间人
public static com.itheima.remoteservice.Iservice asInterface(android.os.IBinder obj){
if(obj == null){return null;}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTION);
if((iin!=null)&&(iin instanceof com.itheima.remoteservice.IService)){
return (com.itheima.remoteservice.IService)iin;
}
}
}
(2)服务端需要创建一个中间人实现IService接口,从而提供的服务方法;客户端需要获取IService接口从而得到中间人并调用服务方法。因此需要保证服务端客户端拥有同一个AIDL文件。AIDL规定拥有相同包名的AIDL文件为同一个ADIL文件。
在服务端和客户端下创建相同的包名,放入相同的IService.aidl,并由AIDL(系统)自动生成相同的IService.java文件(可序列化参数)
public class RemoteService extends Service{
@override
public IBinder onBind(Intent intent){
// 2. 返回定义的中间人对象
return new MyBinder();
}
// RemoteService 提供的服务方法
public void methodService(){
System.out.println("Msg From RemoteService");
}
// 1. 定义一个中间人对象 IBinder
// extends Binder implements IService = extends Stub
private class MyBinder extends Stub {
@override
public void callMethodService(){
this.methodService();
}
}
}
(2)AndroidManifest.xml 配置服务
<service android:name="com.itheima.remoteservice.RemoteService">
<intent-filter>
<action android:name="com.itheima.remoteservice"/>
intent-filter>
setvice>
总结:服务端创建一个 Service 用来监听客户端的连接请求,然后创建一个 AIDL 文件,将暴露给客户端的接口在这个 AIDL 文件中声明,最后在 Service 中实现这个 AIDL 接口即可。
public class MainActivity extends Activity{
private MyConn conn;
private IService iservce; // 中间人对象(extends Binder)
@override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 1. 调用bindService获取中间人对象
Intent intent = new Intent();
intent.setAction("com.itheima.remoteservice"); // 通过意图过滤器为服务建立关系
conn = new MyConn();
// 2. 连接服务,获取中间人对象
bindService(intent,conn,flags);
}
// 监视服务的状态
private class MyConn implements Connection{
@override
public void onServiceConnected(ComponentName name,IBinder service){
// 连接成功,通过连接服务获取中间人对象(获取中间人IBinder对象方式变化)
// Stub.asInterface(service) 将IBinder 转化为实现AIDL接口的 中间人对象(IService)
iservice = Stub.asInterface(service);
}
@override
public void onServiceDisconnected(ComponentName name){
//连接失败
}
}
public void click(View v){
try{
//3. 通过中间人对象调用相应方法
iService.callMethodService();
}catch(RemoteException e){e.printStackTrace();}
}
@override
protected void onDestroy(){
unbindService(conn);
super.onDestroy();
}
}
总结:绑定服务端的 Service ,绑定成功后,将服务端返回的 Binder 对象转成 AIDL 接口所属的类型,然后就可以调用 AIDL 中的方法了。
public interface IBookService extends IInterface {
// 继承IInterface的接口
// 包含两部分
// 1. 实现IBookService的本地实现类Stub
public abstract class Stub extends Binder implements IBookService {...}
// 2. 暴露给客户端的服务方法
List<Book> getBooks() throws RemoteException;
void addBook(Book book) throws RemoteException;
}
实现IBookService的本地实现类Stub,包含1个构造函数,2个重要方法,一个代理内部类
public abstract class Stub extends Binder implements IBookService {
private static final String DESCRIPTOR = "com.xud.ipc.server.IBookService";
public Stub() {
// 服务端获取Binder,实现IBookService方法并用键值对的形式将Stub保存到Binder
this.attachInterface(this, DESCRIPTOR);
}
public static IBookService asInterface(IBinder binder) {
// 客户端获取Binder,若为相同进程则返回对应Stub,否则返回Stub.Proxy
if (binder == null)
return null;
// Binder中查找IInterface
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof IBookService)
return (IBookService) iin;
return new Proxy(binder);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
// 客户端通过调用 remote.transcat方法后,Binder会通知服务端onTranscat接收数据并根据code执行相应方法
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSAVTION_getBooks:
data.enforceInterface(DESCRIPTOR);
List<Book> result = this.getBooks();
reply.writeNoException();
reply.writeTypedList(result);
return true;
case TRANSAVTION_addBook:
data.enforceInterface(DESCRIPTOR);
Book arg0 = null;
if (data.readInt() != 0) {
arg0 = Book.CREATOR.createFromParcel(data);
}
this.addBook(arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
public static final int TRANSAVTION_getBooks = IBinder.FIRST_CALL_TRANSACTION;
public static final int TRANSAVTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1;
public class Proxy implements IBookService {...}
}
实现IBookService的代理类Proxy
Proxy是一个典型的静态代理模式,Proxy并没有实现IBookService中的方法,而是通过remote将方法请求传递到Server进程,也即是上面的Stub类处理,而remote是一个BinderProxy,包含2部分:
public class Proxy implements IBookService {
// 代理类,客户端通过Proxy执行服务端方法
private static final String DESCRIPTOR = "com.xud.ipc.server.IBookService";
private IBinder remote;
public Proxy(IBinder remote) {
// return new Proxy(binder); remote本质上是服务端Stub的引用
this.remote = remote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public IBinder asBinder() {
return remote;
}
// 实现IBookService接口的方法
// 1. 将数据进行序列化
// 2. 并调用服务端的方法remote.transact(Stub.TRANSAVTION_getBooks, data, replay, 0);
@Override
public List<Book> getBooks() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
List<Book> result;
try {
data.writeInterfaceToken(DESCRIPTOR);
remote.transact(Stub.TRANSAVTION_getBooks, data, replay, 0);
replay.readException();
result = replay.createTypedArrayList(Book.CREATOR);
} finally {
replay.recycle();
data.recycle();
}
return result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (book != null) {
data.writeInt(1);
book.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0);
replay.readException();
} finally {
replay.recycle();
data.recycle();
}
}
}
private IBookManager.Stub mbinder = new IBookManager.Stub() {
@Override
public void addBook(Book book) throws RemoteException {
//添加书本
if (!mBookList.contains(book)) {
mBookList.add(book);
}
}
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
};
Binder中两个关键方法:
// Binder具有被跨进程传输的能力是因为它实现了IBinder接口。系统会为每个实现了该接口的对象提供跨进程传输
public class Binder implement IBinder {
// Binder具有的完成特定任务的能力是通过它的IInterface的对象获得的
// IInterface 实现接口方法的binder
// attachInterface方法会将(descriptor,plus)作为(key,value)对存入Binder对象中的一个Map对象中,Binder对象可通过attachInterface方法持有一个IInterface对象(即plus)的引用,并依靠它获得完成特定任务的能力。
void attachInterface(IInterface plus, String descriptor)
// 从IBinder中继承而来
// 根据key值(即参数 descriptor)查找相应的IInterface对象
IInterface queryLocalInterface(Stringdescriptor)
}
在服务端进程,通过实现private IBookManager.Stub mbinder = new IBookManager.Stub() {}抽象类,获得Binder对象。 并保存了IInterface对象。
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
MyClient.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);
然后通过Binder对象获得IInterface对象。
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
//通过服务端onBind方法返回的binder对象得到IBookManager的实例,得到实例就可以调用它的方法了
mIBookManager = IBookManager.Stub.asInterface(binder);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mIBookManager = null;
}
};
其中asInterface(binder)方法如下:
public static com.lvr.aidldemo.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.lvr.aidldemo.IBookManager))) {
return ((com.lvr.aidldemo.IBookManager) iin);
}
return new com.lvr.aidldemo.IBookManager.Stub.Proxy(obj);
}
先通过queryLocalInterface(DESCRIPTOR);查找到对应的IInterface对象,然后判断对象的类型,如果是同一个进程调用则返回IBookManager对象,由于是跨进程调用则返回Proxy对象,即Binder类的代理对象。
(2)客户端调用服务端方法
实现IBookService的代理类Proxy,Proxy是一个典型的静态代理模式,Proxy并没有实现IBookService中的方法,而是通过remote将方法请求传递到Server进程,交给Stub类处理。
public class Proxy implements IBookService {
private static final String DESCRIPTOR = "com.xud.ipc.server.IBookService";
private IBinder remote;
public Proxy(IBinder remote) {
this.remote = remote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public IBinder asBinder() {
return remote;
}
@Override
public List<Book> getBooks() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
List<Book> result;
try {
data.writeInterfaceToken(DESCRIPTOR);
remote.transact(Stub.TRANSAVTION_getBooks, data, replay, 0);
replay.readException();
result = replay.createTypedArrayList(Book.CREATOR);
} finally {
replay.recycle();
data.recycle();
}
return result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (book != null) {
data.writeInt(1);
book.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0);
replay.readException();
} finally {
replay.recycle();
data.recycle();
}
}
}
客户端获得了Binder类的代理对象Proxy,并且通过代理对象获得了IInterface对象,那么就可以调用接口的具体实现方法了,来实现调用服务端方法的目的。
以addBook方法为例,调用该方法后,客户端线程挂起,等待唤醒:
@Override public void addBook(com.lvr.aidldemo.Book book) throws android.os.RemoteException
{
..........
//第一个参数:识别调用哪一个方法的ID
//第二个参数:Book的序列化传入数据
//第三个参数:调用方法后返回的数据
//最后一个不用管
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
..........
}
省略部分主要完成对添加的Book对象进行序列化工作,然后调用transact方法。
Proxy对象中的transact调用发生后,会引起系统的注意,系统意识到Proxy对象想找它的真身Binder对象(系统其实一直存着Binder和Proxy的对应关系)。于是系统将这个请求中的数据转发给Binder对象,Binder对象将会在onTransact中收到Proxy对象传来的数据,于是它从data中取出客户端进程传来的数据,又根据第一个参数确定想让它执行添加书本操作,于是它就执行了服务端相应操作,并把结果写回reply。代码概略如下:
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.lvr.aidldemo.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.lvr.aidldemo.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
//这里调用服务端实现的addBook方法
this.addBook(_arg0);
reply.writeNoException();
return true;
}
然后在transact方法获得_reply并返回结果,本例中的addList方法没有返回值。
客户端线程被唤醒。因此调用服务端方法时,应开启子线程,防止UI线程堵塞,导致ANR。
因此对于Binder跨进程通信过程,可以总结为: