Android Handler机制简单分析
2018-03-07 00:34
351 查看
Android Handler机制简单分析
本文一切从简,将围绕以下流程展开叙述:what?
接触Android的朋友都知道Handler机制用于多线程方面的通信,这好像是一句废话。why?
我们知道java几个具有代表性的多线程通信方法,例如:“wait”和”notify”通知机制
Java中每个类都是Oject的子类(万物皆对象,滑稽~~),也就具备Oject的”wait()”和”notify()”方法特性。简单举例说明:两个线程中,对于某类的对象a,在线程1中执行a.wait(),线程1则一直处于阻塞中,直到在线程2中执行a.notify(),线程1才被唤醒继续执行。
“synchronized”线程锁机制
多个线程共享一个变量,通过上锁( synchronized关键字 )限制线程们对该变量的访问,谁拿到锁,谁便可以对变量进行修改,待其他线程拿到锁访问该变量时,根据变量的变化作出相应的处理,以达到通信的目的。
此处省略n个字…
嗯,上述方法都是利用线程
阻塞的方式进行通信。这若在Android中使用?你得先搞清楚3个问题:
Android中多线程通信是为UI线程(主线程)+Worker线程(子线程)的交互服务的。
基于问题1,Android的UI线程不允许阻塞,否则会造成”ANR”( 想了解ANR? 传送门)
基于问题2,为避免”ANR”,Android中所有的耗时操作(如网络请求,文件读写)须在子线程中完成,并通知进度或结果给主线程用于UI更新。
综上: 既然java原生方法无法满足Android程序设计方面的要求,那只能另辟新径了。还好google比较良心,自己挖“坑”自己补,于是设计了一系列UI线程与Worker线程通信的方法,如:
activity.runOnUiThread(Runable action)(Activity类下的切换回UI线程的方法)
view.post(Runable action),view.postDelayed(Runnable action, long delayMillis)(View类下的切换回UI线程的方法)
还有本文的主角Handler机制(异步消息处理机制)等等。
how?
先来一段Demo:...... public class MainActivity extends AppCompatActivity { private static final int MSG_DOWNLOAD_TASK = 1; private TextView tv_progress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_progress = findViewById(R.id.tv_progress); } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case MSG_DOWNLOAD_TASK: int progress = (int) msg.obj; tv_progress.setText(progress + ""); break; } } }; /** * UI上的Button按钮点击事件 * 模拟执行下载任务 * * @param view */ private void download(View view) { new Thread(new Runnable() { @Override public void run() { int progress = 0; try { while (progress >= 100) { Message msg = Message.obtain(); msg.what = MSG_DOWNLOAD_TASK; msg.obj = progress; mHandler.sendMessage(msg); /** * 模拟下载进度回调中... */ Thread.sleep(1000); progress++; } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } }
上述demo便是Handler的简单用法,希望大家能看懂。为了简练代码,请忽略内存泄漏~~~
analyze
好了,知道怎么用了,接下来就得知道为什么这样写可以切换到主线程,这就麻烦了,得看源码,蛋疼!!!怎么看?直接通过demo看:
1.mHandler = new Handler() {... }
初始化Handler
来,我们来看看Handler构造方法在干嘛:>>> 下文所有源码均源于Android8.0,为了简练,只保留核心代码 <<< public Handler() { this(null, false);//走的是下面的双参构造方法 } public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper();//把当前线程中Looper对象引用交给Handler if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); //不能在此线程中创建handler,因为还没有调用过Looper.prepare() } mQueue = mLooper.mQueue;//从Looper对象取出MessageQueue对象给Handler mCallback = callback;//null值 mAsynchronous = async;//false }
上述代码,我特意把抛异常的说明翻译了一下,Excuse me?我并没有执行啊,怎么没报异常?怎么Looper.myLooper()有值的啊?
其实这并不矛盾,在同一个线程中可以创建一个或多个Handler对象,但前提必须是当前线程已创建(通过Looper.prepare()创建)并保存或已存在唯一的Looper对象(不理解没关系,不了解Looper也没有关系,下文会继续说),Android所有线程之间的通信皆如此,主线程亦然。
Android中,app运行入口是在ActivityThread类里的main函数开始的,没错,你没看错,就是java程序的入口main函数,android app也是java写的,当然也是main入口的,那么我们直接看核心源码来解释上面的疑问:
...... public final class ActivityThread { ...... public static void main(String[] args) {//app程序入口 ...... //1.其实本质还是走Looper.prepare(),见下面Looper类相关代码便知 Looper.prepareMainLooper(); ...... if (sMainThreadHandler == null) { //2.获取的是Handle子类H对象引用,在H中添加了处理各种消息的业务(不理解没关系,反正就是创建个Handler子类的对象) sMainThreadHandler = thread.getHandler(); } ...... //3.轮询消息 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } }
Looper类下相关代码:
...... public final class Looper { ...... public static void prepareMainLooper() { //带参的Looper.prepare(quitAllowed)方法 prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); //已存在Looper对象了,不要再创建了 } sMainLooper = myLooper(); } } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); //每个线程只能创建一个Looper对象,其实还是在说已存在Looper对象了,不要再创建了 } //这里创建了久违的Looper对象 sThreadLocal.set(new Looper(quitAllowed)); } --------------顺便看看Looper.prepare()在干什么-------------------- public static void prepare() { /** *本质是走上面的带参的prepare(quitAllowed)方法 *不要太在意quitAllowed参数,反正是传给Looper对象用的 */ prepare(true); } }
上面代码可以总结两点:
1.由于是app程序入口,main函数一定执行在主线程(UI线程)上,并且程序一开始就为主线程创建并保存好了Looper对象,以便为Handler子类H提供服务,既然已存在,当然不需要自行“Looper.prepare()”了。
2.Android官方已经为我们提供了Handler机制代码模版↓↓↓
逻辑代码写法流程图:
所以,可以这样归纳:主线程与子线程间通信不需要写Looper.prepare(…)和Looper.loop(),子线程与主线程以及子线程与子线程间的通信则需要。
2.Message msg = Message.obtain()....mHandler.sendMessage(msg);
子线程中通过mHandler对象发送消息
先分析Message.obtain()源码:
/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. *个人翻译:从全局池返回一个新Message实例(可能是新创建的,也可能是从全局池中c重新复用的)。 *允许我们在多数情况下避免分配(创建)过多的新对象 */ public static Message obtain() { synchronized (sPoolSync) {//同步锁访问机制 if (sPool != null) {//池不为null,复用已存在的对象 Message m = sPool;//从池中取出Message对象(很明显这个池也是Message类的对象) sPool = m.next; /** *结合上面可以知道这个池其实是由多个Message对象组成的链表结构(不知道链表?找度娘...) *每次复用的都是表头的Message对象 *表头被取走(复用)后,紧连着表头的另一个Message对象成为新的表头,以此类推 *先不要想这个链表是怎么添加Message对象的,也不要着急看Message类全部源码,因为不是本文重点 */ m.next = null;//对即将复用的表头(Message对象)进行脱链,从此自由啦! m.flags = 0; // clear in-use flag (清除“在使用中的”的标记,恢复初始状态以便复用) sPoolSize--;//复用后,链表长度减1 return m;//返回表头(复用表头) } } return new Message();//池为null时直接创建新Message对象 }
再看
new Message()构造方法源码:
/** * Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}). * 个人翻译:构造器(但是推荐的方式是调用"Message.obtain()") */ public Message() { }
嗯哼,如此简单明了的告诉你:其实我的构造方法没啥骚操作,但希望你优先使用Message.obtain()方式获取Message实例,避免铺张浪费。
不好意思,这段时间换了新工作,一直在折腾接入项目的事,然后就是CSDN编辑器不支持Mermaid语法让人头疼,一直在想办法解决这个问题。。。。
相关文章推荐
- 简单分析ThreadPoolExecutor实现的Android版和JDK版
- Android源码分析笔记--Handler机制
- 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
- 和菜鸟一起学android4.0.3源码之wifi direct的简单分析(转载)
- Android内核的简单分析
- 和菜鸟一起学android4.0.3源码之wifi direct的简单分析
- Android-Async-Http 特性简单分析
- AndroidManifest.xml简单分析
- Androidpn 简单实现及分析
- Android 内核的简单分析
- Android Framework------之Keyguard 简单分析
- 简单分析Android中添加shortcut方面的源码
- Android Launcher 3 简单分析
- Android属性动画AnimatorSet源码简单分析
- 深入浅出 - Android系统移植与平台开发(十) - led HAL简单设计案例分析
- [sipdroid]Android2.3 Sip简单分析
- Databinding简单的分析ImageView属性android:src="@{resImgId}"的调用流程
- 【多媒体】Android实现录屏直播(一)ScreenRecorder的简单分析
- android的消息处理机制简单分析——Looper,Handler,Message