Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中的消息處理實例與分析

Android中的消息處理實例與分析

日期:2017/2/23 15:55:16      編輯:關於Android編程

Android中的消息處理實例與分析

摘要

本文介紹了Android中的消息處理機制,給出了Android消息處理中的幾個重點類Handler、Message、MessageQueue、Looper、Runnable、Thread的詳細介紹,提供了兩個消息處理的實例代碼,並深入分析了使用Android消息機制應該遵循的幾個原則。

閱讀本文的收獲

在具有java基礎的情況下,Android的學習比較輕松,很多人在沒有深刻了解Android消息處理機制的背景下,已經能夠開發出可用的app,很多人開始想學習Android消息處理機制的第一個動機是遇到了一個著名的bug“CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.”。這個bug的含義即“只有創建一個view層次的線程能夠更新此view”,在更多情況下,它是想說“只有主線程能夠更新UI”。
本文即是來解釋如何利用Android的消息機制從主線程或者子線程中更新UI或完成其他操作。你可以學到:
1. 如何使用Android的消息實現同步、異步操作;
2. 如何在主線程和子線程發送並接收消息;
3. 消息是如何得到處理的;
4. 使用Android的消息處理機制應該遵循的幾個原則;
5. 兩個具體的消息處理實例源代碼。

閱讀本文需要的技術背景

你可能需要如下技術背景才能完全理解本文的內容,如何你沒有以下背景,建議先學習相關知識:
1. Java語言基礎
2. Java多線程技術
3. Android開發基本知識

第一個例子:在主線程和子線程中發送消息,在主線程中處理消息

先來看一個代碼,代碼地址為:
http://download.csdn.net/detail/logicteamleader/8827099
本例的作用是:創建一個Handler(處理消息的類),在界面上點擊按鈕就會向此Handler發送消息,Handler可以處理這些消息(後面會詳細解釋,消息的處理其實是多個類共同合作的結果),對UI進行修改。界面上有八個按鈕,從上至下其效果分別是:
1. 使用Handler的post來傳遞一個Runnable的實例,該實例的run方法會在按鈕點擊時運行;
2. 使用Handler的postDelayed(Runnable r, long delayMillis)來傳遞一個Runnable的實例,該實例的run方法會在一段時間delayMillis後執行;
3. 使用Handler的sendMessage方法傳遞一個消息,該消息在Handler的handleMessage方法中被解析執行;
4. 使用Message的sendToTarget方法向獲得該Message的Handler傳遞一個消息,該消息在Handler的handleMessage方法中被解析執行;
第5、6、7、8個方法與上面四個方法類似,不同的是它們都是在子線程中調用的。
源代碼如下:

package com.example.wxbhandlertest;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.MessageQueue;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
/**
 * @author wxb
 * Android中的消息處理實例之一
 * * 一、在主線程內發送消息
 * 1.使用post
 * 2.使用postDelay
 * 3.使用sendMessage
 * 4.使用Message.sentToTarget
 * 二、在子線程中使用Handler
 * 1.使用post
 * 2.使用postDelay
 * 3.使用sendMessage
 * 4.使用Message.sentToTarget
 */
public class MainActivity extends Activity {
    private Runnable runnable=null;
    private Runnable runnableDelay=null;
    private Runnable runnableInThread=null;
    private Runnable runnableDelayInThread=null;
    private static TextView tv;
    private static TextView tvOnOtherThread;

    //自定義Message類型
    public final static int MESSAGE_WXB_1 = 1;
    public final static int MESSAGE_WXB_2 = 2;
    public final static int MESSAGE_WXB_3 = 3;
    public final static int MESSAGE_WXB_4 = 4;
    public final static int MESSAGE_WXB_5 = 5;

