为什么子线程中不能直接更新UI

点击上方“dotNET全栈开发”,“设为星标”

加“星标”,每天11.50,好文必达

全文约4000字,预计阅读时间8分钟

当初有同事就碰到类似的问题,于是就总结了一些,那时写这篇文章是我还在第一家公司。今天有人提到,之前在csdn发布过,我就重新修改了一下,发到微信。两年过去了,过去的同事不再交流问题,但问题仍然出现,交流的人换了几波!有些同事换了方向,而我仍在坚持xamarin。是难得还是无奈,我也不知道。反正奥力给,干就完事了!

文中的图已经掉,该错过就错过吧,见谅!

01

主线程也叫UI线程

当一个程序启动的时候,系统自动创建一个主线程,在这个主线程中,你的应用(app、winform等客户端程序)和UI组件发生交互,负责处理UI组件的各种事件,所以主线程也叫UI线程。

02

UI组件的更新一定要在UI线程里

android为了线程安全,不允许在UI线程外的子线程操作UI,这个结论不仅仅是说android,这个概念同样适用于其他的客户端系统,它 的好处时提高客户端UI的用户体验和执行效率(稍后解释),防止线程阻塞。在Java 原生的android中有两种方式更新UI线程

  • handler消息传递机制更新UI线程

  • AsyncTask异步任务更新UI线程

AsyncTask是Android提供的一个轻量级的用于处理异步任务的类,类似于C#中的Task

03

永远不要阻塞UI线程

刚刚说了UI组件的更新一定要在UI线程中,当我们在主线程中发起请求>请求的执行(http请求耗时)>请求完成填充数据,更新UI组件。这一过程一旦超过了10秒钟就会抛出ANR异常(Application Not Responding)应用程序员不响应,所以网络请求耗时的操作大多使用异步操作,早起异步Task相对麻烦,在.net 4.5中增加了新的特性await/async,使用await/async 就简化了很多。原则上的要求就是永远不要阻塞UI线程。

我们通过下面几个简单的示例逐步地学西和掌握如何在子线程中更新UI线程

  • ANR异常

  • 使用RunOnUIThread更新UI线程

  • 异步加载图片,在子线程中更新UI线程

04

4.1 阻塞UI线程并输入事件-模拟ANR异常

下面我们创建一个简单的登录程序,登录的时候使用Thread.Sleep(10000模拟耗时10秒钟,在这10秒钟内程序没有任何响应(如果你做了输入事件比如:触摸屏幕,按返回键),通俗说法就是卡界面了。代码如下,主要是了解ANR异常。

    [Activity(Label = "LoginActivity", MainLauncher = true)]
public class LoginActivity : Activity
{
private EditText et_name;
private EditText et_pwd;
private Button btn;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Login);
et_name = FindViewById<EditText>(Resource.Id.et_name);
et_pwd = FindViewById<EditText>(Resource.Id.et_pwd);
btn = FindViewById<Button>(Resource.Id.btn);
btn.Click += (s, e) =>
{
Client.Client_Login(et_name.Text, et_pwd.Text, () =>
{
Toast.MakeText(this, "登录成功", ToastLength.Long).Show();
}, error =>
{
Toast.MakeText(this, error, ToastLength.Long).Show();
});
};
}
}
public class Client
{
/// <param name="successAction">登录成功的回调</param>
/// <param name="errorAction">登录失败的回调</param>
public static void Client_Login(string name, string pwd,Action successAction,Action<string> errorAction)
{
Thread.Sleep(6000);
if (name == "123" && pwd == "123")
{
successAction();
return;
}
errorAction("密码不正确");
}
}

4.2 ANR异常是如何产生的

ANR:Application Not Responding的缩写,当程序爆出“应用程序无响应”,系统会向用户显示一个对话框,“等待”可以让程序继续运行,“强制关闭”直接kill掉了。

这是值得注意的一点,记得上学的时候用的是酷派,虽然是充话费送的,但其实那手机配置还是可以,但是用就久了,很卡,打开一些app,长时间没有反应,我便在屏幕上点、按返回键等,于是就经常报这个“**程序没有响应”“等待”“退出”的一个对话框,好吧,其实用酷派手机卡了这些无关紧要。

在android程序中,程序的响应时由Activity manager和WindowManager系统服务监听的,主要是由以下两种情况造成的:

  • 在5秒外没有响应UI事件(点击屏幕,点击按钮,按返回键等),反之在5秒内比如Thread.sleep(5000)去点击屏幕,按返回键也不会报出ANR异常的。

  • BroadcaseReceiver在10秒内没有执行完毕

