Handler的一些思考
Handler大家应该都很熟悉而且平时也会经常使用到。
为什么不能在子线程创建Handler
public Handler() {this(null, false);}public Handler(@Nullable Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}mLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}
private void testMethod() {new Thread(new Runnable() {@Overridepublic void run() {Handler handler = new Handler();}}).start();}
运行发现会报错:
java.lang.RuntimeException: Can’t create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
public static @Nullable Looper myLooper() {return sThreadLocal.get();}
调用了ThreadLocal.get(),返回当前线程关联的Looper对象
如果Looper为空 就会抛出异常 :
Can’t create handler inside thread Thread[Thread-2,5,main] that has not called Looper.prepare()
为什么主线程创建 Handler()不会报错
public static void main(String[] args) {···Looper.prepareMainLooper();Looper.loop();···}public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}
Handler创建需要Looper对象,否则会抛出异常,默认获取当前线程的Looper
主线程也就是ActivityThread会自动创建Looper,其它线程如果需要Looper需要手动创建
为什么只能在主线程中操作UI?为什么子线程中TextView setText不报错?
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//Density.setDensity(getApplication(), this);setContentView(R.layout.activity_main);tv = findViewById(R.id.text);new Thread(new Runnable() {@Overridepublic void run() {tv.setText("我瞅瞅你~");}}).start();}
给子线程休眠2s,程序会直接闪退
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
- 分析源码
public final void setText(CharSequence text) {setText(text, mBufferType);}public void setText(CharSequence text, BufferType type) {setText(text, type, true, 0);if (mCharWrapper != null) {mCharWrapper.mChars = null;}}private void setText(CharSequence text, BufferType type,boolean notifyBefore, int oldlen) {//省略...if (mLayout != null) {checkForRelayout();}//省略...private void checkForRelayout() {//省略...if(){requestLayout();invalidate();} else {nullLayouts();requestLayout();invalidate();}}
public void requestLayout() {//省略...ViewRootImpl viewRoot = getViewRootImpl();if (viewRoot != null && viewRoot.isInLayout()) {if (!viewRoot.requestLayoutDuringLayout(this)) {return;}}//省略...if (mParent != null && !mParent.isLayoutRequested()) {mParent.requestLayout();}//省略...}
ViewParent mParent;
ViewRootImpl implements ViewParent
获取ViewRootImpl ,mParent是个接口
ViewRootImpl 实现mParent接口
- 继续跟进ViewRootImpl requestLayout方法
@Overridepublic void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {checkThread();mLayoutRequested = true;scheduleTraversals();}}
checkThread()方法,见名知意应该是用来检查线程
void checkThread() {if (mThread != Thread.currentThread()) {throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");}}
ViewRootImpl.checkThread()检验线程是否是View的创建线程
mThread线程为主线程,Thread.currentThread()即我们运行的子线程
如果当前更新ui不在主线程,就会导致CalledFromWrongThreadException异常
- 在子线程中 setText 执行的速度要比主线程创建 ViewRootImpl 对象的速度要快,那么就不会执行到 ViewRootImpl 的 checkThread 的方法,就不会抛出异常
在setText方法之前加上 sleep,让ViewRootImpl 对象先创建就会检查线程抛出异常
Handler的一些思考相关推荐
- Go后台项目架构思考与重构 | 深度长文
作者 | 腾讯云后台工程师黄雷 编辑 | 唐小引 来源 | CSDN(ID:CSDNnews) 引言 本文首先介绍了架构的重要性,随后从一个实际项目的重构过程作为主线,逐步引出主流的架构设计思想以及其 ...
- python 记录日志到日志服务器_Python日志模块的使用与思考:服务器程序将每日日志写入每日日志文件,logging,及,把,每天,到,当天,中...
需求: 一个Python服务器程序,可能会连续运行几个月,现在需要把每天产生的log信息写入到当天的文件中,即每天产生一个log文件. 使用logging模块编写程序,第一个版本如下: import ...
- java postdelayed_你真的懂Handler.postDelayed()的原理吗?
转载自http://www.dss886.com/2016/08/17/01/ 阅读之前先问大家一个问题:Handler.postDelayed()是先delay一定的时间,然后再放入messageQ ...
- Handler为什么可能会造成内存泄漏以及可用的四种解决方法
在Android系统中,Handler是一个消息发送和处理机制的核心组件之一,与之配套的其他主要组件还有Looper和Message,MessageQueue. 根据官网的描述 There are t ...
- 项目问题思考之策略模式
2019独角兽企业重金招聘Python工程师标准>>> 需求: 项目中一个模块需要支持对图片,PDF,文本文件的操作,需要读取他们的内容在前台显示,显示缩略图,保存他们的内容到后台, ...
- React Bind Handle的思考
文章来自我个人的Github 在平时的开发里面,总会碰到handle绑定的问题.如果你和我一样懒或者思考过,你会觉得这个过程实在是太烦了吧.这里记录一下我的思路和历程. 这里以一个按钮的点击事件来做示 ...
- Asp.Net 构架(Http Handler 介绍) - Part.2
Asp.Net 构架(Http Handler 介绍) - Part.2 引言 在 Part.1 Http请求处理流程 一文中,我们了解了Http请求的处理过程以及其它一些运作原理.我们知道Http管 ...
- ctf php 流量分析题,GKCTF EZWEB的分析题解和思考
GKCTF EZ三剑客-EzWeb 看到这个题前端和我自己出的一个题实在是很像,同样是输入一个url,先看题目长啥样吧. 嗯,啥也没有,输入url基本没反应,F12给的提示是?secret,输入后发现 ...
- 安卓开发之Handler、HandlerThread学习篇
安卓开发之Handler.HandlerThread学习心得篇 开篇说明:本文采用的都是最基础最简单的例子,目的只有一个:希望大家将学习的焦点放在Handler的理解和使用上,我不 ...
最新文章
- KBEngine服务器环境搭建
- 一篇文章,带你了解 “机器学习工程师” 必备技能图谱
- linux源码安装nginx
- AT2705 Yes or No(组合数学)
- mXSS攻击的成因及常见种类
- 外部编辑Infopath的表单模板(xsn)
- 【科普】boy and girl,你是不是对算法工程师有误解
- ManulResetEvent与AutoResetEvent
- const 常量_var,let,const 的区别?
- typora 公式对齐_都0202年了还不会用 Typora——看Typora使用教程这篇就够了
- 黄聪:Destoon中循环嵌套Loop和php代码结合调用自增长数字
- 第 5 章 结合javabean实现CRUD
- uni-app uniCloud 前端操作数据库 模糊查询
- 计算机本地用户和组winx,计算机(Winx系统)实用操作手册.doc
- Android开发--MVP demo+Jsoup在线小说阅读器(一)
- jrtplib 编译安装配置
- Mac上运行matlab2019b卡顿
- 聪明人做生意,打折也能玩出新鲜感!让顾客觉得现在不买就是亏!
- JAVA要不要看源码_为什么要看源码、如何看源码,高手进阶必看
- 优秀的程序员——用批判性思维批判下