Handler: 更新UI的方法

Handler--更新UI的方法,android设计模式总结 解决办法:加上 postInvalidate() 方法。 @Override protected void onRestart() { super.onRestart(); /onRestart中开启新线程,更新UI/ Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ": " + Thread.curr 阅读详情

总是感觉 android 中 UI 更新很让人纠结!自己小结一下,算是抛砖引玉。

读这篇文章之前,假设你已经明白线程、Handler 的使用。

在文章的最后,附录一张草图,主要用于说明 Handler、Message、MessageQueue、Looper 之间的关系。

1. 在 onCreate() 方法中开启线程更新 UI

public class MasterActivity extends Activity { 
TextView tv = null; Button btn = null; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
    System.out.println(Thread.currentThread().getName() + ": " + Thread.currentThread().getId()); 
    tv = (TextView)findViewById(R.id.text); 
    btn = (Button)findViewById(R.id.btn); /*onCreate中开启新线程,更新UI。没有报错或者异常信息!*/ 
    Thread thread = new Thread(new Runnable() { 
        @Override public void run() { 
            System.out.println(Thread.currentThread().getName() + ": " + Thread.currentThread().getId()); 
            tv.setText("update UI is success!"); 
            btn.setText("update UI is success!"); 
        }
    }); 
    thread.start(); 
}  

随便折腾,不会报错或者异常!

以为开启的线程和 UI 线程(主线程)是同一个线程,但是很不幸,他们的线程id根本是风牛马不相及!

大家可以跟踪一下android 源码,这个主要是因为在加载 Activity 的时候,还没有触发检查单线程的模型(即子线程不可以更新ui)。

如果你不相信的话,可以在上面的线程里面 `while true`,那么一定会报错的。

2. 在 activity 如 onResume、onStart、反正是以 on 开头的回调方法

@Override
protected void onRestart() {
    super.onRestart(); /*onRestart中开启新线程,更新UI*/
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ": " + Thread.currentThread().getId());
            tv.setText("update UI is success!");
            btn.setText("update UI is success!");
        }
        
    });
    thread.start();
}

不好意思,按下返回按钮在启动程序,或者按 Home 键再启动程序,就这么折腾几下,就会包异常!信息如下:

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

意思是:只有主线程才可以更新 UI。

解决办法:加上 `postInvalidate()` 方法。

@Override protected void onRestart() { 
    super.onRestart(); /*onRestart中开启新线程,更新UI*/ 
    Thread thread = new Thread(new Runnable() { 
        @Override public void run() { 
            System.out.println(Thread.currentThread().getName() + ": " + Thread.currentThread().getId()); 
            tv.postInvalidate(); btn.postInvalidate(); 
            tv.setText("update UI is success!"); 
            btn.setText("update UI is success!"); 
        }
    }); 
    thread.start(); 
} 

`postInvalidate()` 方法,源码:

public void postInvalidate() { 
    postInvalidateDelayed(0); 
} 

public void postInvalidateDelayed(long delayMilliseconds) { 
    // We try only with the AttachInfo because there's no point in invalidating 
    // if we are not attached to our window 
    if (mAttachInfo != null) { 
        Message msg = Message.obtain(); 
        msg.what = AttachInfo.INVALIDATE_MSG; 
        msg.obj = this; 
        mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds); 
    }
} 

其实,是调用了 Handler 的处理消息的机制!该方法可以在子线程中直接用来更新UI。还有一个方法 invalidate (),稍候再说!

 3.  在 Button 的事件中开启线程,更新 UI

public class MasterActivity extends Activity {
    TextView tv = null; Button btn = null; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        System.out.println(Thread.currentThread().getName() + ": " + Thread.currentThread().getId()); 
        tv = (TextView)findViewById(R.id.text); 
        btn = (Button)findViewById(R.id.btn); 
        btn.setOnClickListener(new OnClickListener() { 
            @Override 
            public void onClick(View v) { 
                Thread thread = new Thread(new Runnable() { 
                    @Override public void run() { 
                    System.out.println(Thread.currentThread().getName() + ": " + Thread.currentThread().getId()); 
                    tv.setText("update UI is success!"); 
                    btn.setText("update UI is success!"); 
                }
            }); 
            thread.start(); 
        }
     }); 
} 

 Sorry,报错!即使你加上 `postInvalidate()` 方法,也会报这个错误。

 android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

 

