Android开发笔记之:消息循环与Looper的详解

Understanding Looper
Looper是用于给一个线程添加一个消息队列(MessageQueue),并且循环等待,当有消息时会唤起线程来处理消息的一个工具,直到线程结束为止。通常情况下不会用到Looper,因为对于Activity,Service等系统组件,Frameworks已经为我们初始化好了线程(俗称的UI线程或主线程),在其内含有一个Looper,和由Looper创建的消息队列,所以主线程会一直运行,处理用户事件,直到某些事件(BACK)退出。
如果,我们需要新建一个线程,并且这个线程要能够循环处理其他线程发来的消息事件,或者需要长期与其他线程进行复杂的交互,这时就需要用到Looper来给线程建立消息队列。
使用Looper也非常的简单,它的方法比较少,最主要的有四个:
    public static prepare();
    public static myLooper();
    public static loop();
    public void quit();
使用方法如下:
1. 在每个线程的run()方法中的最开始调用Looper.prepare(),这是为线程初始化消息队列。
2. 之后调用Looper.myLooper()获取此Looper对象的引用。这不是必须的,但是如果你需要保存Looper对象的话,一定要在prepare()之后,否则调用在此对象上的方法不一定有效果,如looper.quit()就不会退出。
3. 在run()方法中添加Handler来处理消息
4. 添加Looper.loop()调用,这是让线程的消息队列开始运行,可以接收消息了。
5. 在想要退出消息循环时,调用Looper.quit()注意,这个方法是要在对象上面调用,很明显,用对象的意思就是要退出具体哪个Looper。如果run()中无其他操作,线程也将终止运行。
下面来看一个实例
实例
这个例子实现了一个执行任务的服务:


代码如下:

public class LooperDemoActivity extends Activity {
    private WorkerThread mWorkerThread;
    private TextView mStatusLine;
    private Handler mMainHandler;

@Override
    public void onCreate(Bundle icicle) {
 super.onCreate(icicle);
 setContentView(R.layout.looper_demo_activity);
 mMainHandler = new Handler() {
     @Override
     public void handleMessage(Message msg) {
  String text = (String) msg.obj;
  if (TextUtils.isEmpty(text)) {
      return;
  }
  mStatusLine.setText(text);
     }
 };

mWorkerThread = new WorkerThread();
 final Button action = (Button) findViewById(R.id.looper_demo_action);
 action.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
  mWorkerThread.executeTask("please do me a favor");
     }
 });
 final Button end = (Button) findViewById(R.id.looper_demo_quit);
 end.setOnClickListener(new View.OnClickListener() {
     public void onClick(View v) {
  mWorkerThread.exit();
     }
 });
 mStatusLine = (TextView) findViewById(R.id.looper_demo_displayer);
 mStatusLine.setText("Press 'do me a favor' to execute a task, press 'end of service' to stop looper thread");
    }

@Override
    public void onDestroy() {
 super.onDestroy();
 mWorkerThread.exit();
 mWorkerThread = null;
    }

private class WorkerThread extends Thread {
 protected static final String TAG = "WorkerThread";
 private Handler mHandler;
 private Looper mLooper;

public WorkerThread() {
     start();
 }

public void run() {
     // Attention: if you obtain looper before Looper#prepare(), you can still use the looper
     // to process message even after you call Looper#quit(), which means the looper does not
     //really quit.
     Looper.prepare();
     // So we should call Looper#myLooper() after Looper#prepare(). Anyway, we should put all stuff between Looper#prepare()
     // and Looper#loop().
     // In this case, you will receive "Handler{4051e4a0} sending message to a Handler on a dead thread
     // 05-09 08:37:52.118: W/MessageQueue(436): java.lang.RuntimeException: Handler{4051e4a0} sending message
     // to a Handler on a dead thread", when try to send a message to a looper which Looper#quit() had called,
     // because the thread attaching the Looper and Handler dies once Looper#quit() gets called.
     mLooper = Looper.myLooper();
     // either new Handler() and new Handler(mLooper) will work
     mHandler = new Handler(mLooper) {
  @Override
  public void handleMessage(Message msg) {
      /*
       * Attention: object Message is not reusable, you must obtain a new one for each time you want to use it.
       * Otherwise you got "android.util.AndroidRuntimeException: { what=1000 when=-15ms obj=it is my please
       * to serve you, please be patient to wait!........ } This message is already in use."
       */
//      Message newMsg = Message.obtain();
      StringBuilder sb = new StringBuilder();
      sb.append("it is my please to serve you, please be patient to wait!\n");
      Log.e(TAG, "workerthread, it is my please to serve you, please be patient to wait!");
      for (int i = 1; i < 100; i++) {
   sb.append(".");
   Message newMsg = Message.obtain();
   newMsg.obj = sb.toString();
   mMainHandler.sendMessage(newMsg);
   Log.e(TAG, "workthread, working" + sb.toString());
   SystemClock.sleep(100);
      }
      Log.e(TAG, "workerthread, your work is done.");
      sb.append("\nyour work is done");
      Message newMsg = Message.obtain();
      newMsg.obj = sb.toString();
      mMainHandler.sendMessage(newMsg);
  }
     };
     Looper.loop();
 }

public void exit() {
     if (mLooper != null) {
  mLooper.quit();
  mLooper = null;
     }
 }

// This method returns immediately, it just push an Message into Thread's MessageQueue.
 // You can also call this method continuously, the task will be executed one by one in the
 // order of which they are pushed into MessageQueue(they are called).
 public void executeTask(String text) {
     if (mLooper == null || mHandler == null) {
  Message msg = Message.obtain();
  msg.obj = "Sorry man, it is out of service";
  mMainHandler.sendMessage(msg);
  return;
     }
     Message msg = Message.obtain();
     msg.obj = text;
     mHandler.sendMessage(msg);
 }
    }
}

