Android之在子线程更新UI实践

简言:首先和其他许多的GUI库一样,Android的UI线程是不安全的。则如果想更新UI就必须在主线程中进行,否则就会出现异常。

            我们通过一个小Demo来检验一下

      

 acttvity-main.xml代码如下:         

<?xml version="1.0" encoding="utf-8"?>    

  代码如上所示,布局文件中定义了两个空间,一个Button按钮,一个显示Helloworld的TextView,现在我们要实现点击Button

  按钮改变TextView的文本值为Hello JohnnyZhou.


  接下来修改MainActivity的代码:

 

package com.johnnyzhou.androidthreadtest;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity implements View.OnClickListener{    private TextView mText;    private Button mBtn_change;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mText= (TextView) findViewById(R.id.tv_text);        mBtn_change= (Button) findViewById(R.id.btn_change);        mBtn_change.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.btn_change:                //开启一个子线程                new Thread(new Runnable() {                    @Override                    public void run() {                        mText.setText("Hello JohnnyZhou");                    }                });                break;            default:                break;        }      }}

    从上面我们可以看到,我们在mBtn_change的点击事件里面开启了一个子线程,然后在子线程中使用TextView的setText方法,将文本内容改为HelloJohnnyZhou,运行程序你会发现异常。

logcat日志如下所示:

   由上面的异常可知Android确实是不允许在子线程中进行UI操作,但是在某些情况下我们必须在子线程里面去执行一些耗时任务
然后根据任务的执行结果来更新相应的UI控件,那需要怎么实现呢? 针对这种情况,Android提供了一套异步消息处理机制,很好
的解决了在子线程中进行UI操作的问题。下面我们来看一下使用方法:

   修改Mainactivity:

package com.johnnyzhou.androidthreadtest;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity implements View.OnClickListener{    private static final int CHANGE_TEXT=1;    private TextView mText;    private Button mBtn_change;    private Handler handler=new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what){                case CHANGE_TEXT:                    //在这里可以进行UI操作                    mText.setText("HelloJohnnyZhou");                    break;                default:                    break;            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mText= (TextView) findViewById(R.id.tv_text);        mBtn_change= (Button) findViewById(R.id.btn_change);        mBtn_change.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.btn_change:                //开启一个子线程                new Thread(new Runnable() {                    @Override                    public void run() {                        //新建一个Message对象,存储需要发送的消息                        Message message=new Message();                        message.what=CHANGE_TEXT;                        //然后将消息发送出去                        handler.sendMessage(message);                    }                }).start();                break;            default:                break;        }    }}

   我们在上面定义了一个整型常量CHANGE_TEXT,用于表示更新TextView这个动作。然后新增一个Handler对象,并重写父类的handleMessage()方法在这里对具体的Message进行处理。如果handler发现Message的What字段等于CHANGE_TEXT这个常量时,就将TextView的内容改为HIJohnnyZHou

在TextView的点击事件代码块中,我们这次没有接进行UI操作,而是创建了一个Message对象,并将它的what字段指定为CHANGE_TEXT,然后调用handlesendMessage()方法将这条Message消息发送出去。然后当handler接收到这条消息,就在handleMessage()方法里进行处理,再这个时侯handleMessage的方法就是处于主线程中了,我们就可以进行UI操作啦。接着对MessageWhat字段进行判断,如果等于CHANGE_TEXT这个常量,则修改TextView的文本内容。运行程序,你发现此时就可以改变TextView的内容了,是不是很简单呢!


    下面给大家分析一下Android异步消息处理机制是如何工作的:


     Android异步消息处理机制
    Android中的异步消息处理主要由四个部分组成,Message、Handler、MessageQueue和Looper。

     1.Message

      Message是在线程之间传递的消息,它可以在内部携带少量信息,用于在不同线程之间交换数据,我们可以使用Message的what字段来传递字符数据
除此之外我们还可以使用arg1和arg2字段来携带一些整形数据,使用obj来携带一个Object对象。

        //发送方式一 直接发送一个空的Message        mHandler.sendEmptyMessage(WHAT);        //发送方式二 通过sendToTarget发送        mHandler.obtainMessage(WHAT,arg1,arg2,obj).sendToTarget();        //发送方式三 创建一个Message 通过sendMessage发送        Message message = mHandler.obtainMessage();        message.what = WHAT;        mHandler.sendMessage(message);
     2.Handler
     Handler顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()或者post()方法,而发出的
消息经过一系列地辗转处理后,最终会传递到Handler的handleMessage()方法中。
     3.MessageQueue

     MessageQueue是消息队列的意思,它主要是用于存放所有的Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只有一个MessageQueue对象。    

     4.Looper

     Looper是每个线程中的一个管理者,Looper调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。


大致流程(带图):

 1)现在主线程中创建一个handler对象,Looper对象和messageQueue对象是不用创建的,因为在系统源码中这个是在主线程创建的时候就创建好的。
 2)然后重写handlerMessage()方法,这个方法是在handler接收到message之后执行的方法,我们把更新UI的代码放到这个方法中。
 3)然后再子线程中用sendEmptyMessage()方法来发送一个消息到消息队里。
 4)在发送完这个消息之后,Looper这个轮询器因为一直在轮询消息队列。获得到这个消息之后就会执行handlerMessage()方法,因为这个方法是在主线程中实现的。所以UI就可以更新了。

Android之在子线程更新UI(超详细)_第1张图片

 经过这样的一流程后,Message消息也就从子线程到了主线程,这就是整个异步消息的处理机制。


ending:喜欢本文章的同学记得关注哦。一起学习!

   
   
   
   
   
                                                                                               

更多相关文章

  1. Android 线程间通信机制(ITC详解)
  2. Android ADB使用方法
  3. Handle详解和使用方法
  4. android中使用线程池和临时缓存优化网络图片加载
  5. Android下引用系统库的方法及问题
  6. android的UI操作单线程模型理解

随机推荐

  1. Android Shape属性(内含一个Button完整的
  2. 关于如何检测Android的内存泄漏:
  3. Android之ListView属性描述
  4. android 使用mediaplayer播放网络音乐
  5. Android(安卓)多个APK共享数据(Shared Us
  6. listview为空时,显示字符串
  7. Android程式编写及调试新手入门-1
  8. Android中的Notification的使用
  9. android按键移植
  10. Android架构组件(三)——ViewModel