产生上面两种情况原因比较多,要注意的是即时是在UI线程中做了耗时的事情(5秒以上),如果用户没有触发屏幕的任何的事件,这时虽然UI线程阻塞了,也不会产生ANR。其实避免ANR异常原则要求还是那句话”不要再UI线程上做耗时的事情

05

使用RunOnUIThread更新UI线程

大家一定使用过Timer,Timer对象会开启多个线程,但最少不止一个。下面这个例子,将演示两个timer,1秒钟更新一次,对比一下两个TextView的显示的时间。

    [Activity(Label = "Xamarin_android", MainLauncher = true, Icon = "@drawable/icon")]
public class TimerActivity : Activity
{
private TextView tv_test;
private TextView tv_test1;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
tv_test = FindViewById<TextView>(Resource.Id.tv_test);
tv_test1 = FindViewById<TextView>(Resource.Id.tv_test1);
tv_test.Text = "现在的时间是" + DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss");
tv_test1.Text = "现在的时间是" + DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss");

System.Diagnostics.Debug.Write("主线程"+Thread.CurrentThread.ManagedThreadId);
System.Timers.Timer timer = new System.Timers.Timer(10000);
timer.Elapsed += delegate
{
System.Diagnostics.Debug.Write("timer线程"+Thread.CurrentThread.ManagedThreadId);
RunOnUiThread(()=> {
tv_test.Text = "现在的时间是" + DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss");
});
};
timer.Enabled = true;

System.Diagnostics.Debug.Write("主线程" + Thread.CurrentThread.ManagedThreadId);
System.Timers.Timer timer1 = new System.Timers.Timer(10000);
timer1.Elapsed += delegate
{
System.Diagnostics.Debug.Write("timer1线程" + Thread.CurrentThread.ManagedThreadId);
tv_test1.Text = "现在的时间是" + DateTime.Now.ToString("yyyy年MM月dd日 HH:mm:ss");
};
timer1.Enabled = true;
}
}

通过这段代码说明两个问题:

  • 1.timer会开启至少一个线程

  • 2.tv_test的时间是1秒更新一次,tv_test1的时间不会更新,在子线程中无法直接更新UI。

xamarin android中子线程更新UI线程的方法就是RunOnUIThread,该方法参数是一个无参无返回值的委托。

06

异步加载图片,在子线程中更新UI线程

我们已经知道子线程中更新UI的使用方法是RunOnUIThread ,下面这个例子使用异步加载图片,异步的重点是开启子线程。

关于http请求的库,microsoft封装的库在命名空间System.NET.Http,这里演示的是第三方的http请求库RestSharp,你可以在nuget上添加引用。

    [Activity(Label = "Xamarin_android", MainLauncher = true, Icon = "@drawable/icon")]    public class AsyncLoadImageActivity : Activity
{ private ImageView img; private Button btn; private TextView tv_result; private Bitmap bitmap; private Button btn_test; private int noBlock_number; protected override void OnCreate(Bundle bundle)
{ base.OnCreate(bundle);
SetContentView(Resource.Layout.AsyncLoadImage);
btn = FindViewById<Button>(Resource.Id.btn);
img = FindViewById<ImageView>(Resource.Id.img);
tv_result = FindViewById<TextView>(Resource.Id.tv_result);
btn_test = FindViewById<Button>(Resource.Id.btn_noBlockTest);
btn_test.Click += (s, e) =>
{
noBlock_number++;
btn_test.Text += "点击了" + noBlock_number+"次";
};
System.Diagnostics.Debug.Write("UI线程ID:"+Thread.CurrentThread.ManagedThreadId); const string url = "https://gss0.bdstatic.com/-4o3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign=0003b03088b1cb133e693b15e56f3173/0bd162d9f2d3572c257447038f13632763d0c35f.jpg";
btn.Click += (s, e) =>
{
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
GetStreamAsync(url,()=>RunOnUiThread(()=> {
System.Diagnostics.Debug.Write("异步线程ID:" + Thread.CurrentThread.ManagedThreadId);
sw.Stop(); double seconds = sw.Elapsed.TotalSeconds;
tv_result.Text = "加载图片使用了" + seconds + "秒";
img.SetImageBitmap(bitmap);
}),
error =>RunOnUiThread(()=> {
tv_result.Text = error;
}));
};
} /// <param name="successAction">获取图片成功回调方法</param>
/// <param name="errorAction">获取失败回调方法</param>
public void GetStreamAsync(string url,Action successAction,Action<string> errorAction)
{ try
{
RestClient client = new RestClient(url);
RestRequest request = new RestRequest(); var result = client.GetAsync(request,(response,handler)=> { if (response.StatusCode == 0)
{
errorAction("网络状况差,请稍后再试"); return;
} if(response.StatusCode == System.Net.HttpStatusCode.OK)
{ var bytes = response.RawBytes;
MemoryStream stream = new MemoryStream(bytes);
bitmap = BitmapFactory.DecodeStream(stream);
successAction();
}
});
} catch (Exception ex)
{
System.Diagnostics.Debug.Write(ex.StackTrace);
errorAction(ex.ToString());
}
}
}

