如何利用Handler更新android的UI

Handler使用入门

首先看官网API:

Class Overview

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Scheduling messages is accomplished with the post(Runnable)postAtTime(Runnable, long)postDelayed(Runnable, long)sendEmptyMessage(int)sendMessage(Message),sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post orsendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.


 Handler的定义:主要接受子线程发送的数据, 并用此数据配合主线程更新UI.

 解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示  "强制关闭".这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的. 这个时候,Handler就出现了来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象(里面包含数据)  , 把这些消息放入主线程队列中,配合主线程进行更新UI。当用户点击一个按钮时如果执行的是一个常耗时操作的话,处理不好会导致系统假死,用户体验很差,而Android则更进一步,如果任意一个Acitivity没有响应5秒钟以上就会被强制关闭,因此我们需要另外起动一个线程来处理长耗时操作,而主线程则不受其影响,在耗时操作完结发送消息给主线程,主线程再做相应处理。那么线程之间的消息传递和异步处理用的就是Handler。


 Handler特点:handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般   是位于主线程),它有两个作用: 

      1. 安排消息或Runnable 在某个主线程中某个地方执行
      2. 安排一个动作在不同的线程中执行

 Handler中分发消息的方法:

    post(Runnable)
        postAtTime(Runnable,long)
        postDelayed(Runnable long)
        sendEmptyMessage(int)
        sendMessage(Message)
        sendMessageAtTime(Message,long)
        sendMessageDelayed(Message,long)复制代码以上post类方法允许你排列一个Runnable对象到主线程队列中,
        sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.


Handler实例
      (1) 子类需要继承Hendler类,并重写handleMessage(Message msg) 方法, 用于接受线程数
以下为一个实例, 它实现的功能为 : 通过线程修改界面Button的内容
public class MyHandlerActivity extends Activity {
    Button button;
    MyHandler myHandler;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.handlertest);
        button = (Button) findViewById(R.id.button);
        myHandler = new MyHandler();
        // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据
        // Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象
        // (2): 让一个动作,在不同的线程中执行.
        // 它安排消息,用以下方法
        // post(Runnable)
        // postAtTime(Runnable,long)
        // postDelayed(Runnable,long)
        // sendEmptyMessage(int)
        // sendMessage(Message);
        // sendMessageAtTime(Message,long)
        // sendMessageDelayed(Message,long)
      
        // 以上方法以 post开头的允许你处理Runnable对象
        //sendMessage()允许你处理Message对象(Message里可以包含数据,)
        MyThread m = new MyThread();
        new Thread(m).start();
    }
    /**
     * 接受消息,处理消息 ,此Handler会与当前主线程一块运行
     * */
    class MyHandler extends Handler {
        public MyHandler() {
        }
        public MyHandler(Looper L) {
            super(L);
        }
        // 子类必须重写此方法,接受数据
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.d("MyHandler", "handleMessage......");
            super.handleMessage(msg);
            // 此处可以更新UI
            Bundle b = msg.getData();
            String color = b.getString("color");
            MyHandlerActivity.this.button.append(color);
        }
    }
    class MyThread implements Runnable {
        public void run() {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Log.d("thread.......", "mThread........");
            Message msg = new Message();
            Bundle b = new Bundle();// 存放数据
            b.putString("color", "我的");
            msg.setData(b);
            MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI
        }
    }
}

提出问题:

新手刚开始接触Android线程编程的时候,会想到如下使用如下代码:
new Thread( new Runnable() {    
    public void run() {    
         myView.invalidate();   
     }           
}).start();  
但是:该实现方法是不可行的。

分析问题:

因为:Android UI操作并不是线程安全的,这些操作必须在UI线程中执行

  1. myView.invalidate()是UI操作;
  2. new Thread创建了一个新的线程,是一个子线程,而不是UI线程,在这个新的线程里面对view进行刷新,这样是不安全的。

解决问题:

那么如何解决这个问题呢?

答案:使用Handler来实现UI线程的更新的。

实现步骤:

1.在view类里面定义一个Handler变量,这并没有开启一个新的线程,因此在handler里面更新本view是安全的;

