本文为自己多年来在Android实战开发过程中总结归纳的一些常见问题,现在分享出来希望对初学者有所帮助。

本文出自门心叼龙的博客,转载请注明出处: https://blog.csdn.net/geduo_83/article/details/86560330

目录

1.异步消息处理线程存在的意义?

2.异步消息处理线程都会涉及到哪些类?架构图?

3.Handler实现消息的异步收发流程?

4.Handler使用场景?

5.为什么在子线程中直接实例化一个Hanlder会导致程序崩溃?而再UI主线程则不会?

6.子线程中进行UI操作的其他方法

7.什么是消息发送所导致的内存泄漏?如果解决?

8. 一个标准的异步消息处理线程应该怎么写?

9.Android UI主线程为什么要用Loop.loop死循环?

10. ActivityThread是线程吗?

11.主线程的死循环一直运行是不是特别消耗CPU资源呢?

12. 结合图说说Activity生命周期,比如暂停Activity,流程如下?

13. 消息分发的优先级?


1.异步消息处理线程存在的意义?

通过异步消息处理机制实现同一进程间不同线程间的通信,Android有大量的通过消息驱动方式来进行交互,比如Android的四剑客Activity, Service, Broadcast, ContentProvider 的启动过程的交互,都离不开消息机制,Android某种意义上也可以说成是一个以消息驱动的系统

2.异步消息处理线程都会涉及到哪些类?架构图?

  • 2.1 Message:消息实体
  • 2.2 MessageQueue:消息队列存储消息
  • 2.3 Looper:消息循环器,处理消息
  • 2.4 Handler:消息收发器

Hanlder中有Looper
Looper中有MessageQueue
MessageQueue中有Message

3.Handler实现消息的异步收发流程?

  • 3.1 消息发送【Handler.sendMessage】
