新网创想网站建设,新征程启航
为企业提供网站建设、域名注册、服务器等服务
思考:
合阳ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为成都创新互联的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:028-86922220(备注:SSL证书合作)期待与您的合作!
先必须了解下面2个问题
1.顾名思义 UI线程 就是刷新UI 所在线程
2.UI是单线程刷新
1.对Activity 来说 UI线程就是其主线程
2.对View来说 UI线程就是创建ViewRootImpl所在的线程
可以通过 WindowManager 内部会创建ViewRootImpl对象
好了,进入主题。我们来慢慢揭开面纱。
我们可以分别从几个方面切入
我们可能都有使用过 runOnUiThread 现在来看看的源码实现。
可以从上面的源码 看到
不是UI线程 就用Handler切到Handler所在的线程中,如果是UI线程直接就调用run方法。
Activity的创建:
1.Activity创建:mInstrumentation.newActivity
2.创建Context :ContextImpl appContextcreateBaseContextForActivity(r)
我们经常用这个方法干的事情就是,要么在onCreate中获取View宽高的值。要么就是在子线程中做一些耗时操作 ,然后post切到对应View所在的线程 来绘制UI操作。那么这个对应的线程就是UI线程了。
那么这个UI线程就一定是主线程吗?
接来继续来看。它的源码View:post
mAttachInfo 在dispatchAttachedToWindow 中被赋值 ,也就是在ViewRootImpl创建的时候,所以是创建ViewRootImpl所在的线程。
attachInfo 上面时候为null 呢?在ViewRootImpl 还没来得及创建的时候,ViewRootImpl 创建是在 “onResume" 之后。所以在 Activity 的 onCreate 去View.post 那么AttachInfo 是为null 。
当 AttachInfo == null 那么会调用 getRunQueue().post(action) 。
最终这个Runnable 被 缓存到 HandlerActionQueue 中。
直到ViewRootImpl 的 performTraversals 中 调用dispatchAttachedToWindow(mAttachInfo, 0);, 那么才会去处理 RunQueue() 中的Runnable。
来张图 便于理解这个流程
我们有时候去子线程操作UI的时候(如:requestLayout),会很经常见到下面的 报错日志:
Only the original thread that created a view hierarchy can touch its views
为什么会报这个错误呢?
翻译一下:只有创建视图层次结构的原始线程才能接触到它的视图。
也就是操作UI的线程要和ViewRootImpl创建的线程是同一个线程才行,并不是只有主线程才能更新UI啊。
ViewRootImpl创建的线程?那么 ViewRootImpl 在哪里被创建的呢?
从上图可以看到ViewRootImpl创建最开始是从 ActivityThread 的HandleResumeActivity中开始 一直 ViewRootImpl 创建,也就是说ViewRootImpl 对应的UI线程和 ActivityThread 在同一个线程 也就是主线程。
好了 通过上面的讲解,上面的问题相信你可以自己回答啦~
直接在UI线程中开启子线程来更新TextView显示的内容,运行程序我们会发现,如下错误:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.翻译过来就是:只有创建这个控件的线程才能去更新该控件的内容。
所有的UI线程要去负责View的创建并且维护它,例如更新冒个TextView的显示,都必须在主线程中去做,我们不能直接在UI线程中去创建子线程,要利用消息机制:handler,如下就是handler的简单工作原理图:
既然android给我们提供了Handler机制来解决这样的问题,请看如下代码:
public class HandlerTestActivity extends Activity { private TextView tv; private static final int UPDATE = 0; private Handler handler = new Handler() { @Overridepublic void handleMessage(Message msg) { // TODO 接收消息并且去更新UI线程上的控件内容if (msg.what == UPDATE) { // Bundle b = msg.getData();// tv.setText(b.getString("num")); tv.setText(String.valueOf(msg.obj)); } super.handleMessage(msg); } }; /** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tv = (TextView) findViewById(R.id.tv); new Thread() { @Overridepublic void run() { // TODO 子线程中通过handler发送消息给handler接收,由handler去更新TextView的值try { for (int i = 0; i 100; i++) { Thread.sleep(500); Message msg = new Message(); msg.what = UPDATE; // Bundle b = new Bundle();// b.putString("num", "更新后的值:" + i);// msg.setData(b); msg.obj = "更新后的值:" + i; handler.sendMessage(msg); } } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}
我们就通过Handler机制来处理了子线程去更新UI线程控件问题,Andrid开发中要经常用到这种机制。
UI线程,即主线程。为何将UI线程和其他事物线程,是由于其他事务处理会需要更多的时间去加载,也就是所谓的耗时操作,这样不会导致页面加载需要很久(至少画面出来了,数据可以略慢)。设计UI线程是为了使得程序能够独立完成界面加载不受其他线程影响速度。