android主线程ui
⑴ Android UI线程
思考:
先必须了解下面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 (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 在同一个线程 也就是主线程。
好了 通过上面的讲解,上面的问题相信你可以自己回答啦~
⑵ Android:在一个非主线程内直接调用UI线程的Handler实例,这样没问题吗
在Android开发中,我们常常遇到线程安全的问题,特别是在子线程和UI线程之间进行交互时。为了保证应用程序的稳定性和用户体验,我们不能直接在子线程中更新UI线程中的UI元素。为了解决这个问题,Android提供了一种机制——Handler。
Handler的工作原理是这样的:当子线程需要更新UI线程中的UI元素时,它会通过发送消息的方式,将需要更新的内容传递给UI线程。这些消息会被放入UI线程的消息队列中,然后由UI线程中的Handler逐个处理。这样,我们就可以在子线程中执行耗时操作,同时在UI线程中更新UI,从而保证了界面的流畅性。
在Android中,创建多线程的方式主要有两种:一种是通过继承Thread类并重写run方法;另一种是通过实现Runnable接口并实现run方法。无论哪种方式,子线程都无法直接修改UI线程中的UI元素,而Handler正是用来解决这一问题的关键。
Handler的主要方法包括post、postAtTime、postDelayed、sendEmptyMessage、sendMessage、sendMessageAtTime、sendMessageDelayed等。这些方法分别用于在主线程中执行Runnable或发送消息。通过这些方法,我们可以灵活地控制消息的发送时机和执行方式。
下面,我们通过一个简单的例子来说明Handler的使用方法。假设我们需要在主线程中的TextView中显示10到100之间的随机数,每隔5秒更新一次,总共更新5次。主要代码如下:
java
int i = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler.post(run);
}
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
String s = String.valueOf(msg.what);
TextView tv = (TextView)findViewById(R.id.textView);
tv.setText(tv.getText() + " " + s);
}
};
Runnable run = new Runnable(){
@Override
public void run(){
Random r = new Random();
int rnum = r.nextInt((100 - 10) + 1) + 10;
handler.sendEmptyMessage(rnum);
handler.postDelayed(run, 5000);
i++;
if (i==5){
handler.removeCallbacks(run);
}
}
};
通过这个例子,我们可以看到Handler在处理子线程与UI线程之间的交互时的重要作用。在实际开发中,我们可以根据具体需求,灵活地使用Handler的各种方法来实现复杂的线程交互逻辑。
⑶ android里面所说的looper是什么意思啊
Looper即:有消息循环的线程。
在Android里线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新概念。主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,引入一个新的机制Handle,有消息循环,就要往消息循环里 面发送相应的消息,自定义消息一般都会有对应的处理,消息的发送和清除,消息的处理,把这些都封装在Handle里面,注意Handle只是针对那些有Looper的线程,不管是UI线程还是子线程,只要有Looper,就可以往消息队列里面添加东西,并做相应的处理。