Android中Handler的简单用法


Android的消息机制(一)


android 有一种叫消息队列的说法,这里我们可以这样理解:假如一个隧道就是一个消息队列,那么里面的每一部汽车就是一个一个消息,这里我们先忽略掉超车等种种因素,只那么先进隧道的车将会先出,这个机制跟我们android 的消息机制是一样的。

一、角色描述

1.Looper:(相当于隧道)一个线程可以产生一个Looper对象,由它来管理此线程里的Message Queue(车队,消息隧道)

2.Handler:作用是:在子线程中发送Message或者Runnable对象到MessageQueue中;在UI线程中接收、处理从MessageQueue分发出来的Message或者Runnable对象。发送消息一般使用Handler的sendMessage()方法,而发出去的消息经过处理后最终会传递到Handler的handlerMessage()方法中。

3. Message Queue(消息队列):用于存放Message或Runnable对象的消息队列。它由对应的Looper对象创建,并由Looper对象管理。每个线程中都只会有一个MessageQueue对象。

4.线程:UI thread通常就是main thread,而Android启动程序时会替它建立一个Message Queue

5. Message:消息体,用于装载需要发送的对象。

Message

[java] view plain copy
  1. Messagemessage=Message.obtain();
  2. message.arg1=1;
  3. message.arg2=2;
  4. message.obj="Demo";
  5. message.what=3;
  6. BundlebundleData=newBundle();
  7. bundleData.putString("Name","Lucy");
  8. message.setData(bundleData);


Message 可以传递的参数有:

(1). arg1 arg2 整数类型,是setData的低成本替代品。传递简单类型

(2). Object 类型 obj

(3). what 用户自定义的消息代码,这样接受者可以了解这个消息的信息。每个handler各自包含自己的消息代码,所以不用担心自定义的消息跟其他handlers有冲突。

(4).其他的可以通过Bundle进行传递

Message可以通过new Message构造来创建一个新的Message,但是这种方式很不好,不建议使用。最好使用Message.obtain()来获取Message实例,它创建了消息池来处理的。


每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。

在你的Android程序里,新诞生一个线程,或执行(Thread)时,并不会自动建立其Message Loop

Android里并没有GlobalMessage Queue数据结构,例如,不同APK里的对象不能透过Massage Queue来交换讯息(Message)

例如:线程AHandler对象可以传递消息给别的线程,让别的线程BC等能送消息来给线程A(存于AMessage Queue)

线程AMessage Queue里的讯息,只有线程A所属的对象可以处理。

使用Looper.myLooper可以取得当前线程的Looper对象。

使用mHandler = new EevntHandler(Looper.myLooper());可用来构造当前线程的Handler对象;其中,EevntHandler是自已实现的Handler的子类别。

使用mHandler = new EevntHandler(Looper.getMainLooper());可诞生用来处理main线程的Handler对象;其中,EevntHandler是自已实现的Handler的子类别。

Android官网总结的关于Handler类的两个主要用途: (1)线程间的通信: 在执行较为耗时的操作时,Handler负责将子线程中执行的操作的结果传递到UI线程,然后UI线程再根据传递过来的结果进行相关UI元素的更新。(上面已有说明) (2)执行定时任务: 指定任务时间,在某个具体时间或某个时间段后执行特定的任务操作,例如使用Handler提供的postDelayed(Runnable r,long delayMillis)方法指定在多久后执行某项操作,比如当当、淘宝、京东和微信等手机客户端的开启界面功能,都是通过Handler定时任务来完成的。

这样描述可能太抽像,下面举几个实际的例子来说明:

二、举例

1.同线程内不同组件间的消息传递

Looper类用来管理特定线程内对象之间的消息交换(Message Exchange)。你的应用程序可以产生许多个线程。而一个线程可以有许多个组件,这些组件之间常常需要互相交换讯息。如果有这种需要,您可以替线程构造一个Looper对象,来担任讯息交换的管理工作。Looper对象会建立一个MessageQueue数据结构来存放各对象传来的消息(包括UI事件或System事件等)。如下图:

每一个线程里可含有一个Looper对象以及一个MessageQueue数据结构。在你的应用程序里,可以定义Handler的子类别来接收Looper所送出的消息。

同线程不同组件之间的消息传递:

publicclassActivity1extendsActivityimplementsOnClickListener{

Buttonbutton=null;

TextViewtext=null;

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity1);

button= (Button)findViewById(R.id.btn);

button.setOnClickListener(this);

text= (TextView)findViewById(R.id.content);

}