4. 使用 Handler 结合多线程更新 UI

a. 开启一个线程,在 run 方法中通知 Handler

b. Handler 中使用 handleMessage 方法更新 UI

5. Handler 和 invalidate 方法结合多线程更新 UI

方法 `invalidate` 主要用在主线程中(即UI 线程中),不可以用于子线程。如果在子线程中需要使用 postInvalidate 方法。

sdk 的 api 有说明:

public void invalidate () Since: API Level 1 Invalidate the whole view.

If the view is visible, onDraw(Canvas) will be called at some point in the future.

This must be called from a UI thread. To call from a non-UI thread, call postInvalidate(). 

看看该方法源码:

public void invalidate() {
        if (ViewDebug.TRACE_HIERARCHY) {
            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
        }
        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
            mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
            final ViewParent p = mParent;
            final AttachInfo ai = mAttachInfo;
            if (p != null && ai != null) {
                final Rect r = ai.mTmpInvalRect;
                r.set(0, 0, mRight - mLeft, mBottom - mTop); // Don't call invalidate -- we don't want to internally scroll // our own bounds p.invalidateChild(this, r); } } } 
            }
        }
    }

`invalidate` 方法如果你直接在主线程中调用,是看不到任何更新的。需要与Handler结合!

感谢这位“雷锋”,一个不错的例子:http://disanji.net/2010/12/12/android-invalidate-ondraw/

只是被我修改了一点,加入times,看看 onDraw 到底运行多少次。

Android 在 onDraw 事件处理绘图,而 invalidate() 函数可以再一次触发 onDraw 事件,然后再一次进行绘图动作。

public class MasterActivity extends Activity {
	static int times = 1;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        setContentView( new View(null){
 
            Paint vPaint = new Paint();  //绘制样式物件
            private int i = 0;           //弧形角度
 
            @Override
            protected void onDraw (Canvas canvas) {
                super.onDraw(canvas);
                System.out.println("this run " + (times++) +" times!");
 
                // 设定绘图样式
                vPaint.setColor( 0xff00ffff ); //画笔颜色
                vPaint.setAntiAlias( true );   //反锯齿
                vPaint.setStyle( Paint.Style.STROKE );
 
                // 绘制一个弧形
                canvas.drawArc(new RectF(60, 120, 260, 320), 0, i, true, vPaint );
 
                // 弧形角度
                if( (i+=10) > 360 ) {
                    i = 0;
                }
 
                // 重绘, 再一次执行onDraw 程序
                invalidate();
            }
        });
    }
}

经过测试,发现 times 一直在++,说明 onDraw 被多次调用,并且一致在画图!

SDK 的 API 有时候让人很郁闷,无语.....关于 invalidate 的使用,还待探索。革命尚未成功,同志仍需努力!

博客更新,推荐文章:

View编程(2): invalidate()再探

View编程(3): invalidate()源码分析

附录: Handler、Message、MessageQueue、Looper 之间的关系

这里说明

1. Looper 使用无限循环取出消息,是有 android os 控制的。

2. android 线程是非安全的,即不要在子线程中更新 UI。

3. Looper 取出来的消息,handler 可以通过 what、obj 等量来区别分别获取属于自己的消息,所以推荐使用这些量。

欢迎大家关注我的个人微信公众号: ITMan,微信公众号搜索: ITManMark,或者扫描下图二维码.

无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点 这里 可以跳转到教程。

利用Handler来更新android的UI NULL 博文链接:https://rayleung.iteye.com/blog/411860 立即下载

相关推荐

Android编程实现使用handler在子线程中更新UI示例

本文实例讲述了Android编程实现使用handler在子线程中更新UI。分享给大家供大家参考,具体如下: MainActivity代码: package com.example.ui; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatA

Handler四种更新ui的四种方式