这个实例中,主线程中执行任务仅是给服务线程发一个消息同时把相关数据传过去,数据会打包成消息对象(Message),然后放到服务线程的消息队列中,主线程的调用返回,此过程很快,所以不会阻塞主线程。服务线程每当有消息进入消息队列后就会被唤醒从队列中取出消息,然后执行任务。服务线程可以接收任意数量的任务,也即主线程可以不停的发送消息给服务线程,这些消息都会被放进消息队列中,服务线程会一个接着一个的执行它们----直到所有的任务都完成(消息队列为空,已无其他消息),服务线程会再次进入休眠状态----直到有新的消息到来。
如果想要终止服务线程,在mLooper对象上调用quit(),就会退出消息循环,因为线程无其他操作,所以整个线程也会终止。
需要注意的是当一个线程的消息循环已经退出后,不能再给其发送消息,否则会有异常抛出"RuntimeException: Handler{4051e4a0} sending message to a Handler on a dead thread"。所以,建议在Looper.prepare()后,调用Looper.myLooper()来获取对此Looper的引用,一来是用于终止(quit()必须在对象上面调用); 另外就是用于接收消息时检查消息循环是否已经退出(如上例)。

(0)

相关推荐

  • android开发教程之使用looper处理消息队列

    复制代码 代码如下: package com.yanjun; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; public class HandlerActivity extends Activity { @Ov

  • android的消息处理机制(图文+源码分析)—Looper/Handler/Message

    这篇文章写的非常好,深入浅出,关键还是一位大三学生自己剖析的心得.这是我喜欢此文的原因.下面请看正文: 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设 计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android的消息处理机 制,我看了Looper,Handler,Message这几个类的源码,结果又

  • Android中的Looper对象详细介绍

    Java 官网对Looper对象的说明: public class Looperextends ObjectClass used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loo

  • Android消息处理机制Looper和Handler详解

    Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理. Handler:处理者,负责Message的发送及处理.使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等. MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行.当然,存放Message并非实际意义的保存,而是将Message以链表的方

  • Android 线程之自定义带消息循环Looper的实例

    Android 线程之自定义带消息循环Looper的实例 Android系统的UI线程是一种带消息循环(Looper)机制的线程,同时Android也提供了封装有消息循环(Looper)的HandlerThread类,这种线程,可以绑定Handler()对象,并通过Handler的sendMessage()函数向线程发送消息,通过handleMessage()函数,处理线程接收到的消息.这么说比较抽象,那么,本文就利用基础的Java类库,实现一个带消息循环(Looper)的线程,以帮助初学者理解

  • Android开发中Looper.prepare()和Looper.loop()

    什么时候需要 Looper Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Looper.loop()来使消息循环起作用,使用Looper.prepare()和Looper.loop()创建了消息队列就可以让消息处理在该线程中完成. 使用Looper需要注意什么 写在Looper.loop()之后的代码不会被立即执行,当调用后mHandler.getLoo

  • Android开发笔记之:消息循环与Looper的详解

    Understanding LooperLooper是用于给一个线程添加一个消息队列(MessageQueue),并且循环等待,当有消息时会唤起线程来处理消息的一个工具,直到线程结束为止.通常情况下不会用到Looper,因为对于Activity,Service等系统组件,Frameworks已经为我们初始化好了线程(俗称的UI线程或主线程),在其内含有一个Looper,和由Looper创建的消息队列,所以主线程会一直运行,处理用户事件,直到某些事件(BACK)退出.如果,我们需要新建一个线程,并

  • Android开发笔记之:ListView刷新顺序的问题详解

    背景一个典型的ListView,每个Item显示一个TextView,代表一个Task,需要实现二个编辑方式:一个是用CheckBox来标识任务已经完成,另一个要实现的编辑是删除任务.对于完成的CheckBox就直接放在布局中就可,但对于删除不想使用ContextMenu来实现编辑,对于像iOS中那样的列表,它的删除都是通过对列表中每个项目的手势来触发.这个实现起来并不难,可以用一个ViewSwitcher,Checkbox和删除按扭是放入其中,让ViewSwitcher来控制显示哪一个,正常情

  • Android开发使用RecyclerView添加点击事件实例详解

    目录 引言 一.RecyclerView基本使用 1. 添加适配器Adapter 2. 创建列表的每个项的item_layout.xml文件 3. 在activity中使用 二.RecyclerView点击事件详细步骤 1. 在RecyclerView对应的Adapter类里面新建接口 2. 在Adapter类里创建setOnItemClickListener方法 3. 在Adapter类的onBindViewHolder里给每个item设置回调 4. 在RecyclerView对应的Activ

  • Android开发中的几种网络请求方式详解

    Android应用经常会和服务器端交互,这就需要手机客户端发送网络请求,下面介绍四种常用网络请求方式,我这边是通过Android单元测试来完成这四种方法的,还不清楚Android的单元测试的同学们请看Android开发技巧总结中的Android单元测试的步骤一文. Java.NET包中的HttpURLConnection类 Get方式: // Get方式请求 public static void requestByGet() throws Exception { String path = "h

  • Android开发教程之调用摄像头功能的方法详解

    本文实例讲述了Android调用摄像头功能的方法.分享给大家供大家参考,具体如下: 我们要调用摄像头的拍照功能,显然 第一步必须加入调用摄像头硬件的权限,拍完照后我们要将图片保存在SD卡中,必须加入SD卡读写权限,所以第一步,我们应该在Android清单文件中加入以下代码 摄像头权限: <uses-permission android:name="android.permission.CAMERA"/> SD卡读写权限: <uses-permission androi

  • Flask框架学习笔记之消息提示与异常处理操作详解

    本文实例讲述了Flask框架学习笔记之消息提示与异常处理操作.分享给大家供大家参考,具体如下: flask通过flash方法来显示提示消息: from flask import Flask, flash, render_template, request, abort app = Flask(__name__) app.secret_key = '520' @app.route('/') def index(): flash("Hello loli") return render_te

  • Android开发实现从相册中选择照片功能详解

    本文实例讲述了Android开发实现从相册中选择照片功能.分享给大家供大家参考,具体如下: 实际效果图: 代码实现: 1. 权限配置 2. 点击事件绑定 3. 相册访问 4. 根据路径设置图片 5. 其他方法 权限 首先,现在 mainfest.xml 文件中添加以下权限: <!--获取照片权限--> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <us

  • 解析Android开发优化之:对Bitmap的内存优化详解

    1) 要及时回收Bitmap的内存 Bitmap类有一个方法recycle(),从方法名可以看出意思是回收.这里就有疑问了,Android系统有自己的垃圾回收机制,可以不定期的回收掉不使用的内存空间,当然也包括Bitmap的空间.那为什么还需要这个方法呢? Bitmap类的构造方法都是私有的,所以开发者不能直接new出一个Bitmap对象,只能通过BitmapFactory类的各种静态方法来实例化一个Bitmap.仔细查看BitmapFactory的源代码可以看到,生成Bitmap对象最终都是通

  • Android开发之图形图像与动画(五)LayoutAnimationController详解

    首先需要先介绍下LayoutAnimationController: * 1.LayoutAnimationController用于为一个layout里面的控件,或者是一个ViewGroup * 里面的控件设置动画效果(即整个布局) * 2.每一个控件都有相同的动画效果 * 3.这些控件的动画效果在不同的实现显示出来 * 4.LayoutAnimationController可以在xml文件当中设置,也可以在代码中进行设置 本文就针对两种实现LayoutAnimationController的方

  • 解决Fedora14下eclipse进行android开发,ibus提示没有输入窗口的方法详解

    好不容易搭建好了开发环境,可是不管怎么按Ctr + space,ibus就是不弹出来.用鼠标点吧,上面提示没有输入窗口.真是操蛋!google了一圈也没有解决办法,我是第一个遇到这问题的人么??无奈下,干脆换输入法!将系统自带的ibus换成fcitx输入法,安装步骤为:首先切换到root1,yum install fcitx2, alternatives --config xinputrc会出来提示,会提示共有 4 个程序提供"xinputrc".选择    命令-----------

随机推荐