歡迎使用CSDN-markdown編輯器

From–http://blog.sina.com.cn/s/blog_5da93c8f0100y4ul.html
Android Looper和Handler
Message:消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。

Handler:處理者,負責Message的發送及處理。使用Handler時,需要實現handleMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等。

MessageQueue:消息隊列,用來存放Handler發送過來的消息,並按照FIFO規則執行。當然,存放Message並非實際意義的保存,而是將Message以鏈表的方式串聯起來的,等待Looper的抽取。

Looper:消息泵,不斷地從MessageQueue中抽取Message執行。因此,一個MessageQueue需要一個Looper。

Thread:線程,負責調度整個消息循環,即消息循環的執行場所。

Android 系統的消息隊列和消息循環都是針對具體線程的,一個線程可以存在(當然也可以不存在)一個消息隊列和一個消息循環(Looper),特定線程的消息只能分發給本線程,不能進行跨線程,跨進程通訊。但是創建的工作線程默認是沒有消息循環和消息隊列的,如果想讓該線程具有消息隊列和消息循環,需要在線程中首先調用Looper.prepare()來創建消息隊列,然後調用Looper.loop()進入消息循環。 如下例所示:

class LooperThread extends Thread {
public Handler mHandler;

public void run() {
  Looper.prepare();//給線程創建一個消息循環
  mHandler = new Handler() {
    public void handleMessage(Message msg) {
     // process incoming messages here
    }
  };
  Looper.loop();//使消息循環起作用,從消息隊列裏取消息,處理消息
  }
}

class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare();//給線程創建一個消息循環 mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop();//使消息循環起作用,從消息隊列裏取消息,處理消息 } }

注:寫在Looper.loop()之後的代碼不會被立即執行,當調用後 mHandler.getLooper().quit()後,loop纔會中止,其後的代碼才能得以運行。Looper對象通過MessageQueue 來存放消息和事件。一個線程只能有一個Looper,對應一個MessageQueue。
這樣你的線程就具有了消息處理機制了,在Handler中進行消息處理。

Activity是一個UI線程,運行於主線程中,Android系統在啓動的時候會爲Activity創建一個消息隊列和消息循環(Looper)。詳細實現請參考ActivityThread.java文件。
Android應用程序進程在啓動的時候,會在進程中加載ActivityThread類,並且執行這個類的main函數,應用程序的消息循環過程就是在這個main函數裏面實現的

public final class ActivityThread {
......

public static final void main(String[] args) {
......

Looper.prepareMainLooper();

......

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

......

Looper.loop();

......

thread.detach();

......
}
}

這個函數做了兩件事情,一是在主線程中創建了一個ActivityThread實例,二是通過Looper類使主線程進入消息循環中,這裏我們只關注後者。

