new setnewhandler 0和new setnewhandler 0有什么区别

android中的Handler机制原理解析
通过一个独立线程下载图片,主线程中更新UI,在主线程中更新imageView显示的例子,解析Handler的原理和机制。功能需求从网络上下载一张图片,将其显示到ImageView上。
设计思路考虑到需要从网络下载图片,下载操作较耗时,不能放在主线程操作,因此设计一个线程去执行下载动作,下载完成后再更新图片。代码编写如下:public class TestImgActivity extends Activity
protected static final String TAG = "TestActivity";
protected static final int MSG_LOAD_IMAGE = 1;
private String URL ="http://img5./it/u=&fm=23&gp=0.jpg";
private Button mTestB
private ImageView mImgV
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_img);
mImgView = (ImageView) findViewById(R.id.test_img);
mTestBtn = (Button) findViewById(R.id.test_btn);
mTestBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
new Thread(new LoadImageRunnable(mHandler, MSG_LOAD_IMAGE, URL)).start();
public class LoadImageRunnable implements Runnable {
private int mThreadI
private Handler mH
private String sU
public LoadImageRunnable(Handler h, int id, String str) {
mHandler =
mThreadId =
public void run() {
Log.i(TAG, "LoadImageRunnable-----"+Thread.currentThread().getName());
Bitmap bmp = loadImageFromNetwork();
refreshImageView(bmp);
/**更新imageview图片显示*/
private void refreshImageView(Bitmap bmp) {
if (bmp!=null) {
mImgView.setImageBitmap(bmp);
/**从外部链接加载图片*/
private Bitmap loadImageFromNetwork() {
Bitmap bmp = null;
URL url = new URL(sUrl);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
bmp = BitmapFactory.decodeStream(is);
} catch (MalformedURLException e){
e.printStackTrace();
mHandler.sendEmptyMessage(10+mThreadId);
} catch(IOException e) {
mHandler.sendEmptyMessage(10+mThreadId);
e.printStackTrace();
}实际跑一下,crash发生了。log如下:
查看异常代码堆栈,发现是CalledFromWrongThreadException,即只有在UI主线程才能操作View控件! 想起Handler来了(Handler可以更新UI),修改代码,下载完成后,向Handler发送一条消息,在handler的消息处理方法中进行ImageView更新。
/**更新imageview图片显示*/
private void refreshImageView(Bitmap bmp) {
mHandler.obtainMessage(mThreadId, bmp).sendToTarget();
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
switch (msg.what) {
case MSG_LOAD_IMAGE:
if (msg.obj instanceof Bitmap) {
Bitmap bmp = (Bitmap) msg.
mImgView.setImageBitmap(bmp);
Log.i(TAG, "refresh image bitmap.");
};再运行一遍,还真是可以正常更新图片了。日志如下: 概述Handler可以用在UI主线程接收工作线程传递过来的消息,执行更新UI操作。其示意图如下:由此例子,引发以下问题:1. Handler的作用是什么
Handler在主线程上维护一个消息队列,并可以接收其他线程发送过来的消息,在主线程上依次处理消息(通常是更新UI)。一般情况下,在主线程中创建Handler,并在某事件触发时创建新的线程用于完成耗时的操作,当子线程中的工作完成之后,向Handler发送一个消息,在Handler中处理该消息,执行更新UI的操作。当然,Handler提供的是一种线程间通信的方式,不仅限于更新UI。
2. Handler是否意味着主线程?
虽然通常使用Handler来更新UI,但Handler并不等同于主线程。
3. Handler.post(Runnable r)中r的执行是在哪个线程?
此方法中的参数r是一个Runnable类型,那么是否其作用是开启一个独立的线程来执行r呢?
此方法的作用只是将一个操作封装到一个runnable对象中,其执行仍然是在Handler所属线程,而没有开辟一个独立的线程来执行。4. 是否任意线程中都能使用Handler机制,有何注意事项?
实际上,可以在任意线程Thread上使用Handler(通过handler接收其他线程发送过来的消息)。当然,需要注意其使用规范:
Looper.prepare();
Handler mHandler = new Handler();
Looper.loop();5. 一个线程中能否拥有多个Handler?
一个线程中可以创建多个handler,它们互相独立,但引用的是该线程上的同一个MessageQueue和Looper,消息队列中的各个消息根据其对应的时间顺序,依次被处理。
Looper.prepare();
Handler mHandler1 = new Handler();
Handler mHandler2 = new Handler();
Looper.loop();详细分析带着以上问题,通过阅读分析Handler及其相关类的源码来解析Handler的工作原理。首先分析Handler及其相关的类图。再看看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));
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}从Looper类的static(全局)变量sThreadLocal中取出此线程对应的looper,若存在,说明此线程已经创建过looper,抛异常;否则,新建一个looper对象(同时创建一个MessageQueue,并将该线程的引用赋值给mThread),并将该looper对象设置进入sThreadLocal。
由此可以看出,一个线程只能对应一个looper,所有线程上创建的looper都记录在一个静态变量sThreadLocal中,可以简单将sThreadLocal看成是一张looper登记表。其类ThreadLocal在java.lang包里面,此类在android中是对之前的JDK中的版本改良过的。主要提供两个方法,get()和set(),对外透明,可以直接存储进入和从中取出两个操作。对于同一个线程的looper,取出的对象和存入的是同一个。
深入进去看源码,可以发现ThreadLocal类中定义了一个内部类Values,其中持有一个Object[] table,仔细分析代码,发现这个ThreadLocal类并没有持有Values实例,而是在线程Thread类中持有该对象。在ThreadLocal的get方法中,首先取得当前线程对象currentThread,然后从currentThread中取得其Values对象,再从Values中的table来查找当前线程的looper。再来看看Handler的构造方法。
public Handler() {
this(null, false);
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();
//获取到自己的looper(当前线程所对应的looper)
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
mQueue = mLooper.mQ
//将looper的MessageQueue引用过来
mCallback =
mAsynchronous = async;
}Handler的成员mLooper 是直接引用的自己的looper (当前线程所对应的looper)。Looper类的myLooper方法如下:
public static Looper myLooper() {
return sThreadLocal.get();
}最终还是从ThreadLocal类的静态变量sThreadLocal中获取looper。贴上ThreadLocal的set和get两个方法:ThreadLocal.java
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.
int index = hash & values.
if (this.reference == table[index]) {
return (T) table[index + 1];
values = initializeValues(currentThread);
return (T) values.getAfterMiss(this);
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
values.put(this, value);
}ThreadLocal.Values.java
void put(ThreadLocal&?& key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash &; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] =
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.
table[index + 1] =
// Go back and replace first tombstone.
table[firstTombstone] = key.
table[firstTombstone + 1] =
tombstones--;
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}ThreadLocal类中有一个静态变量hashCounter,每个ThreadLocal对象中有一个hash值,每次利用hashCounter创建一个hash值(来标记当前线程),hashCounter值自增0x61c88647的两倍(它是一个魔数,使得下一个hash值总是跟以前产生的不同!),每次对新的线程产生一个hash值,将ThreadLocal的弱引用当作key,而线程的Values值一起存入线程Values值的表table中。下次取的时候,依据ThreadLocal来获取Values值;
这种设计很巧妙,线程中有各自的table,而把ThreadLocal实例作为key,一方面,当线程销毁时相关的线程局部变量会被销毁另一方面,这种利用数组方式存取键值对的方法,与JDK中旧的HashMap方法相比性能更高。这种设计很巧妙,线程中有各自的map,而把ThreadLocal实例作为key,这样除了当线程销毁时相关的线程局部变量被销毁之外,还让性能提升了很多。
如此,一个线程中就可以创建多个handler对象,而这些handler对象都关联同一个Looper:
附上JDK的ThreadLocal类源码,有兴趣的可以对比看一下。最后来看loop方法 顾名思义,loop()方法体里是一个无限循环,反复从消息队列MessageQueue中取出一条消息并处理,直到looper线程退出。实际执行处理消息动作的,是从消息msg的target成员执行dispathMessage,而target就是此msg所引用的Handler对象。Handler.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No L Looper.prepare() wasn't called on this thread.");
final MessageQueue queue = me.mQ
// 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.
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mL
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.recycle();
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
handleMessage(msg);
}在dispatchMessage处理消息时,msg的callback回调存在时就执行callback操作,否则执行handler的mCallback的handleMessage操作。
再进入Handler的源码看,当对handler进行发送消息时,最终都会sendMessage或是postRunnable时,会对其msg的callback赋值:postRunnable就将r赋给callback,sendMessage就将callback置空。
以下面这句代码(向mHandler发送一条消息)为例:mHandler.obtainMessage(mThreadId, bmp).sendToTarget();Handler.java
public final Message obtainMessage(int what, Object obj)
return Message.obtain(this, what, obj);
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 = mQ
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);
}Message.java
//获取一条message
public static Message obtain(Handler h, int what, Object obj) {
Message m = obtain();
m.target =
//从消息池中取出一条消息,若消息池空则新建一条消息
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sP
sPool = m.
m.next = null;
sPoolSize--;
return new Message();
public void sendToTarget() {
target.sendMessage(this);
}向Handler发送一条消息的过程为,从消息池MessageQueue中获取一条消息,将其target指向此handler,然后将此消息msg入列到消息池中(按照消息需要被执行的时间顺序)。到此,Handler的消息池创建过程已经清晰了。当有新的消息被压入到消息池中后,Looper的处理处理循环会依次处理各条消息。然则,回到最初的demo,为何可以在Activity中直接创建Handler而不必执行Looper.prepare()和Looper.loop()?这就要看Activity相关的源码了。 在Activity中有一个成员Activity.javaActivityThread mMainT而ActivityThread的主函数main中,执行了Looper相关的准备和循环操作。ActivityThread.java
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy.
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
Process.setArgV0("&pre-initialized&");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}实际上,Android应用程序的各个组件(Activity、Service、BroadcastReceiver、ContentProvider)的生命周期都是在其APP主线程内,系统在创建APP进程时其java代码执行堆栈:
NaiveStart.main()
ZygoteInit.main
ZygoteInit$MethodAndArgsCall.run
Method.Invoke method.invokeNative
ActivityThread.main()
Looper.loop()
每个应用程序都以ActivityThread.main()为入口进入到消息循环处理。对于一个进程来讲,需要这个闭合的处理框架。 Android.app.ActivityThread进程建立后,将跳入到ActivityThread的main函数开始运行,进入消息循环。
在非UI线程中使用Handler有一个很好的例子,直接调用HandlerThread类即可。HandlerThread将线程Thread和Looper结合封装起来,使得在此类线程中可以直接创建Handler并使用。HandlerThread.java
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}我们将最初的需求改一下,把1个按钮变为3个按钮,点击分别下载不同的网络图片。 如下图:
设计思路: 在UI主线程和HandlerThread下载线程中分别创建Handler,点击按钮时,UI主线程向HandlerThread发送一条消息以启动下载,下载完成后HandlerThread向UI主线程发送一条消息以更新ImageView显示。 代码如下:TestImgActivity .javapublic class TestImgActivity extends Activity implements Callback
protected static final String TAG = "TestActivity";
protected static final int MSG_LOAD_IMAGE = 1;
private String[] mURLs = new String[]{
"/uploads/item//42_4CfEy.jpeg",//高圆圆
"/forum/pic/item/e1d46c380cdb4adb7804b.jpg",//范冰冰
".cn/html/UploadPic/477.jpg"//林心如
private Button mTestBtn1;
private Button mTestBtn2;
private Button mTestBtn3;
private ImageView mImgV
private Handler mHandler = new WeakReferenceHandler(this);
static final int MSG_LOAD_IMG_FINISH = 2;
LoadImageHandlerThread mHandlerT
private OnClickListener mOnClickListener = new OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.test_btn1:
mHandlerThread.LoadImage(mURLs[0]);
Log.i(TAG, "Download gaoyuanyuan!");
case R.id.test_btn2:
mHandlerThread.LoadImage(mURLs[1]);
Log.i(TAG, "Download fanbingbing!");
case R.id.test_btn3:
mHandlerThread.LoadImage(mURLs[2]);
Log.i(TAG, "Download linxinru!");
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_img);
mImgView = (ImageView) findViewById(R.id.test_img);
mTestBtn1 = (Button) findViewById(R.id.test_btn1);
mTestBtn1.setOnClickListener(mOnClickListener);
mTestBtn2 = (Button) findViewById(R.id.test_btn2);
mTestBtn2.setOnClickListener(mOnClickListener);
mTestBtn3 = (Button) findViewById(R.id.test_btn3);
mTestBtn3.setOnClickListener(mOnClickListener);
if (null == mHandlerThread) {
mHandlerThread = new LoadImageHandlerThread(TAG, mHandler);
mHandlerThread.start();
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
switch (msg.what) {
case MSG_LOAD_IMG_FINISH:
if (msg.obj instanceof Bitmap) {
Bitmap bmp = (Bitmap) msg.
mImgView.setImageBitmap(bmp);
Log.i(TAG, "display star!");
return false;
}LoadImageHandlerThread.javapublic class LoadImageHandlerThread extends HandlerThread implements Callback {
private WeakReference&Handler& mMainUiHandlerR
private Handler mInnerH
static final int MSG_LOAD_IMG_START = 1;
private static final String TAG = "LoadImageHandlerThread";
public LoadImageHandlerThread(String name, Handler mainUiHandler) {
super(name);
mMainUiHandlerRef = new WeakReference&Handler&(mainUiHandler);
protected void onLooperPrepared() {
// TODO Auto-generated method stub
mInnerHandler = new WeakReferenceHandler(this);
super.onLooperPrepared();
public void LoadImage(String url) {
mInnerHandler.obtainMessage(MSG_LOAD_IMG_START, url).sendToTarget();
/**请求主线程更新imageview图片显示*/
private void refreshImageView(Bitmap bmp) {
Handler extHandler = mMainUiHandlerRef.get();
if (extHandler!=null) {
extHandler.obtainMessage(TestImgActivity.MSG_LOAD_IMG_FINISH, bmp).sendToTarget();
/**从外部链接加载图片*/
private Bitmap loadImageFromNetwork(String url_str) {
Bitmap bmp = null;
URL url = new java.net.URL(url_str);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
bmp = BitmapFactory.decodeStream(is);
} catch (MalformedURLException e){
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
switch (msg.what) {
case MSG_LOAD_IMG_START:
if (msg.obj instanceof String) {
String url = (String) msg.
Log.i(TAG, "Download star: "+url);
Bitmap bmp = loadImageFromNetwork(url);
if (bmp!=null) {
refreshImageView(bmp);
return true;
}运行后,功能正常,log如下: 总结Handler提供了一种安全的线程间通信的机制,广泛应用于UI主线程与工作线程之间的配合。UI主线程的handler消息处理是在主线程中被调用的,要注意不能有耗时的操作,以避免ANR;通常将耗时的操作放在非主线程中执行,完成后通过handler通知主线程更新UI。
最新教程周点击榜
微信扫一扫Android中的Handler详解以及和Thread的区别_百度知道下次自动登录
现在的位置:
& 综合 & 正文
Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别
Handler一定要在主线程实例化吗?new Handler()和new Handler(Looper.getMainLooper())的区别如果你不带参数的实例化:Handler handler = new Handler();那么这个会默认用当前线程的looper一般而言,如果你的Handler是要来刷新操作UI的,那么就需要在主线程下跑。情况:1.要刷新UI,handler要用到主线程的looper。那么在主线程 Handler handler = new Handler();,如果在其他线程,也要满足这个功能的话,要Handler handler = new Handler(Looper.getMainLooper());2.不用刷新ui,只是处理消息。 当前线程如果是主线程的话,Handler handler = new Handler();不是主线程的话,Looper.prepare(); Handler handler = new Handler();Looper.loop();或者Handler handler = new Handler(Looper.getMainLooper());若是实例化的时候用Looper.getMainLooper()就表示放到主UI线程去处理。如果不是的话,因为只有UI线程默认Loop.prepare();Loop.loop();过,其他线程需要手动调用这两个,否则会报错。
message.what,message.arg1,message.arg2,message.obj,他们在之间有什么区别呢?what就是一般用来区别消息的,比如你传进去的时候msg.what = 3;然后处理的时候判断msg.what == 3是不是成立的,是的话,表示这个消息是干嘛干嘛的(自己能区别开)至于arg1,arg2,其实也就是两个传递数据用的,两个int值,看你自己想要用它干嘛咯。如果你的数据只是简单的int值,那么用这两个,比较方便。其实这里你还少说了个,setData(Bundle),上面两个arg是传递简单int的,这个是传递复杂数据的。msg.obj呢,这个就是传递数据了,msg中能够携带对象,在handleMessage的时候,可以把这个数据取出来做处理了。不过呢,如果是同一个进程,最好用上面的setData就行了,这个一般是Messenger类来用来跨进程传递可序列化的对象的,这个比起上面的来,更消耗性能一些。
/xpxpxp2046/archive/.html
&&&&推荐文章:
【上篇】【下篇】您的举报已经提交成功,我们将尽快处理,谢谢!
大家还关注
<a href="/b/377525.html" target="_blank" class="trackEventQuestion" trackType="PC_问题详细页" trackAction="跳转" trackDes="PC_大家还关注" title="高手们请帮我看一下这个程序错在哪里 啊? main()
{int a[2][3][4]={24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
int i,j,m;
int *p,t,k;
for(i=0;i<23;i++)
{ k=*(p+i);
for(p1=p+1;p1<p1+24;p1++)
if(*p1<k); k=*p1;
if(k!=*(p+i))
{ t=*p1;*p1=*(p+i);*(p+i)=t;}
printf("\n");
for(i=0;i<2;i++)
{ for(j=0;j<3;j++)
{for(m=0;m高手们请帮我看一下这个程序错在哪里 啊?...

我要回帖

更多关于 new handler.callback 的文章

 

随机推荐