android之handler机制深入解析
一、android中需要另开线程处理耗时、网络的任务,但是有必须要在UI线程中修改组件。这样做是为了:
①只能在UI线程中修改组件,避免了多线程造成组件显示混乱
②不使用加锁策略是为了提高性能,因为android中经常使用多线程。
handler就是为了解决在多个线程之间的通信问题。
二、基本使用:
package com.dqxst.first.multithread; import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.TextView; import com.dqxst.first.R; public class HandlerActivity extends Activity {
private TextView tv,msg_tv;
private Handler handler = new Handler();
final Handler msgHandler=new Handler(new Callback() {
@Override
public boolean handleMessage(Message msg) {
//!!!在这里可以进行一些操作,返回true则将msg进行拦截
return false;
}
}){
@Override
public void handleMessage(Message msg) {
switch(msg.what){
case 200:
msg_tv.setText("网络请求成功.");
break;
case 404:
msg_tv.setText("not found!");
break;
}
super.handleMessage(msg);
} };
private MyRunnable runnable = new MyRunnable(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
init(); Log.i("main", "主线程=" + Thread.currentThread().toString());
} class MyRunnable implements Runnable {
@Override
public void run() {
int time = Integer.parseInt(tv.getText().toString());
int now = time + 1;
Log.i("main", "处理UI更新线程=" + Thread.currentThread().toString());
tv.setText(now + "");
//这里是将Runnable间隔1秒重新发送到消息队列中,所以是定时操作的效果
handler.postDelayed(runnable, 1000);
} } //下面主要是在子线程中调用post将消息加入到消息队列中
public void startCallback(View view) {
new Thread() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} Log.i("main", "新线程=" + Thread.currentThread().toString()); handler.post(runnable);
}
}.start();
} //这里是移除消息中的执行体部分,就是那个Runnable
public void stopCallback(View view){
handler.removeCallbacks(runnable);
} public void msgChange(View view){
new Thread(new Runnable() {
@Override
public void run() {
Message message=Message.obtain();
if(Math.random()>0.3){
message.what=200;
}else{
message.what=404;
}
msgHandler.sendMessage(message);
}
}).start();
} private void init() {
tv = (TextView) findViewById(R.id.handler_tv);
tv.setText("0");
msg_tv=(TextView) findViewById(R.id.handler_tv_message);
}
}
handler基本使用
由上例可以看到,handler使用有两种形式:
①post/postDelayed:这时传递的是一个Runnable对象,会封装到一个Message对象中发送到消息队列中
②sendMessage:这时需要重写handler的handleMessage或者在构造时传入一个实现Callback接口的对象(该对象的方法也是前面重写的方法名一样,但是先执行,可以用来对消息进行拦截)
三、源码分析:与handler机制有关的类有Looper、MessageQueue、Message以及Handler本身。
1、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));
}
prepare
②不断的读取MessageQueue中的消息,通过调用loop(),MessageQueue是和Looper相关的,由其创建和管理。注意:这个过程是阻塞的。
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(); //这里是一个死循环,读取message
for (;;) {
Message msg = queue.next(); // might block
//!!!这里注意:通常不会执行这一句,除非使用quit退出Looper
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();
}
}
loop
2、Message:就是消息对象,一般使用都只是用来传递数据(从网络获取的)等,常用属性有
①what :int类型,用于标识不同的消息,在handler中使用
②arg1,arg2 :int类型,用于传递int型的数据
③obj :Object类型,用于传递对象
④data :Bundle类型,
创建Message有一些需要注意的点:
①使用Message.obtain()
或者Handler.obtainMessage()进行创建而不是new,可以利用公共池中对象避免重新分配空间。
②一个Message是和一个handler关联的,表现为Message中的target属性,但是通常这个属性是由系统自动赋值的,就是那个发送该消Message到MessageQueue的handler。
3、Handler:主要作用有以下三点
①关联所在线程的Looper对象和其所管理的MessageQueue对象,这是在构造函数中体现的,最终都会调用以下两个构造函数
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
} mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
} public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler构造函数
!!!注意:在构造函数中,必须有一个Looper对象与其绑定,因为handler要将Message发送到MessageQueue中,而MessageQueue是由Looper创建的。
②发送消息到上面关联的消息队列,可以使用post和sendMessage两种类型方法。可以看到他们内部的调用过程是一样的。最终都是调用MessageQueue中的方法将消息绑定到一个链表上。
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
} public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
//这是上面用到的将Runnable封装到Message中的方法
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
} public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
发送消息
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
} public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
} private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
进一步的调用过程
③在Looper.loop()的循环读取过程中,读取的Message将通过msg.target.dispatchMessage(msg);交由Handler中的dispatchMessage()进行处理。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
dispatchMessage
这里就说明了Handler处理消息有三种方式,需要说明的是:
a、msg.callback就是通过post/postDelayed或者构造函数传入的Runnable对象
b、mCallback就是通过构造函数传入的实现其内部接口的对象,该方法返回true则退出,否则向下执行
四、扩展:
0、HandlerThread:在上面的基本使用中,是创建一个子线程,然后在子线程中通过主线程中的handler实例向主线程的MessageQueue中发送消息。而该类的实现是在子线程中开启一个Looper,然后在使用时将handler与该子线程中的Looper/MessageQueue进行绑定,即可通过handler将消息发送到子线程中进行处理,避免了主线程的阻塞。通过上述的描述,该类的使用只需简单的两步即可:
①实例化一个线程对象并开启该线程:HandlerThread downThread=new HandlerThread("ThreadName");downThread.start();
②将Handler与该线程的Looper绑定:Handler handler=new Handler(downThread.getLooper());
1、在子线程中使用Handler(这里指的是在子线程中创建Handler实例来接受和Message)。通过上面的说明我们知道,Handler创建之前必须有一个该线程的Looper对象被创建。在主线程中直接使用是因为已经由系统进行创建了,此时在创建则会出错。下面是android中给出的标准使用方法:
class LooperThread extends Thread {
public Handler mHandler; public void run() {
Looper.prepare(); mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
}; Looper.loop();
}
}
LooperThread
2、上面说主线程不能再创建Looper,这是因为主线程被初始化是就是一个Looper线程,各种消息都是通过Handler机制处理的。
3、AsyncTask:异步消息处理机制,在内部封装了Handler处理流程。
①首先是android推荐的标准使用方式:创建一个子类,然后调用execute()即可
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
} protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
} protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
DownloadFilesTask
这里简要说明三个泛型参数的作用:
a、Params:就是后台执行需要的参数,例如下载时需要url
b、Progress:就是任务执行的进度,在onProgressUpdate(Progress...)中会被使用,
c、Result:就是任务完成返回的结果,在onPostExecute(Result)中进行处理。
②源码解析:首先说明,Handler机制就是在子线程中处理任务,通过Handler将需要处理的消息传递到与其绑定的Looper的消息队列中,再由Looper取出交由Handler处理。
a、任务在子线程中处理,这里就是通过线程池处理,而任务就是doInBackground()中的部分,该函数被放在线程池的工作任务之中
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
//!!!重点:doInBackground()在这里被封装
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
}; mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
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);
}
}
};
}
AsyncTask构造函数
调用execute()就是用默认线程池来进行处理任务
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
} public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
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; onPreExecute(); mWorker.mParams = params;
exec.execute(mFuture); return this;
}
线程池处理
下面是这个线程池的本身部分,但是要注意:这是默认的情况,即调用execute()时的默认处理,默认处理是指:1、通过执行器模仿单一线程池的效果,即多个任务(一个AsyncTask实例的一次execute()创建一个任务)时只能有一个在线程中执行,2、而且线程中只能有128个任务(这是由阻塞队列的大小限制的)。
其实这是升级之后的效果,我们可以通过执行executeOnExecutor(Executor exec, Params... params)而不是execute()传入定制的线程池来解决上面问题。
//这是一个执行器,负责添加任务到一个ArrayDeque队列中,并且将第一个任务分配给线程池处理。注意下面的execute是synchronized的,说明一次只能有一个线程在处理一个任务。
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive; public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
} protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
} //这是上面用到的线程池的设置
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
}; private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128); public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
执行器和线程池
可以看到构造函数的任务对象中的最后一句是postResult(result),而该方法就是向AsyncTask内部的Handler发送消息。
b、下面就到了Handler的部分,
//获取内部Handler
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
} 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:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
AsyncTask内部Handler
可以看到,这个handler处理两种类型的消息,一种是MESSAGE_POST_PROGRESS,由publishProgress发送,触发;一种是MESSAGE_POST_RESULT,由postResult发送。
//发送MESSAGE_POST_PROGRESS消息,触发onProgressUpdate()
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
//所以重写这个方法可以在主UI中更新界面
protected void onProgressUpdate(Progress... values) {
} //发送MESSAGE_POST_RESULT消息,触发finish()
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
//!!!这里的结束有两种情况,一种是被取消,一种是执行完成。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
消息的发送和处理
有关AsyncTask的使用还有一个问题就是如果在执行耗时任务时用户退出Activity,此时该任务线程可能没有执行完所以不会退出,这是可能造成内存泄露,需要在onPause中进行判断。还有其他问题参见http://droidyue.com/blog/2014/11/08/bad-smell-of-asynctask-in-android/index.html
参考:
http://blog.csdn.net/guolin_blog/article/details/11711405
android之handler机制深入解析的更多相关文章
- Android的Handler机制
Handler机制的原理 Android 的 Handler 机制(也有人叫消息机制)目的是为了跨线程通信,也就是多线程通信.之所以需 要跨线程通信是因为在 Android 中主线程通常只负责 UI ...
- [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...
- 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...
- 10分钟了解Android的Handler机制
Handler机制是Android中相当经典的异步消息机制,在Android发展的历史长河中扮演着很重要的角色,无论是我们直接面对的应用层还是FrameWork层,使用的场景还是相当的多.分析源码一探 ...
- 【转载】Android 的 Handler 机制实现原理分析
handler在安卓开发中是必须掌握的技术,但是很多人都是停留在使用阶段.使用起来很简单,就两个步骤,在主线程重写handler的handleMessage( )方法,在工作线程发送消息.但是,有没有 ...
- Android事件分发机制完全解析,带你从源码的角度彻底理解
Android事件构成 在Android中,事件主要包括点按.长按.拖拽.滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作.所有这些都构成了Android中的事件响应.总的来说,所有的事件都 ...
- android之Handler机制
简单例子开头: 网络http请求网站源码数据并显示 注意点:访问网络需要加Internet权限: android.permission.INTERNET 简单的步骤: 使用UrlConnection请 ...
- Android异步消息处理机制完全解析,带你从源码的角度彻底理解(转)
开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一 ...
- 【转】Android异步消息处理机制完全解析,带你从源码的角度彻底理解
原文网址:http://blog.csdn.net/guolin_blog/article/details/9991569 转载请注明出处:http://blog.csdn.net/guolin_bl ...
随机推荐
- mybatis的二级缓存的使用
1.引入ehcache的jar包和mybatis整合ehcache的jar包: <!-- ehchache --> <dependency> <groupId>ne ...
- 嵌入式Linux基础知识
一.构建嵌入式开发环境 1.编译bootloader并烧写到板子中---uboot, 可以自己定制bootloader and logo 2.编译file system 并烧写--内嵌APP 3.编译 ...
- Electron 问题
打包 Mac 桌面程序时报上述错误 E:\research\node\XXX>electron-packager ./ XXX --platform=darwin --arch=x64 --ic ...
- css实战笔记(一):写网页前的reset工作
reset.css是每个html必备的样式,其中有各种元素属性清零的代码. 为什么要有reset.css 让各个浏览器的CSS样式有一个统一的基准,比如清除各个浏览器为元素自带的margin.padd ...
- Android 对ScrollView滚动监听,实现美团、大众点评的购买悬浮效果
转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming),请尊重他人的辛勤劳动成果,谢谢! 随着移动互联网的快速发展,它已经和我们的生活息息相关了,在 ...
- (转载) Android Studio你不知道的调试技巧
Android Studio你不知道的调试技巧 标签: android studio 2015-12-29 16:05 2514人阅读 评论(0) 收藏 举报 分类: android(74) ...
- submile 安装,汉化,插件
/*删除以前配置文件*/ 删除以前版本sublime后,在删除以前版本的配置信息:直接在C盘 查询里面输入 Roming 然后查找里面的 sublime 文件夹,把他给删除掉 ----------- ...
- java的-D命令行参数 mvn -D参数
java的-D命令行参数 我们会用mvn启动一个应用,如下的命令行: MAVEN_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m" mvn ...
- ZBrush各种拓展功能的简单介绍
ZBrush的一些拓展功能中,比较突出的有2.5D的绘画功能,这个功能可以让ZBrush®从一个三维的雕塑软件转变成为一个二维的绘画软件.你完全可以使用ZBrush®来绘制自己喜欢的平面插画,还可以是 ...
- Server初见——python
import socketphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)phone.bind(('127.0.0.1',8080))p ...