从运行的结果我们可以看到,UI线程ID和异步方法中的回调方法子线程ID不一样,使用异步方法不会阻塞UI线程,执行耗时请求图片方法时,任然可以点击按钮,输入其他的事件。

推荐阅读

工具程序员必装的10款谷歌插件

技巧14个实用的 数据库设计技巧

原创程序员:我终于知道post和get的区别

面试面试官:你连RESTful都不知道我怎么敢要你?

热议年底300人被裁,感受互联网民工讨薪的心路历程

转载程序员:改完这9段屎一样的代码,还挺香!

技巧99%的人不知道搜索引擎的6个技巧

面试面试官:你们前后端分离的接口规范是什么?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决问题的能力 10倍程序员

大家好&#xff0c;我是Z哥。今天我们聊的话题对大多数人来说应该都算是一个“痛点”&#xff0c;就是怎么提高自己解决问题的能力。我们的工作中&#xff0c;每天会遇到大大小小的很多问题。其中有些是之前从未遇到过的问题&#xff0c;这对很多人来说就会很棘手&#xff0c;不…

.NET Core 3.1正式发布,还不赶快升级!

点击蓝字关注我们 .NET Core 3.1于2019年12月3日正式发布&#xff0c;这是一个长期支持&#xff08;LTS&#xff09;版本&#xff0c;并且将支持三年&#xff0c;这个版本对.NET Core的许多方面进行了改进&#xff0c;建议您尽快升级。 .NET Core 3.1 的变更日志很小。唯一新增…

.NET Core Blazor 1-Blazor项目文件分析

简介Blazor是一个使用.NET技术用于代替JavaScript/typescript的前端WEB框架。在前端开发中使用.NET语言进行书写逻辑有利于我们的性能、可靠性和安全性。并且对于使用.NET开发人员而言&#xff0c;全栈的成本更低。截止文章发布时&#xff0c;.NET Core已经发布了3.1版本&#…

除了HTML、CSS与JS,现在WASM也是标准Web语言

大家应该知道&#xff0c;万维网联盟 W3C 认证的 Web 语言有 HTML、CSS 与 JavaScript&#xff0c;而近日联盟正式宣布 WebAssembly 核心规范&#xff08;WebAssembly Core Specification&#xff09;成为官方 Web 标准&#xff0c;这意味着 WebAssembly 成为了第 4 种 Web 语言…

DDD实战与进阶 - 值对象

概述作为领域驱动设计战术模式中最为核心的一个部分-值对象。一直是被大多数愿意尝试或者正在使用DDD的开发者提及最多的概念之一。但是在学习过程中&#xff0c;大家会因为受到传统开发模式的影响&#xff0c;往往很难去运用值对象这一概念&#xff0c;以及在对值对象进行持久…

C# Lazy Loading

前言按需加载对象延迟加载实际是推迟进行创建对象&#xff0c;直到对其调用后才进行创建初始化&#xff0c;延迟&#xff08;懒加载&#xff09;的好处是提高系统性能&#xff0c;避免不必要的计算以及不必要的资源浪费。常规有这些情况&#xff1a;对象创建成本高且程序可能不…

将 WinForms 应用从 .NET Core 3.0 升级到 3.1

点击上方蓝字关注“汪宇杰博客”导语我作为社区里的“拖控件之王”&#xff0c;拖控件贼心不死&#xff0c;有时候会维护一些老项目&#xff0c;其中包括一个2004年的WinForms 软件。9月份的时候我曾经将它迁移到了 .NET Core 3.0&#xff0c;因为代码实现完全没动&#xff0c;…

戴明博士:管理的十四项原则

爱德华兹戴明博士&#xff08;Dr. W. Edwards Deming&#xff09;于1982年首版发行的《走出危机》(Out of The Crisis)一书中&#xff0c;提出了组织管理的14条基本原则。书中戴明博士认为&#xff1a;当时的美国企业多致力于追求短期利润&#xff0c;缺乏不断推出新产品及完善…

在Windows系统中构建还原ASP.NET Core 源码

大家好&#xff0c;这几天试着从Github上拉取AspNetCore的源码&#xff0c;尝试着通过Visual Studio 打开&#xff0c;但是并不尽人意。我们需要去构建我们拉去的源代码&#xff0c;这样才可以通过VisualStudio可还原的项目。毕竟AspNetCore是一个巨型的项目集。先决条件在Wind…