2.然后我们创建一个线程,通过这个线程来给activity的handler变量发送消息,同时通过这个线程进行延时。

 

意即:

      1. Handler接收消息和处理消息;
      2. 创建一个线程,来给Handler发送消息; 

消息处理三部曲:

1.发送消息;2.接收消息;3.处理消息;


 代码参考:


首先创建简单的View,代码如下:
package com.ray.handler;  
import android.content.Context;  
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.graphics.Point;  
import android.graphics.drawable.Drawable;  
import android.view.View;  
   
public class BounceView extends View {  
   
    float x = 40;  
    public BounceView(Context context) {  
         super(context);  
    }  
   
    @Override  
    protected void onDraw(Canvas canvas) {  
         x+=10;  
         Paint mPaint = new Paint();  
         mPaint.setAntiAlias(true);  
         mPaint.setColor(Color.GREEN);  
         canvas.drawCircle(x, 40, 40, mPaint);  
    }  
}  

创建Activity类,代码如下:
package com.ray.handler;  
import android.app.Activity;  
import android.content.Context;  
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.os.Bundle;  
import android.os.Handler;  
import android.os.Message;  
import android.view.View;  
import android.view.Window;  
   
public class TestHandler extends Activity {  
     protected static final int GUIUPDATEIDENTIFIER = 0x101;  
   
     Thread myRefreshThread = null;  
     BounceView myBounceView = null;  
   
     Handler myHandler = new Handler() {//在主线程中创建Handler对象  
          public void handleMessage(Message msg) {//处理消息  
               switch (msg.what) {  
                    case TestHandler.GUIUPDATEIDENTIFIER://  
                         myBounceView.invalidate();//UI界面更新  
                         break;  
               }  
               super.handleMessage(msg);  
          }  
     };  
   
   
     public void onCreate(Bundle savedInstanceState) {  
   
          super.onCreate(savedInstanceState);  
          this.requestWindowFeature(Window.FEATURE_NO_TITLE);  
          this.myBounceView = new BounceView(this);  
          this.setContentView(this.myBounceView);  
   
          new Thread(new myThread()).start();//创建一个子线程  
   
     }  
   
//线程类,这个线程只是发送消息,然后延时而已。  
   
     class myThread implements Runnable {  
   
          public void run() {  
   
               while (!Thread.currentThread().isInterrupted()) {   
   
                    Message message = new Message();  
   
                    message.what = TestHandler.GUIUPDATEIDENTIFIER;  
   
                    TestHandler.this.myHandler.sendMessage(message); //发送一次消息,自动调用上面handler类的handleMessage函数,从而更新view类。  
   
                    try {  
   
                         Thread.sleep(100); //延时  
   
                    } catch (InterruptedException e) {  
   
                         Thread.currentThread().interrupt();  
                    }  
               }  
          }  
     }  
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中,UI 只能在主线程(也称为 UI 线程)中更新。如果你在另一个线程中更新 UI,就会出现空指针异常或其他异常。这是因为 Android 系统保护了 UI 线程,不允许在其他线程中直接更新 UI。为了解决这个问题,Android 提供了一种机制,即 HandlerHandlerAndroid 中的一个类,它的主要作用是将消息发送到主线程的消息队列中,然后在主线程中处理这些消息。通过使用 Handler,你可以在其他线程中发送消息,然后在主线程中更新 UI。 在使用 Handler 更新 UI 的过程中,如果你仍然遇到空指针异常,那么可能是因为你没有正确地初始化 Handler 或者没有正确地发送消息。你可以检查一下你的代码,确保你正确地使用了 Handler。 下面是一个使用 Handler 更新 UI 的示例代码: ``` // 初始化 Handler private Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // 更新 UI mTextView.setText("Hello, world!"); } }; // 在其他线程中发送消息 new Thread(new Runnable() { @Override public void run() { // 发送消息 mHandler.sendEmptyMessage(0); } }).start(); ``` 在上面的代码中,我们首先创建了一个 Handler 对象,并重写了它的 handleMessage() 方法。然后,在其他线程中发送空消息,然后在 handleMessage() 方法中更新 UI。注意,我们使用了 Looper.getMainLooper(),这样可以确保 Handler 在主线程中运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值