在Android下面也有多线程的概念,在C/C++中,子线程可以是一个函数, 一般都是一个带有循环的函数,来处理某些数据,优先线程只是一个复杂的运算过程,所以可能不需要while循环,运算完成,函数结束,线程就销毁。对于那 些需要控制的线程,一般我们都是和互斥锁相互关联,从而来控制线程的进度,一般我们创建子线程,一种线程是很常见的,那就是带有消息循环的线程。

消息循环是一个很有用的线程方式,曾经自己用C在Linux下面实现一个消息循环的机制,往消息队列里添加数据,然后异步的等待消息的返回。当消息队列为空的时候就会挂起线程,等待新的消息的加入。这是一个很通用的机制。

在Android,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新 概念。我们的主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,我们引入一个新的机制Handle,我们有消息循环,就要往消息循环里 面发送相应的消息,自定义消息一般都会有自己对应的处理,消息的发送和清除,消息的的处理,把这些都封装在Handle里面,注意Handle只是针对那 些有Looper的线程,不管是UI线程还是子线程,只要你有Looper,我就可以往你的消息队列里面添加东西,并做相应的处理。

但是这里还有一点,就是只要是关于UI相关的东西,就不能放在子线程中,因为子线程是不能操作UI的,只能进行数据、系统等其他非UI的操作。

那么什么情况下面我们的子线程才能看做是一个有Looper的线程呢?我们如何得到它Looper的句柄呢?

Looper.myLooper();获得当前的Looper

Looper.getMainLooper () 获得UI线程的Lopper

我们看看Handle的初始化函数,如果没有参数,那么他就默认使用的是当前的Looper,如果有Looper参数,就是用对应的线程的Looper。

如果一个线程中调用Looper.prepare(),那么系统就会自动的为该线程建立一个消息队列,然后调用 Looper.loop();之后就进入了消息循环,这个之后就可以发消息、取消息、和处理消息。这个如何发送消息和如何处理消息可以再其他的线程中通过 Handle来做,但前提是我们的Hanle知道这个子线程的Looper,但是你如果不是在子线程运行 Looper.myLooper(),一般是得不到子线程的looper的。

public void run() {
            synchronized (mLock) {
                Looper.prepare();
               //do something
            }
            Looper.loop();
        }

所以很多人都是这样做的:我直接在子线程中新建handle,然后在子线程中发送消息,这样的话就失去了我们多线程的意义了。

class myThread extends Thread{

private EHandler mHandler ;

public void run() {

Looper myLooper, mainLooper;

myLooper = Looper.myLooper ();

mainLooper = Looper.getMainLooper ();

String obj;

if (myLooper == null ){

mHandler = new EHandler(mainLooper);

obj = "current thread has no looper!" ;

}

else {

mHandler = new EHandler(myLooper);

obj = "This is from current thread." ;

}

mHandler .removeMessages(0);

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

mHandler .sendMessage(m);

}

}

可以让其他的线程来控制我们的handle,可以把 private EHandler mHandler ;放在外面,这样我们的发消息和处理消息都可以在外面来定义,这样增加程序代码的美观,结构更加清晰。

对如任何的Handle,里面必须要重载一个函数

public void handleMessage(Message msg)

这个函数就是我们的消息处理,如何处理,这里完全取决于你,然后通过 obtainMessage和 sendMessage等来生成和发送消息, removeMessages(0)来清除消息队列。Google真是太智慧了,这种框架的产生,我们写代码更加轻松了。

有的时候,我们的子线程想去改变UI了,这个时候千万不要再子线程中去修改,获得UI线程的Looper,然后发送消息即可。

我们来看看高焕堂的代码:

// class ac01 extends Activity {

// ………

public void onClick(View v) {

switch (v.getId()){

case 101:

t = new myThread();

t .start();

break ;

case 102:

finish();

break ;

}

}

//------------------------------------------------------

class EHandler extends Handler {

public EHandler(Looper looper) {

super (looper);

}

@Override

public void handleMessage(Message msg) {

tv .setText((String)msg. obj );

}

}