首先看Looper.prepareMainLooper函數的實現,這是一個靜態成員函數,定義在frameworks/base/core/java/android/os/Looper.java文件中:
1 //Looper類分析
2 //沒找到合適的分析代碼的辦法,只能這麼來了。每個重要行的上面都會加上註釋
3 //功能方面的代碼會在代碼前加上一段分析
4 public class Looper {
5 //static變量,判斷是否打印調試信息。
6 private static final boolean DEBUG = false;
7 private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
8
9 // sThreadLocal.get() will return null unless you’ve called prepare().
10 //線程本地存儲功能的封裝,TLS,thread local storage,什麼意思呢?因爲存儲要麼在棧上,例如函數內定義的內部變量。要麼在堆上,例如new或者malloc出來的東西。
11 //但是現在的系統比如Linux和windows都提供了線程本地存儲空間,也就是這個存儲空間是和線程相關的,一個線程內有一個內部存儲空間,這樣的話我把線程相關的東西就存儲到
12 //這個線程的TLS中,就不用放在堆上而進行同步操作了。
//ThreadLocal並不是一個Thread,而是Thread的局部變量。
13 private static final ThreadLocal sThreadLocal = new ThreadLocal();
14 //消息隊列,MessageQueue,看名字就知道是個queue..
15 final MessageQueue mQueue;
16 volatile boolean mRun;
17 //和本looper相關的那個線程,初始化爲null
18 Thread mThread;
19 private Printer mLogging = null;
20 //static變量,代表一個UI Process(也可能是service吧,這裏默認就是UI)的主線程
21 private static Looper mMainLooper = null;
22
23
29 //往TLS中設上這個Looper對象的,如果這個線程已經設過了Looper的話就會報錯,這說明,一個線程只能設一個looper。
//
31 public static final void prepare() {
32 if (sThreadLocal.get() != null) {
33 throw new RuntimeException(“Only one Looper may be created per thread”);
34 }
35 sThreadLocal.set(new Looper());
36 }
37
38
43 //由framework設置的UI程序的主消息循環,注意,這個主消息循環是不會主動退出的
44 //
45 public static final void prepareMainLooper() {
46 prepare();
47 setMainLooper(myLooper());
48 //判斷主消息循環是否能退出….
49 //通過quit函數向looper發出退出申請
50 if (Process.supportsProcesses()) {
51 myLooper().mQueue.mQuitAllowed = false;
52 }
53 }
54
55 private synchronized static void setMainLooper(Looper looper) {
56 mMainLooper = looper;
57 }
58
59
61 public synchronized static final Looper getMainLooper() {
62 return mMainLooper;
63 }
64
65
69 //消息循環,整個程序就在這裏while了。
70 //這個是static函數喔!
71 public static final void loop() {
72 Looper me = myLooper();//從該線程中取出對應的looper對象
73 MessageQueue queue = me.mQueue;//取消息隊列對象…
74 while (true) {
75 Message msg = queue.next(); // might block取消息隊列中的一個待處理消息..
76 //if (!me.mRun) {//是否需要退出?mRun是個volatile變量,跨線程同步的,應該是有地方設置它。
77 // break;
78 //}
79 if (msg != null) {
80 if (msg.target == null) {
81 // No target is a magic identifier for the quit message.
82 return;
83 }
84 if (me.mLogging!= null) me.mLogging.println(
85 “>>>>> Dispatching to ” + msg.target + ” ”
86 + msg.callback + “: ” + msg.what
87 );
88 msg.target.dispatchMessage(msg);
89 if (me.mLogging!= null) me.mLogging.println(
90 “<<<<< Finished to ” + msg.target + ” ”
91 + msg.callback);
92 msg.recycle();
93 }
94 }
95 }
96
97
101 //返回和線程相關的looper
102 public static final Looper myLooper() {
103 return (Looper)sThreadLocal.get();
104 }
105
106
115 //設置調試輸出對象,looper循環的時候會打印相關信息,用來調試用最好了。
116 public void setMessageLogging(Printer printer) {
117 mLogging = printer;
118 }
119
120
125 public static final MessageQueue myQueue() {
126 return myLooper().mQueue;
127 }
128 //創建一個新的looper對象,
129 //內部分配一個消息隊列,設置mRun爲true
130 private Looper() {
131 mQueue = new MessageQueue();
132 mRun = true;
133 mThread = Thread.currentThread();
134 }
135
136 public void quit() {
137 Message msg = Message.obtain();
138 // NOTE: By enqueueing directly into the message queue, the
139 // message is left with a null target. This is how we know it is
140 // a quit message.
141 mQueue.enqueueMessage(msg, 0);
142 }
143
144
147 public Thread getThread() {
148 return mThread;
149 }
150 //後面就簡單了,打印,異常定義等。
151 public void dump(Printer pw, String prefix) {
152 pw.println(prefix + this);
153 pw.println(prefix + “mRun=” + mRun);
154 pw.println(prefix + “mThread=” + mThread);
155 pw.println(prefix + “mQueue=” + ((mQueue != null) ? mQueue : “(null”));
156 if (mQueue != null) {
157 synchronized (mQueue) {
158 Message msg = mQueue.mMessages;
159 int n = 0;
160 while (msg != null) {
161 pw.println(prefix + ” Message ” + n + “: ” + msg);
162 n++;
163 msg = msg.next;
164 }
165 pw.println(prefix + “(Total messages: ” + n + “)”);
166 }
167 }
168 }
169
170 public String toString() {
171 return “Looper{”
172 + Integer.toHexString(System.identityHashCode(this))
173 + “}”;
174 }
175
176 static class HandlerException extends Exception {
177
178 HandlerException(Message message, Throwable cause) {
179 super(createMessage(cause), cause);
180 }
181
182 static String createMessage(Throwable cause) {
183 String causeMsg = cause.getMessage();
184 if (causeMsg == null) {
185 causeMsg = cause.toString();
186 }
187 return causeMsg;
188 }
189 }
190 }

那怎麼往這個消息隊列中發送消息呢??調用looper的static函數myQueue可以獲得消息隊列,這樣你就可用自己往裏邊插入消息了。不過這種方法比較麻煩,這個時候handler類就發揮作用了。先來看看handler的代碼,就明白了。
1 class Handler{
2 ……….
3 //handler默認構造函數
4 public Handler() {
5 //這個if是幹嘛用的暫時還不明白,涉及到java的深層次的內容了應該
6 if (FIND_POTENTIAL_LEAKS) {
7 final Class

歡迎使用Markdown編輯器寫博客

本Markdown編輯器使用StackEdit修改而來,用它寫博客,將會帶來全新的體驗哦:

  • Markdown和擴展Markdown簡潔的語法
  • 代碼塊高亮
  • 圖片鏈接和圖片上傳
  • LaTex數學公式
  • UML序列圖和流程圖
  • 離線寫博客
  • 導入導出Markdown文件
  • 豐富的快捷鍵

快捷鍵

  • 加粗 Ctrl + B
  • 斜體 Ctrl + I
  • 引用 Ctrl + Q
  • 插入鏈接 Ctrl + L
  • 插入代碼 Ctrl + K
  • 插入圖片 Ctrl + G
  • 提升標題 Ctrl + H
  • 有序列表 Ctrl + O
  • 無序列表 Ctrl + U
  • 橫線 Ctrl + R
  • 撤銷 Ctrl + Z
  • 重做 Ctrl + Y

Markdown及擴展

Markdown 是一種輕量級標記語言,它允許人們使用易讀易寫的純文本格式編寫文檔,然後轉換成格式豐富的HTML頁面。 —— [ 維基百科 ]

使用簡單的符號標識不同的標題,將某些文字標記爲粗體或者斜體,創建一個鏈接等,詳細語法參考幫助?。

本編輯器支持 Markdown Extra ,  擴展了很多好用的功能。具體請參考Github.

表格

Markdown Extra 表格語法:

項目 價格
Computer $1600
Phone $12
Pipe $1

可以使用冒號來定義對齊方式:

項目 價格 數量
Computer 1600 元 5
Phone 12 元 12
Pipe 1 元 234

定義列表

Markdown Extra 定義列表語法:
項目1
項目2
定義 A
定義 B
項目3
定義 C

定義 D

定義D內容

代碼塊

代碼塊語法遵循標準markdown代碼,例如:

@requires_authorization
def somefunc(param1='', param2=0):
    '''A docstring'''
    if param1 > param2: # interesting
        print 'Greater'
    return (param2 - param1 + 1) or None
class SomeClass:
    pass
>>> message = '''interpreter
... prompt'''

腳註

生成一個腳註1.

目錄

[TOC]來生成目錄:

數學公式

使用MathJax渲染LaTex 數學公式,詳見math.stackexchange.com.

  • 行內公式,數學公式爲:Γ(n)=(n1)!nN
  • 塊級公式:

x=b±b24ac2a

更多LaTex語法請參考 這兒.

UML 圖:

可以渲染序列圖:

Created with Raphaël 2.1.0張三張三李四李四嘿,小四兒, 寫博客了沒?李四愣了一下,說:忙得吐血,哪有時間寫。

或者流程圖:

Created with Raphaël 2.1.0開始我的操作確認?結束yesno
  • 關於 序列圖 語法,參考 這兒,
  • 關於 流程圖 語法,參考 這兒.

離線寫博客

即使用戶在沒有網絡的情況下,也可以通過本編輯器離線寫博客(直接在曾經使用過的瀏覽器中輸入write.blog.csdn.net/mdeditor即可。Markdown編輯器使用瀏覽器離線存儲將內容保存在本地。

用戶寫博客的過程中,內容實時保存在瀏覽器緩存中,在用戶關閉瀏覽器或者其它異常情況下,內容不會丟失。用戶再次打開瀏覽器時,會顯示上次用戶正在編輯的沒有發表的內容。

博客發表後,本地緩存將被刪除。 

用戶可以選擇 把正在寫的博客保存到服務器草稿箱,即使換瀏覽器或者清除緩存,內容也不會丟失。

注意:雖然瀏覽器存儲大部分時候都比較可靠,但爲了您的數據安全,在聯網後,請務必及時發表或者保存到服務器草稿箱

瀏覽器兼容

  1. 目前,本編輯器對Chrome瀏覽器支持最爲完整。建議大家使用較新版本的Chrome。
  2. IE9以下不支持
  3. IE9,10,11存在以下問題
    1. 不支持離線功能
    2. IE9不支持文件導入導出
    3. IE10不支持拖拽文件導入


  1. 這裏是 腳註內容.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章