Android之在子线程更新UI(超详细)
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.LooperLooper是每个线程中的一个管理者,Looper调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。
大致流程(带图):
1)现在主线程中创建一个handler对象,Looper对象和messageQueue对象是不用创建的,因为在系统源码中这个是在主线程创建的时候就创建好的。2)然后重写handlerMessage()方法,这个方法是在handler接收到message之后执行的方法,我们把更新UI的代码放到这个方法中。
3)然后再子线程中用sendEmptyMessage()方法来发送一个消息到消息队里。
4)在发送完这个消息之后,Looper这个轮询器因为一直在轮询消息队列。获得到这个消息之后就会执行handlerMessage()方法,因为这个方法是在主线程中实现的。所以UI就可以更新了。
ending:喜欢本文章的同学记得关注哦。一起学习!
更多相关文章
- Android 线程间通信机制(ITC详解)
- Android ADB使用方法
- Handle详解和使用方法
- android中使用线程池和临时缓存优化网络图片加载
- Android下引用系统库的方法及问题
- android的UI操作单线程模型理解