用HttpReports快速搭建API分析平台

HttpReports简单介绍HttpReports 是 .Net Core下的一个Web组件&#xff0c;适用于 WebAPI 项目和 API 网关项目&#xff0c;通过中间件的形式集成到您的项目中, 通过HttpReports&#xff0c;可以让开发人员快速的搭建出一个 API 性能分析的基础报表网站。主要包含 HttpReports …

他,TypeScript GitHub Star 上海第一,全国第四!GitHub 总标星超两万!

前两天和老同学羡辙&#xff08;Apache Echarts 核心开发、百度最美工程师&#xff09;聊天。她分享了一个 GitHub 排名的网站给我。http://git-awards.com/users?typecity&languagetypescript&cityShanghai我看了下 TypeScript star 数量的排名。哇噻&#xff01;厉害…

[原]排错实战——拯救加载调试符号失败的IDA

本文之前发表的时候有些问题&#xff0c;作为强迫症患者的我又重新编辑后再次发表。如果您已经看过&#xff0c;请忽略。望见谅。缘起 最近想借助IDA逆向一个函数。在windows下&#xff0c;调试器&#xff08;比如vs, windbg&#xff09;可以通过调试符号&#xff08;PDB&#…

如何运用DDD - 实体

概述本文将介绍领域驱动设计&#xff08;DDD&#xff09;战术模式中另一个常见且非常重要的概念 - 实体。相对战术模式中其他的一些概念&#xff08;例如 值对象、领域服务等&#xff09;来说&#xff0c;实体应该比较容易让人理解和运用。但是我们如何去发现所在领域中的实体呢…

Natasha v2.5.4 版与运行时实战

文章转载授权级别&#xff1a;BNatasha 是一个十分便捷的动态构建库&#xff0c;支持.NET Standard2.0 / Core3.0 ; 比起繁杂的 IL 指令和 Expression 的众多 API , Natasha 的构建方式更加友好简洁&#xff0c; 基于 Natasha 可以让动态工作变得傻瓜&#xff0c;简单&#xf…

.Net Core3.1下使用Swagger搭建web api项目

前言&#xff1a;微软于前天发布.net core 3.1正式版,并将长期支持3.1。所以我听到这个消息后就急忙下载.net core 3.1的SDK和Runtime,应该是公司最先用3.1的攻城狮了????。OK&#xff01;废话少说&#xff0c;今天的目的是基于.net core 3.1建一个web api的项目先下载.net…

一个值得学习的WPF开源项目

项目介绍此项目应用了Prism MVVM框架&#xff0c;项目展示数据来源于其他服务程序&#xff0c;使用的WebAPI通信&#xff0c;如果要正常运行此程序&#xff0c;需要您自己做一个WebAPI程序&#xff0c;由API接口提供数据驱动&#xff0c;其实直接查看代码最直接&#xff0c;有需…

程序员修神之路--打通Docker镜像发布容器运行流程

菜菜哥&#xff0c;我看了一下docker相关的内容&#xff0c;但是还是有点迷糊还有哪不明白呢&#xff1f;如果我想用docker实现所谓的云原生&#xff0c;我的项目该怎么发布呢&#xff1f;这还是要详细介绍一下docker了Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言…

用.NET解索尼相机ARW格式照片

用.NET解索尼相机ARW格式照片目前常用的照片格式是 .jpg&#xff0c;它只能提供 8bit的色彩深度&#xff0c;而目前主流的相机都能提供高达 12bit- 14bit的色彩深度&#xff0c;动态范围和后期处理能力也大大增加&#xff0c;这也是为什么不少摄影爱好者会优先使用相机提供原始…

ASP.NET Core on K8S深入学习(10)K8S包管理器Helm-Part 1

本篇已加入《.NET Core on K8S学习实践系列文章索引》&#xff0c;可以点击查看更多容器化技术相关系列文章。关于HelmWhy Helm&#xff1f;虽然K8S能够很好地组织和编排容器&#xff0c;但是缺少一个更高层次的应用打包工具&#xff0c;而Helm就是专门干这个事的。通过Helm能够…

520 钻石争霸赛 7-5 大勾股定理 (数学)

基本思路&#xff1a; 这道题暴力拿到14分并不难&#xff0c;根据题意模拟即可&#xff0c;具体代码在下面。 至于最后一个测试点超时的问题&#xff0c;现已解决&#xff0c;AC代码在第二部分哦~ 参考代码&#xff08;14分&#xff09;&#xff1a; #include<bits/stdc.h…