public boolean sendMessageAtTime(Message msg,long uptimeMillis){boolean sent =false;MessageQueuequeue= mQueue;if(queue!= null){msg.target =this;sent = queue.enqueueMessage(msg, uptimeMillis);}else{RuntimeException e =new RuntimeException(this+" sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);}return sent;
}
  • 3.2 消息入列【MessageQueue.enqueueMessage】
final boolean enqueueMessage(Message msg,long when){if(msg.when !=0){throw new AndroidRuntimeException(msg +" This message is already in use.");}if(msg.target == null &&!mQuitAllowed){throw new RuntimeException("Main thread not allowed to quit");}synchronized (this){if(mQuiting){RuntimeException e = new RuntimeException(msg.target +" sending message to a Handler on a dead thread");Log.w("MessageQueue", e.getMessage(), e);returnfalse;}elseif(msg.target == null){mQuiting =true;}msg.when = when;Message p = mMessages;if(p == null || when ==0|| when < p.when){msg.next = p;mMessages = msg;this.notify();}else{Message prev = null;while(p != null && p.when <= when){prev = p;p = p.next;}msg.next = prev.next;prev.next = msg;this.notify();}}returntrue;
}
  • 3.3 消息循环器取消息【Loop.loop】
publicstatic final void loop(){Looper me = myLooper();MessageQueue queue= me.mQueue;while(true){Message msg =queue.next();// might blockif(msg != null){if(msg.target == null){return;}if(me.mLogging!= null) me.mLogging.println(">>>>> Dispatching to "+ msg.target +" "+ msg.callback +": "+ msg.what);msg.target.dispatchMessage(msg);if(me.mLogging!= null) me.mLogging.println("<<<<< Finished to    "+ msg.target +" "+ msg.callback);msg.recycle();}}
}
  • 3.4 消息出列【MessageQueue.next】
Message msg = queue.next();// might block
  • 3.5 消息分发【Handler.dispatchMessage】
publicvoid dispatchMessage(Message msg){if(msg.callback != null){handleCallback(msg);}else{if(mCallback != null){if(mCallback.handleMessage(msg)){return;}}handleMessage(msg);}
}
  • 3.6 消息处理【Handler.handlerMessage】
mHandler = new Handler(){publicvoid handleMessage(Message msg){// process incoming messages here}
};

4.Handler使用场景?

  • 4.1 定时器
  • 4.2 子线程向主线程发消息
  • 4.3 主线程向子线程发消息
  • 4.4 子线程向子线程发消息

5.为什么在子线程中直接实例化一个Hanlder会导致程序崩溃?而再UI主线程则不会?

因为在子线程实例化Hanlder的时候会去判断Loop为不为空,如果有空就直接抛出异常了,UI主线程中直接直接实例化Hanlder是因为,在ActivityThread的main函数中已经创建了Loop对象

public class MainActivity extends Activity {private Handler handler1;private Handler handler2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);handler1 = new Handler();new Thread(new Runnable() {@Overridepublic void run() {handler2 = new Handler();}}).start();}
}

程序崩溃了,原因如下:

public Handler(){if(FIND_POTENTIAL_LEAKS){...mLooper = Looper.myLooper();if(mLooper == null){throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = null;
}

为什么在activity中创建的Handler时,并没有创建Looper对象,而程序没有崩溃,原因如下:

public static void main(String[] args) {SamplingProfilerIntegration.start();CloseGuard.setEnabled(false);Environment.initForCurrentUser();EventLogger.setReporter(newEventLoggingReporter());Process.setArgV0("<pre-initialized>");Looper.prepareMainLooper();ActivityThread thread = new ActivityThread();thread.attach(false);if (sMainThreadHandler == null) {MainThreadHandler = thread.getHandler();}AsyncTask.init();if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

6.子线程中进行UI操作的其他方法

另外除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作,本质都是调用了Handler Post方法

  • 6.1 Handler的post()方法
  • 6.2 View的post()方法
  • 6.3 Activity的runOnUiThread()方法

7.什么是消息发送所导致的内存泄漏?如果解决?

在Activity还没有收到消息的情况下就已经关闭了Activity,此时Message中携带了Handler的引用,而Hanlder中又隐式的持有了Activity的引用,垃圾回收机制发现Activity还在被引用着,此时该Activity就不会被销毁,Activity所占用内存了就成了垃圾内存,此时就造成了内存泄漏

  • 7.1 使用清除消息进行解决
@Override
publicvoid onDestroy(){mHandler.removeMessages(MESSAGE_1);mHandler.removeCallbacks(mRunnable);Handler.removeCallbacksAndMessages(null);
}   
  • 7.2 使用弱引用解决
public class HandlerActivity2 extends Activity{private final Handler mHandler = new MyHandler(this);@Overridepublicvoid onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mHandler.sendMessageDelayed(Message.obtain(),60000);// just finish this activityfinish();}publicvoid todo(){};private static class MyHandler extends Handler{private final WeakReference<HandlerActivity2> mActivity;public MyHandler(HandlerActivity2 activity){mActivity = new WeakReference<HandlerActivity2>(activity);}@Overridepublicvoid handleMessage(Message msg){System.out.println(msg);if(mActivity.get()== null){return;}mActivity.get().todo();}}

8. 一个标准的异步消息处理线程应该怎么写?

  • 方法1:
class LooperThread extends Thread {public Handler mHandler;public void run() {Looper.prepare();mHandler = new Handler() {public void handleMessage(Message msg) {// process incoming messages here}};Looper.loop();}
}
  • 方法2:
  // Step 1: 创建并启动HandlerThread线程,内部包含LooperHandlerThread handlerThread = new HandlerThread("gityuan.com");handlerThread.start();// Step 2: 创建HandlerHandler handler = new Handler(handlerThread.getLooper()) {public void handleMessage(Message msg) {// process incoming messages here}};// Step 3: 发送消息handler.post(new Runnable() {@Overridepublic void run() {System.out.println("thread id="+Thread.currentThread().getId());}});

9.Android UI主线程为什么要用Loop.loop死循环?

这个死循环会不会卡死UI主线程?既然是死循环又怎么去处理其他事务呢?

  • 9.1 对于UI主线程绝对不希望运行一段时间就自动退出,那就需要可执行代码一直执行下去,最简单的方法就是死循环
  • 9.2 当然它不是简单的死循环,无消息会处于休眠状态,在onCreate、onStart,onResume里面有比较耗时的操作有可能会导致主线程卡死,Loop.loop并不会导致应用卡死
  • 9.3 在UI主线程会创建Binder子线程去处理其他事务,thread.attach(false);便会创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程

10. ActivityThread是线程吗?

ActivityThread实际上并非线程,不像HandlerThread类,ActivityThread并没有真正继承Thread类,只是往往运行在主线程,该人以线程的感觉,其实承载ActivityThread的主线程就是由Zygote fork而创建的进程

11.主线程的死循环一直运行是不是特别消耗CPU资源呢?

其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,详情见Android消息机制1-Handler(Java层),此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

12. 结合图说说Activity生命周期,比如暂停Activity,流程如下?

  • 12.1 线程1的AMS中调用线程2的ATP;(由于同一个进程的线程间资源共享,可以相互直接调用,但需要注意多线程并发问题)
  • 12.2 线程2通过binder传输到App进程的线程4;
  • 12.3 线程4通过handler消息机制,将暂停Activity的消息发送给主线程;
  • 12.4 主线程在looper.loop()中循环遍历消息,当收到暂停Activity的消息时,便将消息分发给ActivityThread.H.handleMessage()方法,再经过方法的调用,最后便会调用到Activity.onPause(),当onPause()处理完后,继续循环loop下去。

13. 消息分发的优先级?

  • Message的回调方法:message.callback.run(),优先级最高;
  • Handler的回调方法:Handler.mCallback.handleMessage(msg),优先级仅次于1;
  • Handler的默认方法:Handler.handleMessage(msg),优先级最低。

Android实战开发Handler机制深度解析相关推荐

  1. Handler机制原理解析(二)prepare,loop,post

    Handler机制原理解析(二)prepare,loop,post 上一篇已经介绍了Handler机制的原理,如果不熟悉可以看Handler机制原理解析(一).这一篇,介绍下Handler周边的知识点 ...

  2. Handler机制原理解析(一)

    Handler机制原理解析(一) 我们都知道,在Android中,主线程也叫UI线程是负责界面更新的,子线程或者工作线程适合做网络请求,数据库等耗时操作.如果在主线程中执行耗时操作可能引发ANR异常. ...

  3. Android实战开发——引导页面(ViewPager)篇

    Android实战开发之引导页面 文章目录 Android实战开发之引导页面 前言 一.概述 二.操作步骤 1.操作准备 2.初始化 3.适配器设置 3.圆点的滑动事件 三.总结 前言 本篇文章主要介 ...

  4. android Handler机制原理解析(一篇就够,包你形象而深刻)

    首先,我将Handler相关的原理机制形象的描述为以下情景: Handler:快递员(属于某个快递公司的职员) Message:包裹(可以放置很多东西的箱子) MessageQueue:快递分拣中心( ...

  5. 【Android 异步操作】Handler 机制 ( Android 提供的 Handler 源码解析 | Handler 构造与消息分发 | MessageQueue 消息队列相关方法 )

    文章目录 一.Handler 构造函数 二.Handler 消息分发 三.MessageQueue 消息队列相关函数 一.Handler 构造函数 一般使用 Handler 时 , 调用 Handle ...

  6. 【Android 异步操作】Handler 机制 ( MessageQueue 消息队列的阻塞机制 | Java 层机制 | native 层阻塞机制 | native 层解除阻塞机制 )

    文章目录 一.MessageQueue 的 Java 层机制 二.MessageQueue 的 native 层阻塞机制 三.MessageQueue 的 native 层解除阻塞机制 三.Messa ...

  7. Android中的Handler机制

    直接在UI线程中开启子线程来更新TextView显示的内容,运行程序我们会发现,如下错 误:android.view.ViewRoot$CalledFromWrongThreadException: ...

  8. android虚拟机加载机制,深入解析Android虚拟机

    深入解析Android虚拟机 编辑 锁定 讨论 上传视频 <深入解析Android虚拟机>是2014年由清华大学出版社出版的图书,作者是张子言. 书    名 深入解析Android虚拟机 ...

  9. 【Android NDK 开发】JNI 方法解析 ( JNIEnv *env 参数 )

    文章目录 一. JNI 方法解析 二. JNIEnv *env 参数解析 三. C 语言 环境中 JNIEnv *env 参数解析 四. C ++ 环境中 JNIEnv *env 参数解析 总结 : ...

最新文章

  1. 程序员入错行怎么办?
  2. ryu的防火墙功能 ryu.app.rest_firewall,配合mininet和open vswitch(OVS)
  3. Gesture Based TableView
  4. 比较好用的python编译器_10个最好用的在线编译工具
  5. QList模板类常用接口函数
  6. 获取笔记本的SHA1的值。
  7. Codeforces 757B - Bash's Big Day(分解因子+hashing)
  8. java list 去空字符串_从字符串列表中删除空字符串
  9. wordpress登录账号之后才能查看页面,实例
  10. Python错误篇 | UserWarning: findfont: Font family [‘SimHei‘] not found. Falling back to DejaVu Sans.
  11. Glide 圆角+居中裁剪centerCrop冲突问题
  12. 全国各省统计年鉴汇总2021
  13. 3D点云数据标注工具推荐
  14. jsp统计页面访问量和刷访问量的简单使用
  15. python自动化webdriver_轻松自动化---selenium-webdriver(python) (六)
  16. 拿棱镜门黑客软件攻击“俄版百度”,不偷情报只想装大V,FBI们被抓包了
  17. 参加孤尽老师DIY班一期的主要收获
  18. js将页面转成PDF文档
  19. NLP实操手册: 基于Transformer的深度学习架构的应用指南(综述)
  20. svn提交备注_svn 中commit时必须填写备注信息如何设置

热门文章

  1. “师创杯”山东理工大学第九届ACM程序设计竞赛 正式赛 F.校赛~校赛~【思维+规律题】
  2. oracle utl_smtp,Oracle 11g 环境下,利用utl_smtp创建发送邮件的存储过程
  3. 滤波算法 | 无迹卡尔曼滤波(UKF)算法及其MATLAB实现
  4. 【汽车配件管理系统-管理员-配件管理模块】配件管理分类
  5. EAUML日拱一卒-微信小程序实战:位置闹铃 (6)-播放音频
  6. onmouseover事件中把鼠标变成小手形状
  7. 排序---希尔排序实现和性能分析
  8. C++ 算法题题解——多重循环
  9. 论文笔记-基于代码属性图和Bi-GRU的软件脆弱性检测方法
  10. Replicator简介