android常见类(Thread Handler Looper相关)

NOTE: 以下路径相对于android源码下

以下类容主要介绍,线程中消息处理及循环


〇.关系图

为了便于理解,从网络上摘录的关系流程图,这些图在看完代码再来看比较好。






一、ThreadLocal

线程局部存储(ThreadLocalStorage)当从同一线程中引用该变量时,其值总是相同;而从不同的线程中引用该变量时,其值应该不同

方法成员变量:仅在方法内部有效

类成员变量:仅在对象内部有效

线程局部存储(TLS)变量:在本线程内的任何对象内保持一致

静态变量:在本进程内的任何对象内保持一致

跨进程通信(IPC)变量:一般使用 Binder 定义,在所有进程中保持


源码:libcore/luni/src/main/java/java/lang/ThreadLocal.java

编译后放在out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/java/lang/ThreadLocal.java

ThreadLocal类提供了2个关键函数:

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

get: 获取调用线程的局部变量

set/get结果值和调用这个函数的线程有关

本例中,用set/get来保存和获取当前线程的Looper对象

二、Looper

./frameworks/base/core/java/android/os/Looper.java

作用:

    1、调用该类中的静态方法 prepare() 创建一个消息队列

     在 Looper 的静态方法 prepare() 中,给线程局部存储变量中添加一个新的 Looper 对象:

复制代码
  1. public static final void prepare() {  
  2.     if (sThreadLocal.get() != null) {  
  3.         throw new RuntimeException("Only one Looper may be created per thread");  
  4.     }  
  5.     sThreadLocal.set(new Looper());  
  6. }  
复制代码

    Looper 的构造方法中创建了一个 MessageQueue 对象:

复制代码
  1. private Looper() {  
  2.     mQueue = new MessageQueue();  
  3.     mRun = true;  
  4.     mThread = Thread.currentThread();  
  5. }  
复制代码

   2、提供静态方法 loop() ,使调用该方法的线程进行无限循环,并从消息队列中读取消息

复制代码
  1.  1 public static final void loop() {  
  2.  2     Looper me = myLooper();  
  3.  3     MessageQueue queue = me.mQueue;  
  4.  4     while (true) {  
  5.  5         Message msg = queue.next(); // might block
  6.  6         //if (!me.mRun) {  
  7.  7         //    break;  
  8.  8         //}  
  9.  9   
  10. 10         if (msg != null) {  
  11. 11             if (msg.target == null) {  
  12. 12                 // No target is a magic identifier for the quit message.  
  13. 13                 return;  
  14. 14             }  
  15. 15             if (me.mLogging!= null) me.mLogging.println(  
  16. 16                     ">>>>> Dispatching to " + msg.target + " "  
  17. 17                     + msg.callback + ": " + msg.what  
  18. 18             );  
  19. 19               
  20. 20             msg.target.dispatchMessage(msg);  
  21. 21   
  22. 22             if (me.mLogging!= null) me.mLogging.println(  
  23. 23                     "<<<<< Finished to    " + msg.target + " "  
  24. 24                     + msg.callback);  
  25. 25   
  26. 26                 msg.recycle();  
  27. 27          }  
  28. 28      }  
  29. 29 }  
复制代码

1)调用 myLooper() 函数返回当前线程的 Looper对象,即调用 sThreadLocal.get() 方法返回当前线程 ID 对应的 Looper 对象

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

2)通过 Looper 对象获取线程的消息队列

  1. MessageQueue queue = me.mQueue;  

3)进入 while 无限循环

    (1)调用MessageQueue 对象的 next() 方法取出队列中的消息(如果当前队列为空,则当前线程会被挂起,也就是说 next() 内部会暂停当前线程)

  1. Message msg = queue.next();   

    (2)回调 dispatchMessage(msg) 完成对消息的处理。msg 变量的类型是 Message, msg.target 的类型是 Handler

  1. msg.target.dispatchMessage(msg);  

    (3)处理完消息后,调用 msg.recycle() 回收 Message 对象占用的系统资源。因为 Message 类内部使用了一个数据池保存 Message 对象,从而避免不停的创建和删除 Message 对象,因此每次处理完该消息后需要将 Message 对象表明为空闲状态

 

    对程序员来讲,当需要把一个线程变为异步消息处理线程时,应该在 Thread 类的 run() 方法中先调用 Looper.prepare() 为该线程创建一个 MessageQueue 对象,然后再调用 Looper.loop() 方法,使当前线程进入消息处理循环,当然发送消息线程发送消息也是调用同一个mHandler来sendMessage(msg)

    基本框架如下:

{	
	//...
	class LooperThread extends Thread {
		  public Handler mHandler;

		  public void run() {
			  Looper.prepare();	//普通线程接收消息必要步骤一,prepare(),创建了MessageQueue

			  mHandler = new Handler() {	//要重写handleMessage()函数,用于loop()中调用handlerMessage()进行回调处理发送过来的消息
				  public void handleMessage(Message msg) {
					  // process incoming messages here
					  //....
				  }
			  };

			  Looper.loop();//普通线程接收消息必要步骤二,loop(),进入消息处理循环
		  }
	}

	private class WorkerThread extends Thread {	
		//...
		
		@Override
		public void run() {

			String sdPath = Environment.getExternalStorageDirectory().getPath();
			Message msg = new Message();
			
			//...
			
			mHandler.sendMessage(msg);

		}

	}	
	//...
}

三、MessageQueue

./frameworks/base/core/java/android/os/MessageQueue.java

1、消息在 MessageQueue 中以 Message 表示,消息以链表的结构进行保存

2、MessageQueue 中两个主要的方法“取出消息”和“添加消息”,这个一般由Handler中的方法通过MessageQueue 方法间接操作

    1)取出消息 next()

复制代码
  1. final Message next() {  
  2.         ...  
  3.         // Try to retrieve the next message, returning if found.  
  4.         synchronized (this) {  
  5.             now = SystemClock.uptimeMillis();  
  6.             Message msg = pullNextLocked(now);  
  7.             if (msg != nullreturn msg;  
  8.             if (tryIdle && mIdleHandlers.size() > 0) {  
  9.                 idlers = mIdleHandlers.toArray();  
  10.             }  
  11.         }  
  12.         ...  
  13. }  
复制代码

    2)添加消息 enquenceMessage()

    

复制代码
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.         ...  
  3.         synchronized (this) {  
  4.             ...  
  5.   
  6.             msg.when = when;  
  7.             //Log.d("MessageQueue", "Enqueing: " + msg);  
  8.             Message p = mMessages;  
  9.             if (p == null || when == 0 || when < p.when) {  
  10.                 msg.next = p;  
  11.                 mMessages = msg;  
  12.                 this.notify();  
  13.             } else {  
  14.                 Message prev = null;  
  15.                 while (p != null && p.when <= when) {  
  16.                     prev = p;  
  17.                     p = p.next;  
  18.                 }  
  19.                 msg.next = prev.next;  
  20.                 prev.next = msg;  
  21.                 this.notify();  
  22.             }  
  23.         }  
  24.         return true;  
  25.     }  
复制代码

 

四、Handler

./frameworks/base/core/java/android/os/Handler.java

       1、尽管 MessageQueue 提供了直接读/写的函数接口,但对于应用程序员而言一般不直接读/写消息队列。在Looper.loop() 函数中,当取出消息后回调 msg.target 的 handleMessage() 方法,而 msg.target 的类型正是 Handler。

复制代码
  1. public void dispatchMessage(Message msg) {  
  2.         if (msg.callback != null) {  
  3.             handleCallback(msg);  
  4.         } else {  
  5.             if (mCallback != null) {  
  6.                 if (mCallback.handleMessage(msg)) {  
  7.                     return;  
  8.                 }  
  9.             }  
  10.             handleMessage(msg);  
  11.         }  
  12.     }  
复制代码

       我们一般用 Handler 类向消息队列中发送消息,并重载 Handler 类的 handleMessage() 函数添加消息处理代码。

       2、Handler 对象只能添加到有消息队列的线程中,否则发生异常

复制代码
  1. mLooper = Looper.myLooper();  
  2. if (mLooper == null) {  
  3.     throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");  
  4. }  
复制代码

       因此,在构造 Handler 对象前必须已经执行过 Looper.prepare() ,但 Looper.prepare() 不能被执行两次。

 一个线程或者说一个looper,可以有多个Handler,比如一个主线程接收多个worker线程发来的消息,多个worker线程可以使用同一个Handler来发送消息(当然也只能在一个Handler接收处理),也可以不同worker线程使用不同Handler来发送(当然是由对应的Handler来接收处理)

五、UI线程和自定义 Thread的区别

1、UI线程

UI线程是从 ActivityThread 运行的,在 ActivityThread 中的 main() 方法中已经使用 Looper.prepareMainLooper() 为线程添加了 Looper 对象(即已经创建了消息队列MessageQueue),如下代码片:

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

<span style="white-space:pre">	</span>//...

        Looper.prepareMainLooper();//调用了prepare()即new 一个Looper对象,这个Looper构造函数中创建了MessageQueue

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

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        AsyncTask.init();

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

        Looper.loop();//调用函数Loop,用于UI线程中进行循环消息处理

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

2、自定义 Thread 

自定义 Thread 是一个裸线程,没有Looper,也就没有MessageQueue,因此不能直接在 Thread 中定义 Handler对象(new Handler时会获取当前线程的Looper对象),也就不能处理Message。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值