publicvoidonClick(View v) {

switch(v.getId()) {

caseR.id.btn:

Looper looper = Looper.myLooper();//取得当前线程里的looper

MyHandler mHandler =newMyHandler(looper);//构造一个handler使之可与looper通信

//buton等组件可以由mHandler将消息传给looper,再放入messageQueue,同时mHandler也可以接受来自looper消息

mHandler.removeMessages(0);

String msgStr ="主线程不同组件通信:消息来自button";

Message m = mHandler.obtainMessage(1, 1, 1, msgStr);//构造要传递的消息

mHandler.sendMessage(m);//发送消息:系统会自动调用handleMessage方法来处理消息

break;

}

}

privateclassMyHandlerextendsHandler{

publicMyHandler(Looper looper){

super(looper);

}

@Override

publicvoidhandleMessage(Message msg) {//处理消息

text.setText(msg.obj.toString());

}

}

}

说明:

此程序启动时,当前线程(即主线程, main thread)已诞生了一个Looper对象,并且有了一个MessageQueue数据结构。

looper = Looper.myLooper ();调用Looper类别的静态myLooper()函数,以取得目前线程里的Looper对象.

mHandler = new MyHandler (looper);构造一个MyHandler对象来与Looper沟通。Activity等对象可以藉由MyHandler对象来将消息传给Looper,然后放入MessageQueue里;MyHandler对象也扮演Listener的角色,可接收Looper对象所送来的消息。

Message m = mHandler.obtainMessage(1, 1, 1, obj);先构造一个Message对象,并将数据存入对象里。

mHandler.sendMessage(m);就透过mHandler对象而将消息m传给Looper,然后放入MessageQueue里。


Android消息处理机制(二)


角色综述(回顾):

(1)UI thread通常就是main thread,而Android启动程序时会替它建立一个MessageQueue

(2)当然需要一个Looper对象,来管理该MessageQueue

(3)我们可以构造Handler对象来push新消息到Message Queue里;或者接收Looper(Message Queue取出)所送来的消息。

(4)线程AHandler对象可以传递给别的线程,让别的线程BC等能送讯息来给线程A(存于AMessage Queue)

(5)线程AMessage Queue里的消息,只有线程A所属的对象可以处理。

子线程传递消息给主线程

publicclassActivity2extendsActivityimplementsOnClickListener{

Buttonbutton=null;

TextViewtext=null;

MyHandlermHandler=null;

Threadthread;

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity1);

button= (Button)findViewById(R.id.btn);

button.setOnClickListener(this);

text= (TextView)findViewById(R.id.content);

}

publicvoidonClick(View v) {

switch(v.getId()) {

caseR.id.btn:

thread=newMyThread();

thread.start();

break;

}

}

privateclassMyHandlerextendsHandler{

publicMyHandler(Looper looper){

super(looper);

}

@Override

publicvoidhandleMessage(Message msg) {//处理消息

text.setText(msg.obj.toString());

}

}

privateclassMyThreadextendsThread{

@Override

publicvoidrun() {

Looper curLooper = Looper.myLooper();

Looper mainLooper = Looper.getMainLooper();

String msg ;

if(curLooper==null){

mHandler=newMyHandler(mainLooper);

msg ="curLooper is null";

}else{

mHandler=newMyHandler(curLooper);

msg ="This is curLooper";

}

mHandler.removeMessages(0);

Message m =mHandler.obtainMessage(1, 1, 1, msg);

mHandler.sendMessage(m);

}

}

}

说明:

Android会自动替主线程建立Message Queue。在这个子线程里并没有建立Message Queue。所以,myLooper值为null,而mainLooper则指向主线程里的Looper。于是,执行到:mHandler = new MyHandler (mainLooper);mHandler属于主线程。 mHandler.sendMessage(m);就将m消息存入到主线程的Message Queue里。mainLooper看到Message Queue里有讯息,就会作出处理,于是由主线程执行到mHandlerhandleMessage()来处理消息。


Handler中,与Message发送消息相关的方法有:

Message obtainMessage():获取一个Message对象。 boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。 boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行。 boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行。 boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message对象到消息队列中,在UI线程取到消息后,延迟执行。 void removeMessage():从消息队列中移除一个未响应的消息。
post的有关方法: 对于Handler的Post方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作。 Post允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable,long)。详细解释如下: boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行。 boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行。 boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行 void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象。
Android中AsyncTask的简单用法

  在开发Android移动客户端的时候往往要使用多线程来进行操作,我们通常会将耗时的操作放在单独的线程执行,避免其占用主线程而给用户带来不好的用户体验。但是在子线程中无法去操作主线程(UI 线程),在子线程中操作UI线程会出现错误。因此android提供了一个类Handler来在子线程中来更新UI线程,用发消息的机制更新UI界面,呈现给用户。这样就解决了子线程更新UI的问题。但是费时的任务操作总会启动一些匿名的子线程,太多的子线程给系统带来巨大的负担,随之带来一些性能问题。因此android提供了一个工具类AsyncTask,顾名思义异步执行任务。这个AsyncTask生来就是处理一些后台的比较耗时的任务,给用户带来良好用户体验的,从编程的语法上显得优雅了许多,不再需要子线程和Handler就可以完成异步操作并且刷新用户界面。