//------------------------------------------------------

class myThread extends Thread{

private EHandler mHandler ;

public void run() {

Looper myLooper, mainLooper;

myLooper = Looper.myLooper ();

mainLooper = Looper.getMainLooper ();

String obj;

if (myLooper == null ){

mHandler = new EHandler(mainLooper);

obj = "current thread has no looper!" ;

}

else {

mHandler = new EHandler(myLooper);

obj = "This is from current thread." ;

}

mHandler .removeMessages(0);

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

mHandler .sendMessage(m);

}

}

}

完全是不知所云,一坨狗屎。我们来看,在上面的run里面

Looper myLooper, mainLooper;

myLooper = Looper.myLooper (); //很明显这个会返回空,因为你还没有 prepare,不会返回Looper。

mainLooper = Looper.getMainLooper ();

建议大家在看Looper的时候不要看高焕堂的书,感觉他也不是很懂,倒还把我搞糊涂了。讲了那么多,完全是他自己的理解,他自己的理解很是复杂,关键的是把简单的问题复杂化,并且复杂之后的东西还是错的。我们看看Goole Music App的源代码。

在MediaPlaybackActivity.java中,我们可以看一下再OnCreate中的有这样的两句:

mAlbumArtWorker = new Worker("album art worker");
        mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());

很明显这两句,是构建了一个子线程。并且这个子线程还是Looper的子线程,这里很牛逼的使用了 mAlbumArtWorker.getLooper()这个函数,因为我们知道,我们能够得到子线程的Looper的途径只有一个:就是在子线程中调用 Looper.myLooper (),并且这个函数还要在我们perpare之后调用才能得到正确的Looper,但是他这里用了一个这样的什么东东 getLooper,不知道它是如何实现的?

这里有一个大概的思路,我们在子线程的的prepare之后调用 myLooper ()这个方法,然后保存在一个成员变量中,这个getLooper就返回这个东西,但是这里会碰到多线程的一个很突出的问题,同步。我们在父线程中调用 mAlbumArtWorker.getLooper(),但是想要这个返回正确的looper就必须要求我们的子线程运行了prepare,但是这个东 西实在子线程运行的,我们如何保证呢?

我们看Google是如何实现的?

private class Worker implements Runnable {
        private final Object mLock = new Object();
        private Looper mLooper;
        
