Android_Handler

来源:互联网 发布:火箭vs勇士数据 编辑:程序博客网 时间:2024/06/06 18:08

一.Handler的简介
        首先来了解一下Handler:
        Handler为Android操作系统中的线程通讯工具,来自包:android.os.Handler
        Handler绑定了两个队列:
        1.消息队列:发送--接受--处理消息(消息队列使用sendMessage和HandleMessage的组合来发送和处理消息。)
        2.线程队列:启动--结束--休眠线程(线程队列类似一段代码,或者说一个方法的委托,用户传递方法。使用post,postDelayed添加委托,使用 removeCallbacks移除委托。)
        在进一步了解Handler之前,先说一下为什么需要Handler这个类:
        当一个应用程序启动的时候,Android首先会开启一个主线程(UI线程),主线程管理界面中的UI控件,进行事件分发!比如说:当你点击一个按钮的时候,Android系统会分发事件到Button上,来响应你的操作。如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收到Android系统的一个错误提示“强制关闭”。这个时候我们需要把这些耗时的操作,放在一个子线程中!然而这涉及到子线程更新UI。在java里,可以直接在子线程中刷新UI界面,可是在android编程中,是不可以的!因为他违背了单线程模型:Android中,UI操作必须在主线程(UI线程)中进行的,这个跟Android的线程安全有关!那么更新UI只能在主线程中更新,子线程中操作是危险的,因此想更新UI,可以通过子线程发送信息给主线程,让主线程来更新UI,所以就引入了Handler!
        那么Handler是用来干嘛的?通俗一点来说:Handler就是在各个进程间发送数据的处理对象!在任何的线程中,只要获得了另一个线程的Handler,就可以向那个线程发送数据!基于这个机制,我们可以在UI线程中建一个Handler,然后新建一个子线程进行一些费时的操作,操作完成后,可以调用主线程中的handler,来发送信息告诉主线程,UI线程(主线程)进行更新UI界面!

二.Handler常用的API

方法签名

描    述

public void handleMessage (Message msg)

子类对象通过该方法接收信息

public final boolean sendEmptyMessage (int what)

发送一个只含有what值的消息

public final boolean sendMessage (Message msg)

发送消息到Handler,

通过handleMessage方法接收

public final boolean hasMessages (int what)

监测消息队列中是否还

有what值的消息

public final boolean post (Runnable r)

将一个线程添加到消息队列

Handler Handler处理者 
        是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message) 方法,它是处理这些Message的操作内容,例如更新UI。 通常需要子类化Handler来实现handleMessage方法。
Message QueueMessage Queue消息队列 
        用来存放通过Handler发布的消息,按照先进先出执行。 每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。 
Looper Looper是每条线程里的Message Queue的管家。 
        Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper()得到当前线程的Looper就有可能为NULL。Looper消息队列(MessageQueue)的管家,在消息队列中,只能有一个管家,管理着整个消息队列,而消息队列可以有多个消息(Message),Hadnler(工人)也可以有多个,管家负责派遣他们向消息队列中存储或取出消息后执行任务!
三.Handler的使用
(1) Handler的消息处理机制
界面效果图:



代码:

package com.liangdianshui.handlermessage;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity {private int number = 1;private static final int CHANGE_TITLE = 0;public static final String TAG = "MainActivity";private TextView mTvTitle;private Button mChange;private MyThread myThread;Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case CHANGE_TITLE:Log.d(TAG, "Handler中线程的id= " + Thread.currentThread().getId()+ "/n");mTvTitle.setText("Title " + number);break;default:break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(TAG, "MainActivity中线程的id= " + Thread.currentThread().getId()+ "/n");init();}private void init() {mTvTitle = (TextView) findViewById(R.id.tv_title);mChange = (Button) findViewById(R.id.bt_change);mChange.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {myThread = new MyThread();myThread.start();try {myThread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}myThread.destroy();number++;}});}class MyThread extends Thread {private boolean finished = false;@Overridepublic void run() {while (!finished) {Message message = new Message();message.what = CHANGE_TITLE;message.obj = "Title " + number;handler.sendMessage(message);Log.i(TAG, "MyThread中线程的id= " + Thread.currentThread().getId()+ "/n");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}@Overridepublic void destroy() {finished = true;}}}
         Handler消息处理机制Demo:http://download.csdn.net/detail/two_water/9332033
        从Log可以看出消息处理是在主线程中处理的,在消息处理函数中可以安全的调用主线程中的任何资源,包括刷新界面。工作线程和主线程运行在不同的线程中,所以必须要注意这两个线程间的竞争关系。
总结:
1、如果通过工作线程(子线程)刷新界面,可以使用Handler对象来实现。
2、注意工作线程和主线程之间的竞争关系。推荐handler对象在主线程中构造完成(并且启动工作线程之后不要再修改之,否则会出现数据不一致),然后在工作线程中可以放心的调用发送消息SendMessage等接口。
3、handler对象的handleMessage接口将会在主线程中调用。在这个函数可以放心的调用主线程中任何变量和函数,进而完成更新UI的任务。
(2)子线程创建消息队列更新UI
效果图:
Button被按下时,从主线程向子线程发送一个数字,然后子线程将数字用Toast显示,而主线程将TextView也被设置成该数字。



代码:

package com.liangdianshui.handlermessage3;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;public class MainActivity extends Activity {private int number = 1;public static final String TAG = "MainActivity";private TextView mTvTitle;private Button mChange;private LooperThread looperThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(TAG, "MainActivity中线程的id= " + Thread.currentThread().getId()+ "/n");looperThread = new LooperThread();looperThread.start();init();}private void init() {mTvTitle = (TextView) findViewById(R.id.tv_title);mChange = (Button) findViewById(R.id.bt_change);mChange.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Message message = Message.obtain();message.arg1 = number;mTvTitle.setText("主线程发送了 :" + String.valueOf(message.arg1));looperThread.handler.sendMessage(message);number++;}});}class LooperThread extends Thread {public Handler handler;@Overridepublic void run() {Looper.prepare();handler = new Handler() {public void handleMessage(Message msg) {Log.d(TAG, "LooperThread中线程的id= "+ Thread.currentThread().getId() + "/n");Toast.makeText(MainActivity.this,"LooperThread handler 收到消息 :" + msg.arg1,Toast.LENGTH_LONG).show();};};Looper.loop();// loop()会调用到handler的handleMessage(Message// msg)方法,所以,写在下面;}}}

子线程创建消息队列更新UI:http://download.csdn.net/detail/two_water/9332045

(3)Handler的线程队列

效果图:


代码:

package com.liangdianshui.handlerthreadqueue;import android.app.Activity;import android.app.ProgressDialog;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity {private Button mBtDownload;private ProgressDialog mProDialog;Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {mProDialog.setProgress(msg.arg1);mHandler.post(mThread);};};Runnable mThread = new Runnable() {int i = 0;@Overridepublic void run() {i += 1;Message message = mHandler.obtainMessage();message.arg1 = i;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}mHandler.sendMessage(message);if (message.arg1 == 100) {mHandler.removeCallbacks(mThread);mProDialog.setMessage("Finished");mProDialog.dismiss();}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mBtDownload = (Button) findViewById(R.id.bt_download);mProDialog = new ProgressDialog(MainActivity.this);mProDialog.setTitle("Download");mProDialog.setMessage("Downloading.....");mProDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);mProDialog.setIndeterminate(false);mProDialog.setCancelable(true);mBtDownload.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {mProDialog.show();mHandler.post(mThread);}});}}
Handler的线程队列Demo:http://download.csdn.net/detail/two_water/9332007





1 0