先大概认识下Android.os.AsyncTask类:

* android的类AsyncTask对线程间通讯进行了包装,提供了简易的编程方式来使后台线程和UI线程进行通讯:后台线程执行异步任务,并把操作结果通知UI线程。

* AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。
* Params 启动任务执行的输入参数,比如HTTP请求的URL。
* Progress 后台任务执行的百分比。
* Result 后台执行任务最终返回的结果,比如String,Integer等。

* AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,开发者需要实现这些方法。

* 1) 继承AsyncTask
* 2) 实现AsyncTask中定义的下面一个或几个方法
* onPreExecute(), 该方法将在执行实际的后台操作前被UI 线程调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条,或者一些控件的实例化,这个方法可以不用实现。
* doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台处理工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
* onProgressUpdate(Progress...),在publishProgress方法被调用后,UI 线程将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
* onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI 线程调用,后台的计算结果将通过该方法传递到UI 线程,并且在界面上展示给用户.

* onCancelled(),在用户取消线程操作的时候调用。在主线程中调用onCancelled()的时候调用。

为了正确的使用AsyncTask类,以下是几条必须遵守的准则:

1) Task的实例必须在UI 线程中创建

2) execute方法必须在UI 线程中调用

3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法,需要在UI线程中实例化这个task来调用。

4) 该task只能被执行一次,否则多次调用时将会出现异常

doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。

下面通过一个Demo来说明如何使用Android.os.AsyncTask类,通过进度条来显示进行的进度,然后用TextView来显示进度值。程序结构图如下:

[1] \layout\main.xml 布局文件源码如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent"    >    <TextView         android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="Hello , Welcome to Andy's Blog!"/>    <Button       android:id="@+id/download"       android:layout_width="fill_parent"       android:layout_height="wrap_content"       android:text="Download"/>    <TextView         android:id="@+id/tv"       android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="当前进度显示"/>    <ProgressBar       android:id="@+id/pb"       android:layout_width="fill_parent"       android:layout_height="wrap_content"       style="?android:attr/progressBarStyleHorizontal"/></LinearLayout>

[2] /src中的MainActivity.java源码如下:

package com.andyidea.demo;import android.app.Activity;import android.os.AsyncTask;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.ProgressBar;import android.widget.TextView;public class MainActivity extends Activity {            Button download;    ProgressBar pb;    TextView tv;        /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        pb=(ProgressBar)findViewById(R.id.pb);        tv=(TextView)findViewById(R.id.tv);                download = (Button)findViewById(R.id.download);        download.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                DownloadTask dTask = new DownloadTask();                dTask.execute(100);            }        });    }        class DownloadTask extends AsyncTask<Integer, Integer, String>{        //后面尖括号内分别是参数(例子里是线程休息时间),进度(publishProgress用到),返回值 类型                @Override        protected void onPreExecute() {            //第一个执行方法            super.onPreExecute();        }                @Override        protected String doInBackground(Integer... params) {            //第二个执行方法,onPreExecute()执行完后执行            for(int i=0;i<=100;i  ){                pb.setProgress(i);                publishProgress(i);                try {                    Thread.sleep(params[0]);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            return "执行完毕";        }        @Override        protected void onProgressUpdate(Integer... progress) {            //这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数            //但是这里取到的是一个数组,所以要用progesss[0]来取值            //第n个参数就用progress[n]来取值            tv.setText(progress[0]+"%");            super.onProgressUpdate(progress);        }        @Override        protected void onPostExecute(String result) {            //doInBackground返回时触发,换句话说,就是doInBackground执行完后触发            //这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕"            setTitle(result);            super.onPostExecute(result);        }            }}

[3] 下面看下程序的运行结果截图:

更多相关文章

  1. Android笔记-MultiThreading in Android(1)-Thread,Looper,Handl
  2. Android的内存泄漏和调试
  3. EventBus :概述及基本概念 《一》
  4. Android(安卓)内存管理 &Memory Leak & OOM 分析
  5. Android(安卓)& iOS 开发全面对比分析
  6. Android高手进阶教程(二十五)之---Android(安卓)中的AIDL!!!
  7. Android(安卓)4.0 触摸屏消息(二大写的二)
  8. Android中的线程处理
  9. Android线程模型解析(包括UI的更新)

随机推荐

  1. android翻译
  2. Android(安卓)Paint
  3. Android的UI主线程和子线程
  4. Android 4.4 音量调节流程分析(一)
  5. Android 中几个重要的View
  6. Linux下搭建Android开发环境及Android真
  7. android中实现view的更新UI有两组方法的
  8. android Animation动画效果基础
  9. Android(安卓)View 绘制流程之四:绘制流程
  10. 《Android开发从零开始》――18.消息处理