《深入理解Android》学习笔记1——Handler 和 Looper 分析

        Android之Handler 和 Looper 分析

      当应用程序第一次启动时系统会为他启动一个主线程,主要负责处理与UI相关的操作,这个主线程就是我们常说的UI线程,android的UI操作不是线程安全的,并且只能在UI线程中进行,如果在其他线程中操作就会报 ( CalledFromWrongThreadException ) 。同时,应用如果对用户的操作响应时间超过5s,系统就会弹出无响应提示 ( ANR ) ,因此一些费时的操作 ( 例如访问网络,读写文件等 ) 又必须开启 一个线程进行。为了解决类似的问题,Android提供了一个Message Queue(消息队列)并结合Handler和Looper组件来实现。
        1、Message Queue消息队列是用来存放Handler发送的消息,按照先进先出原则,每一个Message Queue对应一个Handler,
        2、Handler是消息的处理者,负责消息的发送和处理。处理消息需要重写handleMessage方法,根据发送的内容执行对应的操作,例如更新UI等。Handler 可通过两种方式发送消息:sendMessage跟post。sendMessage发送的是一个Message对象,由Handler的handleMessage方法处理,post发送的是一个runnable对象,由自己执行。
        3、Looper是一个消息循环,不断的从Message Queue中取出非NULL的Message,并调用Message的target ( 当Message通过sendMessage发送到对应的Message Queue时,在该函数里面设置了该Message的target属性为当前Handler )指向的那个Handler的dispatchMessage函数对Messaga进行处理。在dispatchMessage函数里面,如何处理Message由用户决定,三个判断,处理的优先级如下:

      (1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
      (2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
      (3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。从ActivityThread.java的源码可以看出

	
    public static void main(String[] args) {
        SamplingProfilerIntegration.start();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();	//这里为主线程新建了一个Looper,而用户自己new出来的线程是没有的
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }


这个Looper对象就保存在主线程的TLS中。而Looper对象内部封装了一个消息队列。

 ==========================================================================================================================

       

         Android系统中Java的应用程序和其他系统上的相同,都是靠消息驱动来工作的,工作原理如下

        1、有一个消息队列,可以往这个消息队列中投递消息。

        2、有一个消息循环,不断从消息队列中取出消息。

        在Android系统中,这些工作主要由Looper和Handler来实现

        1、Looper类,用于封装消息循环,并且有一个消息队列。

        2、Handler类,像辅助类,封装了消息投递、消息处理等接口。

一、Looper类分析

        我们以一个Looper的常见例子来分析Looper类。

class LooperThread extends Thread {
	public Handler mHandler;
	public void run() {
		//调用prepare
		Looper.prepare();
		……
		//进入消息循环
		Looper.loop();
	}
}

        代码中有两个关键调用Looper.prepare()和Looper.loop();下面就来分析这两个函数

1、Looperprepare函数如下

public static final void prepare() {
	//一个Looper只能调用一次prepare
	if(sThreadLocal.get() != null) {
		throw new RunTimeExcepition(“Only one Looper may be created per thread”);
	}
	//构造一个Looper对象,设置到调用线程的局部变量中。
	sThreadLocal.set(new Looper());
}
//sThreadLocal定义
private static final ThreadLocal sThreadLocal = new ThreadLocal();

       ThreadLocalJava中的线程局部变量类,全名应该是Thread Local Variable。它的实现和操作系统提供的线程本地存储(TLS)有关系。该类有两个关键函数:

        a、set:设置调用线程的局部变量

        b、get:获取调用现场的局部变量

 

        由上面可知,prepare会在调用线程的局部变量中设置一个Looper对象。这个调用线程就是LooperThreadrun线程。Looper对象的构造函数如下

private Looper() {
	mQueue = new MessageQueue();
	mRun = true;
	mThread = Thread.currentThread();
}

       prepare函数的工作其实就是在调用prepare的线程中,设置一个Looper对象,这个Looper对象就保存在这个调用线程的TLS中。而Looper对象内部封装了一个消息队列。

         也就是说,prepare函数通过ThreadLocal机制,把Looper和调用线程关联在一起了。这样做的目的是什么呢?下面我们来看第二个函数。

 

2、Looperloop函数如下

public static void loop() {
    Looper me = myLooper();  //myLooper返回保存在调用线程TLV中的Looper对象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
//取出这个Looper的消息队列
    MessageQueue queue = me.mQueue;
    
    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    
    while (true) {
        Message msg = queue.next(); // might block
		//处理消息,Message对象中有一个targer,它是Handler类型。
		//如果target为空,则表示需要退出消息循环
        if (msg != null) {
            if (msg.target == null) {
                // No target is a magic identifier for the quit message.
                return;
            }
            long wallStart = 0;
            long threadStart = 0;
            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
                wallStart = SystemClock.currentTimeMicro();
                threadStart = SystemClock.currentThreadTimeMicro();
            }
			//调用该信息的Handler,交给它的dispatchMessage函数处理
            msg.target.dispatchMessage(msg);

            if (logging != null) {
                long wallTime = SystemClock.currentTimeMicro() - wallStart;
                long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                if (logging instanceof Profiler) {
                    ((Profiler) logging).profile(msg, wallStart, wallTime,
                            threadStart, threadTime);
                }
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }
            
            msg.recycle();
        }
    }
}

//myLooper函数返回调用线程的线程局部变量,也就是存储在其中的Looper对象

public static final Looper myLooper() {
    return (Looper) sThreadLocal.get();
}

通过上面的代码分析发现,Looper的作用是:

a、封装一个消息队列

b、Looper的prepare函数把这个Looper和调用prepare的线程(也就是最终的处理线程)绑定在一起了

c、处理线程调用loop函数,处理来自消息队列的消息

 

        当事件源向这个Looper发送消息的时候,其实是把消息加到这个Looper的消息队列里了。那么,该消息就将由和Looper绑定的处理线程来处理。事件源是怎么向Looper消息队列添加信息的?看下面的Handler

LooperMessageHandler的关系

a、Looper中有一个Message队列,里面存储的是一个个待处理的Message

b、Message中有一个Handler,这个Handler是用来处理Message的。

 

 

 二、Handler类分析

        Handler中所包括的成员

    final MessageQueue mQueue;	      //Handler中也有一个消息队列
    final Looper mLooper;	      //也有一个Looper
    final Callback mCallback;	      //有一个回调用的类


        关于这几个成员变量是怎么使用的要从Handler的构造函数分析。Handler有四个构造函数,主要区别是在对上面三个成员变量的初始化上。如下:

/**
 * Default constructor associates this handler with the queue for the
 * current thread.
 *
 * If there isn't one, this handler won't be able to receive messages.
 */
public Handler() {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    //获得调用线程的Looper
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
    //得到Looper的消息队列
    mQueue = mLooper.mQueue;
    //无callback设置
    mCallback = null;
}

/**
 * Constructor associates this handler with the queue for the
 * current thread and takes a callback interface in which you can handle
 * messages.
 */
public Handler(Callback callback) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    //和上面的构造函数类似,但是多了一个设置callback
    mCallback = callback;
}

/**
 * Use the provided queue instead of the default one.
 */
public Handler(Looper looper) {
    mLooper = looper;	//looper由外部传入,是哪个线程的looper不确定
    mQueue = looper.mQueue;
    mCallback = null;
}

/**
 * Use the provided queue instead of the default one and take a callback
 * interface in which to handle messages.
 */
public Handler(Looper looper, Callback callback) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;	
}


        由构造函数的分析可知,Handler中的消息队列实际就是某个Looper的消息队列,那么这样安排的目的何在?

        你还记得怎么Looper的消息队列中插入消息吗?在不知道Handler的情况下,有一个很原始的方法可以解决这个问题:

        a、调用LoopermyQueue,返回消息队列的MessageQueue

        b、构造一个Message,填充它的成员,尤其是targer变量

        c、调用MessageQueueenqueueMessage,将消息插入消息队列。

    这种原始方法很麻烦,而且容易出错,但是有了Handler后,可以简化编程的工作。 