    private static Handler mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what){
            case MESSAGE_WXB_1:
                tv.setText("invoke sendMessage in main thread");
                break;
            case MESSAGE_WXB_2:
                tv.setText("Message.sendToTarget in main thread");
                break;
            case MESSAGE_WXB_3:
                tvOnOtherThread.setText("invoke sendMessage in other thread");
                break;
            case MESSAGE_WXB_4:
                tvOnOtherThread.setText("Message.sendToTarget in other thread");
                break;  
            }
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) this.findViewById(R.id.tvOnMainThread);
        tvOnOtherThread = (TextView) this.findViewById(R.id.tvOnOtherThread);

        //方法1.post
        runnable = new Runnable(){
            public void run(){
                tv.setText(getString(R.string.postRunnable));
            }
        };
        Button handler_post = (Button) this.findViewById(R.id.btnHandlerpost);
        handler_post.setOnClickListener(new OnClickListener() {     
            @Override
            public void onClick(View v) {
                mHandler.post(runnable);
            }
        });

        //方法2:postDelay
        runnableDelay = new Runnable(){
            public void run(){
                tv.setText(getString(R.string.postRunnableDelay));
            }
        };

        Button handler_post_delay = (Button) this.findViewById(R.id.btnHandlerPostdelay);
        handler_post_delay.setOnClickListener(new OnClickListener() {       
            @Override
            public void onClick(View v) {
                mHandler.postDelayed(runnableDelay, 1000);  //1秒後執行
            }
        });

        //方法3:sendMessage
        Button btnSendMessage = (Button) this.findViewById(R.id.btnSendMessage);
        btnSendMessage.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Message msg = mHandler.obtainMessage();
                msg.what = MESSAGE_WXB_1;
                mHandler.sendMessage(msg);
            }
        });

        //方法4:Message.sendToTarget
        Button btnSendtoTarget = (Button) this.findViewById(R.id.btnSendtoTarget);
        btnSendtoTarget.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Message msg = mHandler.obtainMessage();
                msg.what = MESSAGE_WXB_2;
                msg.sendToTarget();
            }
        });

       //在其他線程中發送消息
        //1.post
        runnableInThread = new Runnable(){
            public void run(){
                tvOnOtherThread.setText(getString(R.string.postRunnableInThread));
            }
        };

        Button btnPost = (Button) this.findViewById(R.id.btnPost);
        btnPost.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(){
                    @Override
                    public void run() {
                        super.run();
                        mHandler.post(runnableInThread);
                    }
                }.start();
            }
        });

        //2.postDelay
        runnableDelayInThread = new Runnable(){
            public void run(){
                tvOnOtherThread.setText(getString(R.string.postRunnableDelayInThread));
            }
        };

        Button btnPostDelay = (Button) this.findViewById(R.id.btnPostDelay);
        btnPostDelay.setOnClickListener(new OnClickListener() {             
            @Override
            public void onClick(View v) {
                new Thread(){
                    @Override
                    public void run() {
                        super.run();
                        mHandler.postDelayed(runnableDelayInThread, 1000);
                    }
                }.start();
            }
        });

        //3.sendMessage
        Button btnSendMessage2 = (Button) this.findViewById(R.id.btnSendMessage2);
        btnSendMessage2.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(){
                    @Override
                    public void run() {
                        super.run();
                        Message msg = mHandler.obtainMessage();
                        msg.what = MESSAGE_WXB_3;
                        mHandler.sendMessage(msg);
                    }
                }.start();
            }
        });

      //方法4:Message.sendToTarget
        Button btnSendToTarget2 = (Button) this.findViewById(R.id.btnSendToTarget2);
        btnSendToTarget2.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(){
                    @Override
                    public void run() {
                        super.run();
                        mHandler.obtainMessage(MESSAGE_WXB_4).sendToTarget();
                    }
                }.start();
            }
        });
    }
}

幾個規則

看完代碼後,在解釋具體類之前,先了解幾個規則:
第一, 只有創建view的線程能夠更新此view;一般來說,創建UI的是Android主線程,因此只有在主線程中才能更新UI;
第二, 處理消息的類是Handler,它依附於創建自己的線程;如果在主線程中創建Handler mHandler,則向mHandler發送的消息會在主線程中被解析;如果在子線程中創建Handler sHandler,則向sHandler發送的消息會在子線程中被解析;
第三, 發送消息有三類方法,Handler的post方法,Handler的sendMessage方法,以及Message的sentToTarget方法,它們最終其實都調用了Handler的sendMessage方法;
第四, 消息的方法可以即時也可以延時,代表就是post和postDelay,以及sendMessage和sendMessageDelayed;延時發送消息可以實現很多用戶需要的界面延時效果,例如最常用的SplashWindow。
第五, post類型的方法只需要實例化一個Runnable接口,且不需要重載Handler. handleMessage方法,比較簡單易用;
第六, sendMessage和sentToTarget需要重載Handler. handleMessage方法,實現對不同消息的解析。

Handler

Handler是Android消息處理中最重要的一個類,不管什麼編程語言或者框架,叫Handler的類一般都很重要,也都很復雜,當年的Windows句柄類更是讓人一頭霧水。
幸好,Android的Handler很容易理解和使用。它就是一個消息處理的目標類,其主要作用有兩個:1)它安排要執行的消息和runnable在未來某個時間點執行;2)處理來自不同線程的消息並執行操作。
使用Handler要注意以下幾點:
1. Handler屬於創建它的線程,並與線程的消息循環關聯,同時與此線程的消息隊列(MessageQueue)關聯。
2. Handler發送消息的方法有兩類:post類和sendMessage類,用法見例子;
3. 如果使用post發送消息,則消息中包含的Runnable會在解析消息時執行;
4. 如果使用sendMessage發送消息,則需要重載Handler. handleMessage方法,實現對不同消息的解析