1.使用Handler消息传递机制 public class MainActivity extends Activity { private ActivityMainBinding mainBinding; Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { if(msg.what==111){

weixin_52942787的博客 405

避开模板匹配的坑:OpenCVSharp中边缘梯度算法的优化实践

本文深入探讨了OpenCVSharp中边缘梯度算法在模板匹配中的优化实践,解决了传统方法在尺度变化、角度旋转和光照差异下的局限性。通过Sobel算子提取边缘梯度特征,结合轮廓检测和多尺度匹配策略,显著提升了匹配的鲁棒性和准确性。文章还分享了并行计算、内存访问优化等实战技巧,适用于工业检测和自动化识别场景。

http9protocoller的博客 199

Android Handler使用详解之UI更新

AndroidUI界面更新的四种方式: import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.widget.TextView; public class MainActivity extends Activity { private TextView t

小旭的专栏 2093

深入解析NRF52832安全DFU Bootloader实现机制与蓝牙OTA升级流程

本文深入解析了NRF52832芯片安全DFU Bootloader的实现机制与蓝牙OTA升级流程。详细阐述了Bootloader的核心概念、双区升级策略、源码初始化流程以及蓝牙DFU服务的数据传输协议,并提供了从环境搭建、密钥生成到编译烧录、生成升级包的完整实战指南,帮助开发者构建安全可靠的无线固件更新系统。

weixin_29188043的博客 494

别再死记硬背公式了!用Unity ShaderGraph从零实现一个皮肤SSS效果(附完整节点图)

本文详细介绍了如何利用Unity ShaderGraph零代码实现皮肤次表面散射(SSS)效果,包括背光透射(BTDF)和表面散射(BSSRDF)的核心原理与节点构建方法。通过可视化工具,技术美术师和Shader初学者可以直观理解光线在皮肤组织中的散射行为,快速创建真实的皮肤材质效果,适用于游戏角色渲染等场景。

weixin_30838873的博客 595

【Android】 Handler——子线程更新UI

子线程更UI

学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗,学习只为当小狗狗 2880

量化爬虫第一步:手把手教你搞定同花顺问财的hexin-v参数(附JS逆向实战)

本文详细介绍了如何通过JS逆向技术破解同花顺问财数据接口的核心加密参数hexin-v,构建稳定可靠的量化数据采集方案。从抓包分析到Cookie Hook技术应用,再到环境补全与参数生成实现,手把手教你完成量化爬虫的第一步。文章还提供了实战注意事项与优化建议,帮助开发者规避常见问题。

weixin_42620202的博客 306

【多线程,UI更新与Handler消息异步处理机制】线程整合笔记

1.dsfsa

qq_38304672的博客 692

3D高斯溅射融合Gabor滤波器:频率感知如何提升神经渲染效率与质量

在计算机视觉与图形学领域,3D场景重建旨在从多视角图像中恢复真实世界的三维结构与外观。其核心原理是通过优化场景表示,使渲染出的二维图像与输入照片尽可能一致。传统方法在效率与细节保真度间常面临权衡,高频细节(如精细纹理、锐利边缘)的重建尤为消耗算力。频率感知技术为此提供了突破方向,它借鉴信号处理思想,将场景分解为不同空间频率的成分进行针对性建模。Gabor滤波器作为经典的频率-方向分析工具,能有效刻画纹理的局部频域特征。将Gabor的频率选择性与3D高斯溅射(3DGS)这一高效显式表示结合,构成了神经Gabo

weixin_33087827的博客 273

高性能网站设计之缓存更新的套路

看到好些人在写更新缓存数据代码时,先删除缓存,然后再更新数据库,而后续的操作会把数据再装载的缓存中。然而,这个是逻辑是错误的。试想,两个并发操作,一个是更新操作,另一个是查询操作,更新操作删除缓存后,查询操作没有命中缓存,先把老数据读出来后放到缓存中,然后更新操作更新了数据库。于是,在缓存中的数据还是老的数据,导致缓存中的数据是脏的,而且还一直这样脏下去了。 我不知道为什么这么多人用的都是这

代码技巧 1万+

Handler:更新UI的方法

总是感觉 android 中 UI 更新很让人纠结!自己小结一下,算是抛砖引玉。读这篇文章之前,假设你已经明白线程、Handler 的使用。 在文章的最后,附录一张草图,主要用于说明 Handler、Message、MessageQueue、Looper 之间的关系。 1. 在 onCreate() 方法中开启线程更新 UI [java] view pla

AlienTech for better life!~ 1148

dolphinscheduler服务注册中心源码解析(一)服务注册与发现整体设计

下一篇文章我们开始讲解dolphinscheduler基于Zookeeper实现注册中心的源码解析。

最爱嫣夜来 376

android studio 更新ui的四种方式 handler

更新ui的四种方式:getactivity.runonuithread;handler.post;handler.sendmessage;view post handler.post: public class Main3Activity extends AppCompatActivity { TextView textView; private Handler handl

sdsfs443的博客 2034

一文学会VOS2009对接网关指定走某个落地网关

介绍:  在我们日常使用VOS2009/3000的过程中,肯定会有很多落地网关与对接网关,并且某些时候还需要某些对接网关指定去走某个落地网关或者某些落地网关,从而实现比较精准的控制对接网关与落地网关的关系,下面就演示下在VOS2009/3000中如何配置某个对接网关指定走某个或者多个落地网关。 实现步骤:  VOS2009可以通过添加固定前缀的方式来指定对接网关走某个落地网关。 比如:  r1对接网关在被叫改写规则里面填写*:a*  r2对接网关在被叫改写规则里填写*:b* 1.登录VOS客户端 2.进入

非著名运维的博客 1963

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Android中更新UI的三种方式

Android中更新UI的三种方式: 1.在工作线程中更新UI 2.使用AsyncTask 3.使用Handler 本博客的界面用的同一个xml 在<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" a...

android葱的博客 8704

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Android 更新UI的两个方法

Android 更新UI的两个方法 在Android的开发过程中,常常需要适时的更新UI。Androd中的UI是在主线程中更新的。如果在主线程之外的线程中直接更新,就会出现报错并抛出异常: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hier

sunset716的专栏 2093

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Handler介绍:更新UI程序的方法

总是感觉 android 中 UI 更新很让人纠结!自己小结一下,算是抛砖引玉。读这篇文章之前,假设你已经明白线程、Handler 的使用。 在文章的最后,附录一张草图,主要用于说明 Handler、Message、MessageQueue、Looper 之间的关系。 1. 在 onCreate() 方法中开启线程更新 UI view plainprint? ...

weixin_33743661的博客 174

Android开之在非UI线程中更新UI

当在非UI线程中更新UI(程序界面)时会出现如下图所示的异常: Only the original thread that created a view hierarchy can touch its views. 那如何才能在非UI线程中更细UI呢? 方法有很多种,在这里主要介绍两种: 第一种:在需要更新UI的代码行后加Looper.prepare();与Looper.loop();两句话即可。如:

fengyuzhengfan的专栏 2395

android 更新ui的几种方式,详解android进行异步更新UI的四种方式

大家都知道由于性能要求,Android要求只能在UI线程中更新UI,要想在其他线程中更新UI,我大致总结了4种方式,欢迎补充纠正:使用Handler消息传递机制;使用AsyncTask异步任务;使用runOnUiThread(action)方法;使用Handler的post(Runnabel r)方法;下面分别使用四种方式来更新一个TextView。1.使用Handler消息传递机制package...

weixin_42566072的博客 360

Android开发笔记:使用 Handler 的 sendMessage() 方法更新 UI

这种方式主要是通过 Handler 对象发送出去了一个 Message 对象的方式更新 UI。 使用方法如下: new Thread(new Runnable() { @Override public void run() { Message message = mHandler.obtainMessage(7, "子线程中发布消息,更新主线程"); ...

IT开发的博客 1996

Android开发笔记:使用 Handler 的 post() 方法更新 UI

使用 Handler 的 post() 方法更新 UI,是通过 post 出一个 Runnable 的方式,代码如下: //使用post方法直接更新ui线程 new Thread(new Runnable() { @Override public void run() { mHandler.post(new Runnable() { @O...

IT开发的博客 937
上一篇: 微服务: 立志做个伟大的项目
下一篇: 微服务: 学习几个容易混淆的URL注解
veryitman
博客等级 码龄16年 472粉丝 310原创
评论 40
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值