码迷,mamicode.com
首页 > 编程语言 > 详细

基础篇-在非UI线程中更新UI元素

时间:2015-02-09 12:58:32      阅读:272      评论:0      收藏:0      [点我收藏+]

标签:xamarin android   线程   异步   

个人原创,转载请注明出处: http://blog.csdn.net/supluo/article/details/


先了解两个概念

1、UI:User Interface的缩写,用户界面的意思。你可以不恰当的理解为我们能够看到的,操作的东西;在Android中什么才称为UI呢,可以简单的理解为View及其子类等元素。这是一个不够正确的概念,只是对新手做一个简单的抛砖引玉。

2、ANR:Application Not Responding,意思是程序没有响应。

在如下情况下,Android会报出ANR错误:

– 主线程 (“事件处理线程” / “UI线程”) 在5秒内没有响应输入事件

– BroadcastReceiver 没有在10秒内完成返回

因此耗时的操作通常会放在其他线程中去执行,以防导致程序卡顿甚至出现ANR错误,线程执行完成之后再操作UI元素。

下面举例导致卡顿的例子,在一个按钮中执行以下代码:

            proDialog.Show();//显示进度对话框
            System.Threading.Thread.Sleep(10 * 1000);//线程休眠10秒,模拟耗时操作,触发ANR
            HandleResult("数据更新成功:" + new Random().Next(10));更新其他UI元素
上面的代码就会阻塞主UI线程,其实对话框也不会显示出来,调用对话框show方法后,对话框并不会立即出现,待读者自行讨论,因此上面的效果特别糟糕,只会显示按钮被按下的效果,十秒之后更新UI,对话框不会显示出来,因此正确的做法是显示一个对话框之后,将耗时的操作放在其他线程中去执行,执行完成之后再更新ui,但是我们并不能在其他线程中直接更新UI元素,因此这篇文章的主题就是讲解如何在非UI线程中更新UI元素。

下面进入正题

在正常开发过程中,我们通常需要将程序中遇到的比较耗时的操作新开其他线程去执行,以防阻塞主UI线程导致ANR错误,但是在实际开发过程中可能会遇到如下两个异常信息

1、Only the original thread that created a view hierarchy can touch its views;

2、Can‘t create handler inside thread that has not called Looper.prepare();

第一种在非UI线程中更新UI元素就会导致这种错误,第二种是在非UI线程中视图显示某些UI提示信息;其实可以笼统的归纳为都是在非UI线程中更新UI导致的。我们先看下面两个错误的例子:

一、非UI线程更新UI元素,(在一个按钮事件中执行以下操作,事件绑定代码就不贴了)

new Thread(() =>
            {
                try
                {
                    mText.Text = "数据更新成功:" + new Random().Next(10);
                }
                catch (Exception ex)
                {
                }
            }).Start();

执行结果抛出android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.即上面说的第一种错误信息出现的情况。

二、非UI线程进行UI提示。(在一个按钮事件中执行以下操作,事件绑定代码就不贴了)

new Thread(() =>
            {
                try
                {
                    proDialog.Show();//proDialog是一个dialog
                }
                catch (Exception ex)
                {
                }
            }).Start();
执行结果抛出java.lang.RuntimeException: Can‘t create handler inside thread that has not called Looper.prepare(),即上面说的第二种错误信息出现的情况。

解决办法:

这种错误对于才接触这行的开发者来讲,经常出现这样的问题,其实这种问题也相当简单比较容易解决,但是最主要的是找到一个最为合适的解决办法,下面说几种比较简单的解决方法。

1、委托式

将非UI元素中执行UI操作的部分委托给主UI线程执行,见以下代码:

        proDialog.Show();
            new Thread(() =>
            {
                System.Threading.Thread.Sleep(10 * 1000);

                this.RunOnUiThread(() => //this 指代的是Activity对象,RunOnUiThread 是Activity的一个成员方法
                {
                    HandleResult("数据更新成功:" + new Random().Next(10));
                });
            }).Start();

2、View.Post形式

使用UI元素的Post或者PostDelay方法

官方文档注释:Causes the Runnable to be added to the message queue. The runnable will be run on the user interface thread.

Parameters: action The Runnable that will be executed.

Returns:Returns true if the Runnable was successfully placed in to the message queue. Returns false on failure, usually because the looper processing the message queue is exiting.

示例见以下代码:

proDialog.Show();
            new Thread(() =>
            {
                try
                {
                    System.Threading.Thread.Sleep(10 * 1000);
                    mText.Post(() =>
                    {
                        HandleResult("数据更新成功:" + new Random().Next(10));
                    });
                }
                catch (Exception ex)
                {
                    this.RunOnUiThread(
                        () =>
                        {
                            Toast.MakeText(this, "线程执行过程中出现了错误!", ToastLength.Long).Show();
                        }


                    );
                }
            }).Start();
这种方法包括PostDelay方法其实跟第一种方法是极其类似的,底层实现的本质是一样。


3、Handler形式

1)、初始化一个Handler对象

      Handler mHandler;//定义变量

     mHandler = new Handler(HandleMessage);//初始化,此处可以使用匿名函数等方式

  //方法定义,此方法中主要是更新UI操作

private void HandleMessage(Message msg)  
        {
            switch (msg.What) { 
                case 1:
                    HandleResult("数据更新成功:" + new Random().Next(10));
                    break;
            }
        }

2)、在恰当的时候,使用handler发送消息进行UI更新

            proDialog.Show();
            new Thread(() =>
            {
                System.Threading.Thread.Sleep(10 * 1000);
                mHandler.SendEmptyMessage(1);
            }).Start();
Handler的具体使用可以查阅相关资料再进一步了解。

上面的方法大多都是使用了Handler来进行消息的处理,下面给出一张handler的工作图,

技术分享

4、AsyncTask形式

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.

这个代码有点多,可以自行百度查阅,有相当多的不错的文章可以借阅。

提醒一点,在Xamarin.Android中,如果传递给AsyncTask的执行参数是字符串,则需要使用Java.Lang.String进行包装,早些的版本我测试是需要这样做的,最近的版本还没来得及进行测试,稍后我补全这一部分,或者有知道的也可以交流下。

以上就是比较常用的方式。

Demo下载地址


个人搞了个博客App,平时上个厕所,睡觉前等随便看两篇文章,总能有些收获,希望大家支持!http://blog.csdn.net/supluo/article/details/43489475

基础篇-在非UI线程中更新UI元素

标签:xamarin android   线程   异步   

原文地址:http://blog.csdn.net/supluo/article/details/43667407

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!