        /**
         * Creates a worker thread with the given name. The thread
         * then runs a {@link android.os.Looper}.
         * @param name A name for the new thread

*/
        Worker(String name) {
            Thread t = new Thread(null, this, name);
            t.setPriority(Thread.MIN_PRIORITY);
            t.start();
            synchronized (mLock) {
                while (mLooper == null) {
                    try {
                        mLock.wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }
        }
        
        public Looper getLooper() {
            return mLooper;
        }
        
        public void run() {
            synchronized (mLock) {
                Looper.prepare();
                mLooper = Looper.myLooper();
                mLock.notifyAll();
            }
            Looper.loop();
        }
        
        public void quit() {
            mLooper.quit();
        }
    }

我们知道,一个线程类的构造函数是在主线程中完成的,所以在我们的 Worker的构造函数中我们创佳一个线程,然后让这个线程运行,这一这个线程的创建是指定一个 Runnabl,这里就是我们的Worker本身,在主线程调用 t.start();,这后,我们子线程已经创建,并且开始执行work的run方法。然后下面的代码很艺术:

synchronized (mLock) {
                while (mLooper == null) {
                    try {
                        mLock.wait();
                    } catch (InterruptedException ex) {
                    }
                }
            }

我们开始等待我们的子线程给mLooper赋值,如果不赋值我们就继续等,然后我们的子线程在运行run方法之后,在给 mLooper赋值之后,通知worker够着函数中的wait,然后我们的构造函数才能完成,所以我们说:

mAlbumArtWorker = new Worker("album art worker");

这句本身就是阻塞的,它创建了一个子线程,开启了子线程,并且等待子线程给mLooper赋值,赋值完成之后,这个函数才返回,这样才能保证我们的子线程的Looper的获取绝对是正确的,这个构思很有创意。值得借鉴。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/jinlking/archive/2009/07/09/4333456.aspx

Android Looper详解的更多相关文章

  1. Android HandlerThread详解

    概述 Android HandlerThread使用,自带Looper消息循环的快捷类. 详细 代码下载:http://www.demodashi.com/demo/10628.html 原文地址: ...

  2. android handle详解3 ThreadHandler

    在android handle详解2的基础上,我们来学习ThreadHandler ThreadHandler的本质就是对android handle详解2的实现 HandlerThread其实还是一 ...

  3. android handle详解2 主线程给子线程发送消息

    按照android handler详解分析的原理我们可以知道,在主线程中创建handle对象的时候,主线程默认创建了一个loop对象使用threalocal函数将loop对象和主线程绑定. 我们能不能 ...

  4. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  5. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  6. Android ActionBar详解

    Android ActionBar详解 分类: Android2014-04-30 15:23 1094人阅读 评论(0) 收藏 举报 androidActionBar   目录(?)[+]   第4 ...

  7. Android 签名详解

    Android 签名详解 AndroidOPhoneAnt设计模式Eclipse  在Android 系统中,所有安装 到 系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程 ...

  8. Android编译系统详解(一)

    ++++++++++++++++++++++++++++++++++++++++++ 本文系本站原创,欢迎转载! 转载请注明出处: http://blog.csdn.net/mr_raptor/art ...

  9. Android布局详解之一:FrameLayout

      原创文章,如有转载,请注明出处:http://blog.csdn.net/yihui823/article/details/6702273 FrameLayout是最简单的布局了.所有放在布局里的 ...

随机推荐

  1. 禁止用户使用密码的方式登录服务器,让其使用key的方式登录

    这里我的远程登录工具xshell 所以这里我要做的是 第一步是在xshell中配置公私钥 密钥类型选择为rsa,长度为2048,并点击下一步,如下所示: 继续下一步 如果给密钥设置了密码,那么在使用这 ...

  2. UVA - 11609 Teams (排列组合数公式)

    In a galaxy far far awaythere is an ancient game played among the planets. The specialty of the game ...

  3. modelsim与debussy联调环境的搭建

    为了方便查看波形,找来了一款软件——debussy,它的一个优点是任你查看设计内信号,只需一个波形文件,如FSDB文件.而不用像modelsim那样想看某些信号,添加了之后还要重新编译仿真,浪费了很多 ...

  4. [k8s]kube-router替代kube-proxy实现svc网络和pod网络

    本文讲解了kube-router部署,无需在部署kube-proxy了. kube-router采用lvs实现svc网络,采用bgp实现pod网络. kube-router也是基于cni网络,本文是容 ...

  5. 手机防盗之获取手机经纬度(Android)

    获取手机经纬度有gps , network , 基站 三种方式,我们可以根据定位的条件,获取一个最好的定位方式.然后将获取到经纬度信息发送到指定的手机号码中. /* * 单态只允许存在一个实例. * ...

  6. jvisualvm工具使用

    VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带(java启动时不需要特定参数,监控工具在bin/jvisualvm.exe). https:// ...

  7. Oracle PLSQL Demo - 12.定义包体[Define PACKAGE BODY]

    CREATE OR REPLACE PACKAGE BODY temp_package_demo is FUNCTION f_demo(userid NUMBER) RETURN BOOLEAN IS ...

  8. centos7 nginx安装 及MySQL-python

    1.pip install MySQL-python 出错问题 pip install -r requirement.txt 发现安装MySQL-python 出错,更新setuptools也没用 解 ...

  9. Python 按当前日期(年、月、日)创建多级目录的方法

    先看实际效果,现在时间2018.4.26 使用python脚本按照年月日生成多级目录,创建的目录可以将系统生成的日志文件放入其中,方便查阅,代码如下: #!/usr/bin/env python #c ...

  10. MySQL修改root密码的多种方法(转)

    http://jingyan.baidu.com/article/0320e2c198ad5f1b87507bc8.html ********************************* 方法1 ...