Message

Message是消息類,它有幾個重要的屬性:
public int what:消息類型
public int arg1:參數1
public int arg2:參數2
public Object obj:對象參數
以上幾個參數用戶可以隨意定制。
值得注意的有幾點:
1. Android使用消息池模式來提高消息的效率,因此一般不適用new來創建消息,而是使用obtain方法來從消息池中獲取一個Message的實例;Handler類也有obtain方法;
2. Message有一個Handler作為Target,一般來說就是獲取該消息的所在線程的關聯Handler;因此使用sendToTarget方法發送消息時,將消息發給它的Target;

MessageQueue

MessageQueue是消息隊列,一個線程最多擁有一個消息隊列,這個類一般不會被程序員直接使用。它的入隊方法enqueueMessage用來將一個Message加入隊列,這個方法是線程安全的,因此才保證了Android的消息處理機制是線程安全的。
MessageQueue的next方法用來獲取下一個Message,沒有任何消息時,主線程常常在此方法處等待。

Runnable

Java的接口,它代表一個可執行的代碼片段,如下所示:

public interface Runnable {

    /**
     * Starts executing the active part of the class' code. This method is
     * called when a thread is started that has been created with a class which
     * implements {@code Runnable}.
     */
    public void run();
}

Thread

Java的線程類,大家都應該很熟悉了。

第二個例子:在子線程中創建消息處理循環

第一個例子描述了如何在多個線程中發送消息,並在主線程中統一接收和處理這些消息;第二個例子則描述如何在子線程中建立一個消息循環,並從主線程發送消息給子線程,讓子線程處理這些消息。
第二個例子中有兩個消息循環,兩個Handler,主線程首先向子線程發送一個消息,子線程的收到消息後再向主線程發送一個消息,主線程收到消息後更新UI。
例子代碼如下:
http://download.csdn.net/detail/logicteamleader/8827401
源代碼如下:

package com.example.wxbloopinthread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    private static TextView tv = null;

    //自定義Message類型
    public final static int MESSAGE_WXB_1 = 1;

    //主線程中創建Handler
    private static Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch(msg.what){
            case MESSAGE_WXB_1:
                tv.setText("主線程發送,子線程接收消息後回發,主線程修改UI");
                break;
            }
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);     
        setContentView(R.layout.activity_main);
        tv = (TextView) this.findViewById(R.id.textView1);

        //創建子線程
        new LooperThread().start();

        //點擊按鈕向子線程發送消息
        Button btn = (Button) this.findViewById(R.id.btnSendMessage);
        btn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                LooperThread.sHandler.sendEmptyMessage(MESSAGE_WXB_1);
            }
        });
    }

    //定義子線程
    static class LooperThread extends Thread {
        public static Handler sHandler = null;

        public void run() {
            //創建消息循環
            Looper.prepare();

            sHandler = new Handler() {
                public void handleMessage(Message msg) {
                    switch(msg.what){
                    case MESSAGE_WXB_1:
                        mHandler.sendEmptyMessage(MESSAGE_WXB_1);
                        break;
                    }
                }
            };
            //開啟消息循環
            Looper.loop();
        }
    }
}

第二個例子使用了另一個重要的類Looper,它是代表Android中的消息循環處理類。

Looper

Looper被用來創建一個線程的消息循環。線程默認情況下是沒有消息循環的,要創建消息循環,必須先調用Looper.prepare,然後在合適的地方調用Looper.loop,這個loop方法就開始循環處理本線程接收到的消息,直到loop循環被停止。
在大部分情況下,Looper都是和Handler一起使用,它通過Handler接收和處理消息。
使用Looper要注意以下幾點:
1. Android的主線程是默認已經創建了Looper對象的,因此不能在主線程中調用Looper.prepare;
2. Looper.prepare和Looper.loop都是靜態方法,調用時要注意,不要使用new來創建一個Looper;

總結

使用Android消息的方法有以下幾種:
1. 使用Handler和Runnable,即時或延時發送一個消息;
2. 使用Handler和Message,即時或延時發送一個消息,需重載Handler. handleMessage方法;
需要注意的規則有以下幾條:
1. 只有創建view的線程能夠更新此view;一般來說,創建UI的是Android主線程,因此只有在主線程中才能更新UI;
2. 處理消息的類是Handler,它依附於創建自己的線程;如果在主線程中創建Handler mHandler,則向mHandler發送的消息會在主線程中被解析;如果在子線程中創建Handler sHandler,則向sHandler發送的消息會在子線程中被解析;
3. Looper被用來創建一個線程的消息循環,要創建消息循環,必須先調用Looper.prepare,然後在合適的地方調用Looper.loop。
具體的體會,還是要多看代碼才行。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved