Android Handler 原理初探
2016-08-09 19:36
465 查看
前言
经典使用方法
代码写法
子线程更新UI
在更新UI时为什么要用到子线程都有哪些常用的更新UI的方法
ViewpostRunnable
ActivityrunOnUIThreadRunnable
子线程如何将状态更新到UI
UI 线程
子线程
自定义Thread
通过上面两步,通常情况下,我们就可以正常的使用Handler了。但,这不是本文的目的;不是告诉你用法就行了,再说了这些东西Google一下立马就出来很多,说不定比我的还好。打算在深入一下,到底为什么可以这样做!
会用到的文件
看不论是attachInfo.mHandler.post(action)还是ViewRootImpl.getRunQueue().post(action)最后都调用的是Handler.post getRunQueue()最终的实现
第一步: prepare()
第二部: loop()
为啥不是prepareMainLooper 而是 prepare() The main looper for your application is created by the Android environment, so you should never need to call this function yourself
典型代码如下所示:
最后的运行结果显示:Button的颜色是红色的;这说明:子线程是可以更新UI的,但是过了5s之后程序挂了,这好像有说明子线程是不能更新UI的,出现了如下报错;到底是要闹哪样???
分析一下这份log我们发现,异常是在ViewRootImpl.java的checkThread中抛出的;是不是有点儿诡异?第一个Thread竟然没有checkThread,第二个Thread竟然checkTread?来来跟着我的思路,这是不是说明,第一个Thread的时候压根儿就没有ViewRootImpl,到了第二个Thread(mBtn.setBackgroundColor(Color.GREEN);)的时候ViewRootImpl给创建了!O(∩_∩)O哈哈~机智如我!!
搜了一下这方面的资料。发现还真是这样的。。
看下addView的实现
WindowManagerGlobal.java
好了。。
至于 View, Windows, WindowManager, WindowManagerImpl, View,DecorView, ViewGroup, View,ViewRoot,ViewGroup,ViewRoot,WindowManagerGlobal DecorView之间的关系,抽时间在分析。
可以参考这篇文章,很不错的。。
http://www.cnblogs.com/samchen2009/p/3364327.html
经典使用方法
代码写法
子线程更新UI
在更新UI时为什么要用到子线程都有哪些常用的更新UI的方法
ViewpostRunnable
ActivityrunOnUIThreadRunnable
子线程如何将状态更新到UI
UI 线程
子线程
前言
Android 开发过程中,遇到了冗长的耗时的操作,亦或是为了使得代码结构更加清晰,或者是要动态的更新UI。一言不合就上Handler,这里不讨论java编程时的一些多线程模型,只探讨一下Android中提供给开发者使用的Handler。经典使用方法
关于Handler/Looper/Message之间的关系,在上一篇博文 Android Handler机制初探 中有较为详细的描述。这里打算说人话,对照代码来解释一遍。代码写法
new Handler对象Handler myHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_WHAT: //do something break; } super.handleMessage(msg); } };
自定义Thread
class myThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { Message message = new Message(); message.what = MESSAGE_WHAT; myHandler.sendMessage(message); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
通过上面两步,通常情况下,我们就可以正常的使用Handler了。但,这不是本文的目的;不是告诉你用法就行了,再说了这些东西Google一下立马就出来很多,说不定比我的还好。打算在深入一下,到底为什么可以这样做!
子线程更新UI
使用Handler作为子线程更新UI可能是Handler用法中最为常用、经典的了。但是问题是为什么要这么做?为什么是我?其他人呢(其他方式)?在更新UI时为什么要用到子线程,都有哪些常用的更新UI的方法
如果不在UI线程中更新,而新开启一个线程处理逻辑,然后在子线程中更新UI线程;会面临线程安全问题,再说了,Android压根儿就不让你这么高。吧所有的操作都放在UI线程中到不是不可能,但当遇到高耗时的操作时,你能等得了?程序会崩溃的呀。。所以,还是Handler大法好。常见的更新UI的方法有如下几种Handler.post(Runnable) Handler.sendMessage() View.post(Runnable) AsyncTask Activity.runOnUiThread()
会用到的文件
//frameworks/base/core/java/android/app/ActivityThread.java //frameworks/base/core/java/android/app/Activity.java //frameworks/base/core/java/android/os/Looper.java //frameworks/base/core/java/android/os/Handler.java //frameworks/base/core/java/android/os/HandlerThread.java //frameworks/base/core/java/android/os/Message.java //frameworks/base/core/java/android/view/View.java //frameworks/base/core/java/android/view/ViewRootImpl.java //frameworks/base/core/java/android/view/WindowManagerGlobal.java
View.post(Runnable)
有关其他的方式,这儿不多说啦。主要看下View.post(Runnable)方法/** * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * * @param action The Runnable that will be executed. * * @return Returns true if the Runnable was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. * * @see #postDelayed * @see #removeCallbacks */ public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; }
看不论是attachInfo.mHandler.post(action)还是ViewRootImpl.getRunQueue().post(action)最后都调用的是Handler.post getRunQueue()最终的实现
//Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(mAttachInfo.mHandler);
Activity.runOnUIThread(Runnable)
如果是主线程,直接更新;如果不是,使用Handler更新。/** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. * * @param action the action to run on the UI thread */ public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
子线程如何将状态更新到UI
通过上一篇文章 Android Handler机制初探,我们知道Handler和Looper之间是1对1的关系。问题是,我们在自定义的代码中实现了Handler,可是Looper在哪儿?这里区分一下UI线程(主线程)、非UI线程UI 线程
这种ActivityThread.java中的main函数。看到了Looper.prepareMainLooper();和Looper.loop()了吧!不多说public static void main(String[] args) { //...... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
子线程
子线程基本上可以看做是自定义的线程;这就属于千变万化的了,你不能在指望ActivityThread帮你干活了,此时就要自己动手丰衣足食。怎么搞呢!当然是参照线程的东西了(ActivityThread中的实现)。第一步: prepare()
第二部: loop()
为啥不是prepareMainLooper 而是 prepare() The main looper for your application is created by the Android environment, so you should never need to call this function yourself
典型代码如下所示:
package cp.com.clarify; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; //import android.support.v7.app.AlertDialog; import android.graphics.Color; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.View; import android.widget.Button; public class MainActivity extends /*AppCompatActivity*/ Activity{ private final static String TAG="MainActivity"; private TestThread mTestThread; Intent intent; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intent = new Intent(getApplicationContext(), Main2Activity.class); mTestThread = new TestThread(); mTestThread.start(); mTestThread.getHandler().sendEmptyMessage(1); final Button mBtn = (Button)findViewById(R.id.button2); new Thread(new Runnable() { @Override public void run() { Log.e(TAG,""+this); Log.e(TAG,"-----------------> set toolbar to RED <------------------------"); mBtn.setBackgroundColor(Color.RED); } }).start(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(5000);//注意这里啊,1000000点伤害。。 } catch (InterruptedException e) { e.printStackTrace(); } Log.e(TAG,"-----------------> set toolbal to Green <------------------------"); Log.e(TAG,""+this); mBtn.setBackgroundColor(Color.GREEN); } }).start(); } class TestThread extends Thread { private Handler mHandler; private final Object mLock = new Object(); public void run() { Looper.prepare(); synchronized (mLock) { mHandler = new Handler(){ @Override public void handleMessage(Message msg) { //..... switch(msg.what){ case 1: Log.d(TAG,"================"); break; default: break; } } }; mLock.notifyAll(); } Looper.loop(); } public Handler getHandler() { synchronized (mLock) { if (mHandler == null) { try { mLock.wait(); } catch (InterruptedException e) { } } return mHandler; } } public void exit() { getHandler().post(new Runnable(){ public void run() { Looper.myLooper().quit(); }}); } } @Override protected void onResume() { Log.e(TAG,"-----------------> onResume <------------------------"); super.onResume(); } @Override protected void onStop() { Log.e(TAG,"-----------------> onStop <------------------------"); super.onStop(); } @Override protected void onPostCreate(Bundle savedInstanceState) { Log.e(TAG,"-----------------> onPostCreate <------------------------"); super.onPostCreate(savedInstanceState); } @Override protected void onPostResume() { Log.e(TAG,"-----------------> onPostResume <------------------------"); super.onPostResume(); } @Override protected void onDestroy() { Log.e(TAG,"-----------------> onDestroy <------------------------"); super.onDestroy(); } @Override protected void onPause() { Log.e(TAG,"-----------------> onPause <------------------------"); super.onPause(); } @Override protected void onRestart() { Log.e(TAG,"-----------------> onRestart <------------------------"); super.onRestart(); } }
最后的运行结果显示:Button的颜色是红色的;这说明:子线程是可以更新UI的,但是过了5s之后程序挂了,这好像有说明子线程是不能更新UI的,出现了如下报错;到底是要闹哪样???
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556) at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942) at android.view.ViewGroup.invalidateChild(ViewGroup.java:5084) at android.view.View.invalidateInternal(View.java:12724) at android.view.View.invalidate(View.java:12660) at android.view.View.invalidateDrawable(View.java:16805) at android.widget.TextView.invalidateDrawable(TextView.java:5408) at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385) at android.graphics.drawable.ColorDrawable.setColor(ColorDrawable.java:136) at android.view.View.setBackgroundColor(View.java:17196) at cp.com.clarify.MainActivity$3.run(MainActivity.java:76) at java.lang.Thread.run(Thread.java:818)
分析一下这份log我们发现,异常是在ViewRootImpl.java的checkThread中抛出的;是不是有点儿诡异?第一个Thread竟然没有checkThread,第二个Thread竟然checkTread?来来跟着我的思路,这是不是说明,第一个Thread的时候压根儿就没有ViewRootImpl,到了第二个Thread(mBtn.setBackgroundColor(Color.GREEN);)的时候ViewRootImpl给创建了!O(∩_∩)O哈哈~机智如我!!
搜了一下这方面的资料。发现还真是这样的。。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume{ // If we are getting ready to gc after going to the background, well // we are back active so skip it. // TODO Push resumeArgs into the activity for consideration if (r != null) { final Activity a = r.activity; if (localLOGV) Slog.v( TAG, "Resume " + r + " started activity: " + a.mStartedActivity + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished); // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); //这里。。这里 start ViewManager wm = a.getWindowManager(); //这里。。这里 end WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l);//这里。。这里。。 } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v( TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; }
看下addView的实现
WindowManagerGlobal.java
root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams);
好了。。
至于 View, Windows, WindowManager, WindowManagerImpl, View,DecorView, ViewGroup, View,ViewRoot,ViewGroup,ViewRoot,WindowManagerGlobal DecorView之间的关系,抽时间在分析。
可以参考这篇文章,很不错的。。
http://www.cnblogs.com/samchen2009/p/3364327.html
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories