移动开发中,我们要处理好主线程与子线程之间的关系,耗时的操作应安排到子线程中,避免阻塞主线程,导致ANR
异步处理技术是提高应用性能,解决主线程和子线程之间通信的关键。
异步处理技术有很多种,常见的有Thread,AsyncTask,Handler&Looper,Executor等。
Thread
线程是java的一个概念,实际是执行任务的基本单元
创建线程的两种方法
继承Thread类并重写run方法。
实现Runnable接口并实现run方法
Android中各种类型的线程本质上都是基于Linux系统的pthreads,在应用层可分为三种线程
主线程
主线程称为UI线程,随着应用启动而启动,主线程用来描述Android组件,同时刷新屏幕上的UI元素。
Android系统如果检测到非主线程更新UI组件,那么会报出CalledFromWrongThreadException异常,只有在主线程才能操作UI,是因为Android的UI工具包不是线程安全的。主线程中创建Handler会顺序执行接收到的消息,包括从其他线程发送的消息。
Binder线程
Binder线程用于不同进程间线程通信,每个进程都维护了一个线程池,用来处理其他进程中线程发送消息,这些包括系统服务,intents,ContentProviders,和service等。
应用不需要关心需要Binder线程,因为系统会优先将请求转换为只用主线程。
一个典型的需要使用Bnder线程的场景是应用提供一个给其他进程通过AIDL接口绑定的service
后台线程
在应用中显示创建的线程都是后台线程,也就是当刚创建出来时,这些线程的执行体是空的,需要手动添加任务。在Linux系统层面,主线程和后台线程是一样的。在Android框架中,通过WindowManager赋予了主线程只能处理UI更新以及后台线程不能直接操作UI的限制。
HandlerThread
HandlerThread集成了Looper和MessageQueue的线程,并启动HandlerThread时,会共同生成Looper和MessageQueue,然后等待消息进行处理,它的run方法如下
使用HandlerThread的好处是开发者不需要自己创建和维护Looper
HandlerThread handlerThread = new HandlerThread("123");
handlerThread.start();
handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Handler中只有一个消息队列,队列中消息的是顺序执行的,因此是线程安全的,吞吐量会收到一定影响,队列中的任务可能会被前面没有执行完的任务阻塞。
HandlerThread的内部机制确保创建Looper和发送消息之间不存在竞态条件,这个是通过将HandlerThread.getLooper()实现为一个阻塞操作,只有当HandlerThread准备好接受消息之后才返回。
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
//如果线程已经启动,那么looper准备好之前应先等待
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
如果具体的业务要求在HandlerThread开始接受消息之前要进行默写初始化操作,可以重写HandlerThread的onLooperPrepared函数。
intentService
intentService具有service一样的生命周期,同时也提供了后台线程中处理异步机制。
intentService在一个后台线程中顺序执行所有任务。
通过Context.startService传递一个Intent类型的参数启动intentService的异步执行。
如果此时intentService正在运行中,那么这个新的intent将会进入队列进行排队,直到后台处理完队列前面的任务;
如果此时intentService没有在运行,那么将会启动一个新的intentService,
当后台线程队列的所有任务执行完成之后,intentService将结束它的生命周期,因此intentService不需要手动结束。
intentService是通过HandlerThread来实现后台处理任务。
使用
intentService是一个抽象类,使用前需要继承它并实现onHandlerIntent方法,这个方法中实现具体的后台处理业务逻辑,同时需要在子类的构造方法中需要调用super(String name)传入子类的名字。
语法如下
public class MyIntentService extends IntentService {
public MyIntentService() {
super(MyIntentService.class.getName());
setIntentRedelivery(true);
//setIntentRedelivery设置为true,那么intentService的onStartCommand方法返回START_REDELIVER_INTENT.
//如果onHandleIntent返回之前进程死掉了,那么进程会重新启动,intent将会重新投递。
}
@Override
protected void onHandleIntent(Intent intent) {
//这个方法是在后台线程中调用
}
}
当然在AndroidManifest.xml文件中进行注册
Executor
创建和销毁对象们都是存在开销的。
如果应用中频繁的创建和销毁线程,改善应用体验。
Executor的框架定义是一个名为Executor接口定义,Executor主要目的是分离任务的创建和它的执行。
public interface Executor {
void execute(Runnable command);
}
可以自己实现Executor并重写execute直接创建线程来执行Runnable
public class MyExecutor implements Executor {
@Override
public void execute(Runnable command) {
new Thread(command).start();
}
}
预定义线程池
实际开发中execute,需要增加类似队列,任务优先级等功能,最终实现一个线程池。
ThreadPoolExecutor
预定义线程池都是基于ThreadPoolExecutor 类上创建的,通过ThreadPoolExecutor 开发者可以自定义线程池的一些线程池的一些行为。
**构造函数的五个参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
//参 1corePoolSize 核心线程池
核心线程会一直存在于线程池中,即使当前没有任务需要处理。
当线程数小于核心线程数时,即使当前有空闲的线程,线程池也会优先创建线程来处理任务。
//参2 maximumPoolSize 最大线程数
当线程数大于核心线程数,且任务队列已经满了,这时线程池会创建新的线程,直到线程数量达到最大线程数为止。
//参3 keepAliveTime 线程的空闲存活时间
当线程的空闲时间超过这个值时,线程会被销毁,直到线程数等于核心线程数。
//参4 unit keepAliveTime的单位
可选的有TimeUnit.NANOSECONDS,MICROSECONDS,MILLISECONDS,SECONDS。
//参5 workQueue 线程池所使用的任务缓冲队列**
-放等待任务的--有效的缓存序列BlockingQueue
执行
//3. 创建自定义的线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3,//核心线程池
6,//最大运行的线程数量
1,//存活时间,指定是等待任务的存活时间
TimeUnit.HOURS,//时间单位
new LinkedBlockingQueue()//缓冲队列,用来存放那些处于等待中的 任务---一般都这样写
);
threadPoolExecutor.execute(new DownloadTask(1));
//移除线程池
//threadPoolExecutor.remove()
//glide也是多任务的
}
/**
* 创建下载任务--继承Runnable
* 下载任务,负责完成去服务器下载文件
* 它里面的作用就是http下载模块的功能
*/
class DownloadTask implements Runnable {
private int num;
public DownloadTask(int num) {
this.num = num;//这个参数是为了区分线程设立的
Log.e("tag", "线程" + num + " waitting。。。");
}
//执行耗时操作--run方法执行说明已经开始进行操作了
@Override
public void run() {
Log.e("tag", "线程" + num + "开始运行。。。");
SystemClock.sleep(5000);
Log.e("tag", "线程" + num + "over了----------");
}
}
Executor框架为开发者提供了预定义的线程实现
1:FixedThreadPool-线程数量固定的线程池。
特点:当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭。当所有线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来
创建:Executors.newFixedThreadPool(3); 参数表示线程的个数
2:CachedThreadPool缓存型池子线程数量不定的线程池
特点:它只有非核心线程,当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。空闲线程会等待60s来执行新任务
能reuse的线程,必须是timeout IDLE内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
创建:Executors.newCachedThreadPool();
3:ScheduleThreadPool - 核心线程数量是固定的
特点:核心线程固定,非核心线程没有限制,并且当非核心线程闲置时,被立即回收
创建:Executors.newScheduledThreadPool(3);
4:SingleThreadExcutor - 线程池内部只有一个核心线程
特点:它确保所有的任务都在同一个线程中按顺序执行。
创建: Executors.newSingleThreadExecutor();
AsyncTask
AsyncTask是在框架基础上进行的封装,它实现将耗时任务移到工作线程中执行,同时提供方便的接口实现工作线程和主线程通信
一个应用中使用所有AsyncTask实例会共享全局的属性,也就是说AsyncTask中的任务使串行执行,那么应用中的所有的AsyncTask都会进行排队,只有等前面的任务执行完成之后才会执行下一个AsyncTask中的任务,在mTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,"aa");或者在APILevel大于13的系统上execute都有这个效果
如果是异步执行,AsyncTask中的ThreadPoolExecutor 指定的核心线程数是CPU+1。
即:在四核CPU上,最多只有5个任务可以同时进行,其他任务需要在队列中排队,等待空闲的线程。(昨天看貌似变了)
public abstract class AsyncTask {
private static final String LOG_TAG = "AsyncTask";
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
}
使用
VirusTask mTask = new VirusTask();
// 执行任务—参数可变参数
mTask.execute("haha", "hehe");
//AsyncTask执行任务每次只能执行一次.因此再次调用要重新new对象
//定义子类继承此抽象类
// 泛型1 运行中 指定doInBackground的参数类型 也是execute方法的参数类型
// 泛型2 进度更新 onProgressUpdate的参数类型 publishProgress的参数类型
// 泛型3 结束 onPostExecute的参数类型 doInBackground的返回值类型
private class VirusTask extends AsyncTask {
//执行任务前会指定的方法 运行在UI线程做一些准备工作
@Override
protected void onPreExecute() {
super.onPreExecute();
}
//doInBackground处理耗时任务 运行在子线程(默认只重写此方法)(其他方法都在ui线程)
@Override
protected String doInBackground(String... params) {
return "aaaaa";
}
//onPostExecute任务结束后执行的方法 运行在UI线程一般用来更新UI
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
}
//onProgressUpdate更新进度 运行在UI线程
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
}
Handler消息异步处理机制
执行流程
Handler在创建时会与当前所在的线程的Looper对象相关联(如果当前线程的Looper为空或不存在,则会抛出异常。
此时需要在线程中主动调用Looper.prepare()来创建一个Looper对象)。handler通过send或post发送消息,sendmessage中调用messageQueue的enqueueMesssage将消息传入messageQueue中,Looper的loop方法调用MessageQueue的next方法返回信息,通过msg.Target.DispatchMessage来处理信息,callback中实现处理。一般会调用handler的handlermessage方法
Handler使用
方式一: post(Runnable)
创建一个工作线程,实现 Runnable 接口,实现 run 方法,处理耗时操作
new Thread(new Runnable() {
@Override
public void run() {
/**
耗时操作
*/
handler.post(new Runnable() {
@Override
public void run() {
/**
更新UI
*/
}
});
}
}).start();
创建一个 handler,通过 handler.post/postDelay,投递创建的 Runnable,在 run 方法中进行更新 UI 操作。
方式二: sendMessage(Message)
创建一个工作线程,继承 Thread,重新 run 方法,处理耗时操作
创建一个 Message 对象,设置 what 标志及数据
通过 sendMessage 进行投递消息
创建一个handler,重写 handleMessage 方法,根据 msg.what 信息判断,接收对应的信息,再在这里更新 UI。
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) { //判断标志位
case 1:
/**
获取数据,更新UI
*/
break;
}
}
};
public class WorkThread extends Thread {
@Override
public void run() {
super.run();
/**
耗时操作
*/
//从全局池中返回一个message实例,避免多次创建message(如new Message)
Message msg =Message.obtain();
msg.obj = data;
msg.what=1; //标志消息的标志
handler.sendMessage(msg);
}
}
new WorkThread().start();
Handler 存在的问题
内存方面
Handler 被作为 Activity 引用,如果为非静态内部类,则会引用外部类对象。当 Activity finish 时,Handler可能并未执行完,从而引起 Activity 的内存泄漏。故而在所有调用 Handler 的地方,都用静态内部类。
异常方面
当 Activity finish 时,在 onDestroy 方法中释放了一些资源。此时 Handler 执行到 handlerMessage 方法,但相关资源已经被释放,从而引起空指针的异常。
避免
如果是使用 handlerMessage,则在方法中加try catch。
如果是用 post 方法,则在Runnable方法中加try catch。
Handler 的改进
内存方面:使用静态内部类创建 handler 对象,且对 Activity 持有弱引用
异常方面:不加 try catch,而是在 onDestory 中把消息队列 MessageQueue 中的消息给 remove 掉。则使用如下方式创建 handler 对象:
/**
* 为避免handler造成的内存泄漏
* 1、使用静态的handler,对外部类不保持对象的引用
* 2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
*/
private static class MyHandler extends Handler {
private final WeakReference mActivityReference;
MyHandler(Activity activity) {
this.mActivityReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = (MainActivity) mActivityReference.get(); //获取弱引用队列中的activity
switch (msg.what) { //获取消息,更新UI
case 1:
byte[] data = (byte[]) msg.obj;
activity.threadIv.setImageBitmap(activity.getBitmap(data));
break;
}
}
}
并在 onDesotry 中销毁:
@Override
protected void onDestroy() {
super.onDestroy();
//避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
handler.removeCallbacks(postRunnable); //清除runnable对应的message
//handler.removeMessage(what) 清除what对应的message
}
Handler 的使用实现
耗时操作采用从网络加载一张图片
继承 Thread 或实现 Runnable 接口的线程,与 UI 线程进行分离,其中 Runnable 与主线程通过回调接口进行通信,降低耦合,提高代码复用性。
在 Activity 中创建 handler 对象,调用工作线程执行
public class MainActivity extends AppCompatActivity {
ImageView threadIv;
ImageView runnableIv;
SendThread sendThread;
PostRunnable postRunnable;
private final MyHandler handler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
threadIv = (ImageView) findViewById(R.id.thread_iv);
runnableIv = (ImageView) findViewById(R.id.runnable_iv);
sendThread = new SendThread(handler);
sendThread.start();
postRunnable = new PostRunnable(handler);
postRunnable.setRefreshUI(new PostRunnable.RefreshUI() {
@Override
public void setImage(byte[] data) {
runnableIv.setImageBitmap(getBitmap(data));
}
});
new Thread(postRunnable).start();
}
/**
为避免handler造成的内存泄漏
1、使用静态的handler,对外部类不保持对象的引用
2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
/
private static class MyHandler extends Handler {
private final WeakReference mActivityReference;
MyHandler(Activity activity) {
this.mActivityReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = (MainActivity) mActivityReference.get(); //获取弱引用队列中的activity
switch (msg.what) { //获取消息,更新UI
case 1:
byte[] data = (byte[]) msg.obj;
activity.threadIv.setImageBitmap(activity.getBitmap(data));
break;
}
}
}
private Bitmap getBitmap(byte[] data) {
return BitmapFactory.decodeByteArray(data, 0, data.length);
}
@Override
protected void onDestroy() {
super.onDestroy();
//避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
handler.removeCallbacks(postRunnable); //清除runnable对应的message
//handler.removeMessage(what) 清除what对应的message
}
}
方式一:实现 runnable 接口,通过 post(Runnable)通信,并通过给定的回调接口通知 Activity 更新
public class PostRunnable implements Runnable {
private Handler handler;
private RefreshUI refreshUI;
byte[] data = null;
public PostRunnable(Handler handler) {
this.handler = handler;
}
@Override
public void run() {
/**
* 耗时操作
*/
final Bitmap bitmap = null;
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("http://i3.17173cdn.com/2fhnvk/YWxqaGBf/cms3/FNsPLfbkmwgBgpl.jpg");
HttpResponse httpResponse = null;
try {
httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
data = EntityUtils.toByteArray(httpResponse.getEntity());
}
} catch (IOException e) {
e.printStackTrace();
}
//返回结果给UI线程
handler.post(new Runnable() {
@Override
public void run() {
refreshUI.setImage(data);
}
});
}
public interface RefreshUI {
public void setImage(byte[] data);
}
public void setRefreshUI(RefreshUI refreshUI) {
this.refreshUI = refreshUI;
}
}
方式二:继承Thread,通过handler的sendMessage通信
public class SendThread extends Thread {
private Handler handler;
public SendThread(Handler handler) {
this.handler = handler;
}
@Override
public void run() {
super.run();
/**
* 耗时操作
*/
byte[]data=null;
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet("https://d36lyudx79hk0a.cloudfront.net/p0/descr/pc27/3095587d8c4560d8.png");
HttpResponse httpResponse = null;
try {
httpResponse = httpClient.execute(httpGet);
if(httpResponse.getStatusLine().getStatusCode()==200){
data= EntityUtils.toByteArray(httpResponse.getEntity());
}
} catch (IOException e) {
e.printStackTrace();
}
//返回结果给UI线程
doTask(data);
}
/**
* 通过handler返回消息
* @param data
*/
private void doTask(byte[] data) {
Message msg =Message.obtain(); //从全局池中返回一个message实例,避免多次创建message(如new Message)
msg.obj = data;
msg.what=1; //标志消息的标志
handler.sendMessage(msg);
}
}
Handler 通信机制
创建Handler,并采用当前线程的Looper创建消息循环系统;
Handler通过sendMessage(Message)或Post(Runnable)发送消息,调用enqueueMessage把消息插入到消息链表中;
Looper循环检测消息队列中的消息,若有消息则取出该消息,并调用该消息持有的handler的dispatchMessage方法,回调到创建Handler线程中重写的handleMessage里执行。
Handler 如何关联 Looper、MessageQueue
1、Handler 发送消息
Message msg =Message.obtain();
msg.obj = data;
msg.what=1; //标志消息的标志
handler.sendMessage(msg);
从sendMessageQueue开始追踪,函数调用关系:sendMessage -> sendMessageDelayed ->sendMessageAtTime,在sendMessageAtTime中,携带者传来的message与Handler的mQueue一起通过enqueueMessage进入队列了。
对于postRunnable而言,通过post投递该runnable,调用getPostMessage,通过该runnable构造一个message,再通过 sendMessageDelayed投递,接下来和sendMessage的流程一样了。
2、消息入队列
在enqueueMessage中,通过MessageQueue入队列,并为该message的target赋值为当前的handler对象,记住msg.target很重要,之后Looper取出该消息时,还需要由msg.target.dispatchMessage回调到该handler中处理消息。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在MessageQueue中,由Message的消息链表进行入队列
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
3、Looper 处理消息
再说处理消息之前,先看Looper是如何构建与获取的:
构造Looper时,构建消息循环队列,并获取当前线程
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
但该函数是私有的,外界不能直接构造一个Looper,而是通过Looper.prepare来构造的:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
这里创建Looper,并把Looper对象保存在sThreadLocal中,那sThreadLocal是什么呢?
static final ThreadLocal sThreadLocal = new ThreadLocal();
它是一个保存Looper的TheadLocal实例,而ThreadLocal是线程私有的数据存储类,可以来保存线程的Looper对象,这样Handler就可以通过ThreadLocal来保存于获取Looper对象了。
TheadLocal 如何保存与获取Looper?
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
public T get() {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
在 set 中都是通过 values.put 保存当前线程的 Looper 实例,通过 values.getAfterMiss(this)获取,其中put和getAfterMiss都有key和value,都是由Value对象的table数组保存的,那么在table数组里怎么存的呢?
table[index] = key.reference;
table[index + 1] = value;
很显然在数组中,前一个保存着ThreadLocal对象引用的索引,后一个存储传入的Looper实例。
接下来看Looper在loop中如何处理消息
在loop中,一个循环,通过next取出MessageQueue中的消息
若取出的消息为null,则结束循环,返回。
设置消息为空,可以通过MessageQueue的quit和quitSafely方法通知消息队列退出。
若取出的消息不为空,则通过msg.target.dispatchMessage回调到handler中去。
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
Long.toHexString(ident) + " to 0x"
Long.toHexString(newIdent) + " while dispatching to "
msg.target.getClass().getName() + " "
msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
4、handler处理消息
Looper把消息回调到handler的dispatchMessage中进行消息处理:
若该消息有callback,即通过Post(Runnable)的方式投递消息,因为在投递runnable时,把runnable对象赋值给了message的callback。
若handler的mCallback不为空,则交由通过callback创建handler方式去处理。
否则,由最常见创建handler对象的方式,在重写handlerMessage中处理。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
子线程中创建handler,处理消息
public class MainActivity extends Activity {
TextView valeTv;
// 定义键值对
private String KEY = "key";
private String VALUE = "value";
// 定义一个自己的线程
class MyThread extends Thread {
public Handler mHandler;
@Override
public void run() {
System.out.println("线程开始运行");
Looper.prepare();// 在线程中必须建立一个自己的looper,不能用ui线程中的
mHandler = new Handler() {// 在新线程中创建Handler时必须创建Looper
public void handleMessage(Message msg) {
if (msg.what == 111) {
String str = msg.getData().getString(KEY);
// valeTv.setText(str);//不能更新ui
Toast.makeText(MainActivity.this, str, 0).show();
}
};
};
Looper.loop();
}
}
private MyThread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
valeTv = (TextView) findViewById(R.id.vale_textView);
thread = new MyThread();
// 启动线程
thread.start();
//sentMsg();//发在这很危险,因为发送消息的动作可能在线程执行前,这样就出错了
}
public void buttonListener(View v) {
sentMsg();//因为点击事件肯定在线程开始执行后才进行,所以这里是正确的
}
private void sentMsg() {
// 创建消息
Message msg = new Message();
msg.what = 111;
Bundle bundle = new Bundle();
bundle.putString(KEY, VALUE);
// 设置数据
msg.setData(bundle);
System.out.println("向线程发送消息");// 这句话必须在“线程开始运行”后打印才表示正确
// 发送消息,由于线程执行的时间不固定,这句话必须放在线程start后的一段时间才行。这里放在点击事件中,确保线程已经开始执行了。
thread.mHandler.sendMessage(msg);
}
}
内存泄漏
一是在Activity的onDestroy方法中调用handler.removeCallbacksAndMessages(null);取消所有的消息的处理,包括待处理的消息;
二是声明handler的内部类为static。
参考
《Android高级进阶》
http://www.jianshu.com/p/0a274564a4b1
http://www.cnblogs.com/tianzhijiexian/p/3880581.html