(1)Handler和Message
        Handler提供了一些列函数,帮组我们完成创建消息和插入消息队列的工作。如下: 

 public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
			//把Message的target设置为自己,然后加入到消息队列中
            msg.target = this;
            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }


        从这些函数可以看出,如果没有Handler的辅助,当我们自己操作MessageQueue的enqueueMessage时,得花费很多的功夫。 

(2)Handler的消息处理

        我们往Looper的消息队列中加入了一个消息,按照Looper的处理规则,它在获取消息后会调用target的dispatchMessage函数,再把这个消息派发给Handler处理,Handler对消息处理如下。

    public void dispatchMessage(Message msg) {
	
	//如果Message本身有callback,则直接交给Message的callback处理
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
			//如果本Handler设置了mCallback,则交给mCallback处理
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
//最后才交给子类处理,通常采用这种方法,通过重载handleMessage完成处理工作
            handleMessage(msg);
        }
    }


 三、Looper和Handler的同步关系

        下面通过一个例子来看他们之间的同步关系

 

class LooperThread extends Thread {
	public Looper myLooper = null;

	@Override
	public void run() {//假设run在线程2中执行
		Looper.prepare();
		//myLooper必须在这个线程中赋值
		myLooper = Looper.myLooper();
		Looper.loop();
	}
}

//这段代码在线程1中执行,并且会创建线程2
{
	LooperThread lpThread = new LooperThread();
	lpThread.start();
	Looper looper = lpThread.myLooper;
	//thread2Handler和线程2的Looper挂上钩
	Handler thread2Handler = new Handler(looper);
	//sendMessage发送的消息由线程2处理
	thread2Handler.sendMessage(new Message());
}

        这段代码的目的很简单

        a、 线程1创建线程2,并且线程2通过Looper处理消息

    b、线程1中得到线程2的Looper,并且工具这个Looper创建一个Handler,这样发送给该Handler的消息将由线程2处理

        但是,这段代码是有问题的,如果我们熟悉多线程,就会知道Looper looper = lpThread.myLooper;这行代码存在这严重问题,myLooper的创建是在线程2中,而looper的赋值在线程1中,很有可能此时线程2run函数还没来得及给myLooper赋值,这样线程1中的looper就将去到myLooper的初值,也就是null。另外

   Handler thread2Handler = new Handler(looper);不能替换为Handler thread2Handler =new Handler(Looper.myLooper());

        因为myLooper返回的是调用线程的Looper,即Thread1Looper,而不是我们想要的Thread2Looper。要解决这个问题,可以通过AndroidHandlerThread来解决这个问题。

class HandlerThread extends Thread {
	//线程1调用getLooper来获取新线程的Looper
	public Looper getLooper() {
		...
		synchronized (this) {
			while(isAlive() && mLooper == null) {
				try {
					wait();//如果新线程还没创建Looper,则等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		return mLooper;
	}
	//线程2运行它的run函数,looper就是在run线程里创建的。
	@Override
	public void run() {
		mTid = Process.myTid();
		Looper.prepare();	//创建这个线程上的Looper
		synchronized (this) {
			mLooper = Looper.myLooper();
			notifyAll();	//通知Looper的线程1,Looper已经创建好了。
		}
		Process.setThreadPriority(mPriority);
		onlooperPrepared();
		Looper.loop();
		mTid = -1;
	}
	
}

        这里通过waitnotifyAll就解决了我们的问题

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值