android通知線程
1. 每個Android 都應必須了解的多線程知識點~
進程是系統調度和資源分配的一個獨立單位。
在Android中,一個應用程序就是一個獨立的集成,應用運行在一個獨立的環境中,可以避免其他應用程序/進程的干擾。當我們啟動一個應用程序時,系統就會創建一個進程(該進程是從Zygote中fork出來的,有獨立的ID),接著為這個進程創建一個主線程,然後就可以運行MainActivity了,應用程序的組件默認都是運行在其進程中。開發者可以通過設置應用的組件的運行進程,在清單文件中給組件設置:android:process = "進程名";可以達到讓組件運行在不同進程中的目的。讓組件運行在不同的進程中,既有好處,也有壞處。我們依次的說明下。
好處:每一個應用程序(也就是每一個進程)都會有一個內存預算,所有運行在這個進程中的程序使用的總內存不能超過這個值,讓組件運行不同的進程中,可以讓主進程可以擁有更多的空間資源。當我們的應用程序比較大,需要的內存資源比較多時(也就是用戶會抱怨應用經常出現OutOfMemory時),可以考慮使用多進程。
壞處:每個進程都會有自己的虛擬機實例,因此讓在進程間共享一些數據變得相對困難,需要採用進程間的通信來實現數據的共享。
線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。
在Android中,線程會有那麼幾種狀態:創建、就緒、運行、阻塞、結束。當應用程序有組件在運行時,UI線程是處於運行狀態的。默認情況下,應用的所有組件的操作都是在UI線程里完成的,包括響應用戶的操作(觸摸,點擊等),組件生命周期方法的調用,UI的更新等。因此如果UI線程處理阻塞狀態時(在線程里做一些耗時的操作,如網路連接等),就會不能響應各種操作,如果阻塞時間達到5秒,就會讓程序處於ANR(application not response)狀態。
1.線程作用
減少程序在並發執行時所付出的時空開銷,提高操作系統的並發性能。
2.線程分類
守護線程、非守護線程(用戶線程)
2.1 守護線程
定義:守護用戶線程的線程,即在程序運行時為其他線程提供一種通用服務
常見:如垃圾回收線程
設置方式:thread.setDaemon(true);//設置該線程為守護線程
2.2 非守護線程(用戶線程)
主線程 & 子線程。
2.2.1 主線程(UI線程)
定義:Android系統在程序啟動時會自動啟動一條主線程
作用:處理四大組件與用戶進行交互的事情(如UI、界面交互相關)
因為用戶隨時會與界面發生交互,因此主線程任何時候都必須保持很高的響應速度,所以主線程不允許進行耗時操作,否則會出現ANR。
2.2.2 子線程(工作線程)
定義:手動創建的線程
作用:耗時的操作(網路請求、I/O操作等)
2.3 守護線程與非守護線程的區別和聯系
區別:虛擬機是否已退出,即
a. 當所有用戶線程結束時,因為沒有守護的必要,所以守護線程也會終止,虛擬機也同樣退出
b. 反過來,只要任何用戶線程還在運行,守護線程就不會終止,虛擬機就不會退出
3.線程優先順序
3.1 表示
線程優先順序分為10個級別,分別用Thread類常量表示。
3.2 設置
通過方法setPriority(int grade)進行優先順序設置,默認線程優先順序是5,即 Thread.NORM_PRIORITY。
4.線程狀態
創建狀態:當用 new 操作符創建一個線程的時候
就緒狀態:調用 start 方法,處於就緒狀態的線程並不一定馬上就會執行 run 方法,還需要等待CPU的調度
運行狀態:CPU 開始調度線程,並開始執行 run 方法
阻塞(掛起)狀態:線程的執行過程中由於一些原因進入阻塞狀態,比如:調用 sleep/wait 方法、嘗試去得到一個鎖等
結束(消亡)狀態:run 方法執行完 或者 執行過程中遇到了一個異常
(1)start()和run()的區別
通過調用Thread類的start()方法來啟動一個線程,這時此線程是處於就緒狀態,並沒有運行。調用Thread類調用run()方法來完成其運行操作的,方法run()稱為線程體,它包含了要執行的這個線程的內容,run()運行結束,此線程終止,然後CPU再調度其它線程。
(2)sleep()、wait()、yield()的區別
sleep()方法屬於Thread類,wait()方法屬於Object類。
調用sleep()方法,線程不會釋放對象鎖,只是暫停執行指定的時間,會自動恢復運行狀態;調用wait()方法,線程會放棄對象鎖,進入等待此對象的等待鎖定池,不調用notify()方法,線程永遠處於就緒(掛起)狀態。
yield()直接由運行狀態跳回就緒狀態,表示退讓線程,讓出CPU,讓CPU調度器重新調度。禮讓可能成功,也可能不成功,也就是說,回到調度器和其他線程進行公平競爭。
1.Android線程的原則
(1)為什麼不能再主線程中做耗時操作
防止ANR, 不能在UI主線程中做耗時的操作,因此我們可以把耗時的操作放在另一個工作線程中去做。操作完成後,再通知UI主線程做出相應的響應。這就需要掌握線程間通信的方式了。 在Android中提供了兩種線程間的通信方式:一種是AsyncTask機制,另一種是Handler機制。
(2)為什麼不能在非UI線程中更新UI 因為Android的UI線程是非線程安全的,應用更新UI,是調用invalidate()方法來實現界面的重繪,而invalidate()方法是非線程安全的,也就是說當我們在非UI線程來更新UI時,可能會有其他的線程或UI線程也在更新UI,這就會導致界面更新的不同步。因此我們不能在非UI主線程中做更新UI的操作。
2.Android實現多線程的幾種方式
3.為何需要多線程
多線程的本質就是非同步處理,直觀一點說就是不要讓用戶感覺到「很卡」。
4.多線程機制的核心是啥
多線程核心機制是Handler
推薦Handler講解視頻: 面試總被問到Handler?帶你從源碼的角度解讀Handler核心機制
根據上方提到的 多進程、多線程、Handler 問題,我整理了一套 Binder與Handler 機制解析的學習文檔,提供給大家進行學習參考,有需要的可以 點擊這里直接獲取!!! 裡面記錄許多Android 相關學習知識點。
2. Android——消息分發機制
什麼是 Handler 機制 ?
Handler 機制是 Android 中用於 線程間通信 的一套通信機制。
為什麼是 Handler ?Handler 機制為什麼被那麼多次的提及 ?
從Android4.0開始,Android 中網路請求強制不允許在主線程中操作,而更新UI的操作則不允許在子線程中執行。當在子線程中執行網路請求,拿到伺服器返回的數據之後,要更新UI。由於系統的要求,勢必會產生一種矛盾:數據在子線程,更新UI要在主線程。此時我們必須要把數據返回到主線程中才行,Handler機制應運而生。
Android 中針對耗時的操作,放在主線程操作,輕者會造成 UI 卡頓,重則會直接無響應,造成 Force Close。同時在 Android 3.0 以後,禁止在主線程進行網路請求。
針對耗時或者網路操作,那就不能在主線程進行直接操作了,需要放在子線程或者是工作線程中進行操作,操作完成以後,再更新主線程即 UI 線程。這里就涉及到一個問題了,在子線程執行完成以後,怎麼能更新到主線程即 UI 線程呢,針對以上問題,就需要用到 Android 的消息機制了,即: Handler, Message, MessageQueue, Looper 全家桶
Handler機制中最重要的四個對象
Handler的構造方法:
Looper :
Handler的使用:
MessageQueue:
Looper.loop()
Handler.dispatchMessage()
handler導致activity內存泄露的原因:
handler發送的消息在當前handler的消息隊列中,如果此時activity finish掉了,那麼消息隊列的消息依舊會由handler進行處理,若此時handler聲明為內部類(非靜態內部類),我們知道內部類天然持有外部類的實例引用,這樣在GC垃圾回收機制進行回收時發現這個Activity居然還有其他引用存在,因而就不會去回收這個Activity,進而導致activity泄露。
假如在子線程執行了耗時操作,這時用戶操作進入了其他的 acitvity, 那麼 MainActivity 就會被內存回收的,但是這個時候發現 Handler 還在引用著 MainActivity,內存無法及時回收,造成內存泄漏。
Handler 防止內存泄漏常見方法:
為什麼通過 Handler 可以把子線程的結果通知或者攜帶給 UI 線程 ?
這里的 Handler 指的是主線程的 Handler ,同時與 Handler 配套的 Looper , MessageQueue 是在 UI 線程初始化的,所以在子線程中調用 Handler 發送消息可以更新 UI 線程。
Looper 在 UI 線程源碼, 在 ActivityThread 類:
3. android 主線程和子線程有什麼區別
本文較為深入的分析了android中UI主線程與子線程。分享給大家供大家參考。具體如下:
在一個Android 程序開始運行的時候,會單獨啟動一個Process。默認的情況下,所有這個程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的兩種,除此之外還有Content Provider和Broadcast Receiver)都會跑在這個Process。
一個Android 程序默認情況下也只有一個Process,但一個Process下卻可以有許多個Thread。在這么多Thread當中,有一個Thread,我們稱之為UI Thread。UI Thread在Android程序運行的時候就被創建,是一個Process當中的主線程Main Thread,主要是負責控制UI界面的顯示、更新和控制項交互。在Android程序創建之初,一個Process呈現的是單線程模型,所有的任務都在一個線程中運行。因此,我們認為,UI Thread所執行的每一個函數,所花費的時間都應該是越短越好。而其他比較費時的工作(訪問網路,下載數據,查詢資料庫等),都應該交由子線程去執行,以免阻塞主線程。
那麼,UI Thread如何和其他Thread一起工作呢?常用方法是:誕生一個主線程的Handler物件,當做Listener去讓子線程能將訊息Push到主線程的Message Quene里,以便觸發主線程的handlerMessage()函數,讓主線程知道子線程的狀態,並在主線程更新UI。
例如,在子線程的狀態發生變化時,我們需要更新UI。如果在子線程中直接更新UI,通常會拋出下面的異常:
11-07 13:33:04.393: ERROR/javaBinder(1029):android.view.ViewRoot$:Only the original thread that created a view hierarchy can touch its views.
意思是,無法在子線程中更新UI。為此,我們需要通過Handler物件,通知主線程Ui Thread來更新界面。
如下,首先創建一個Handler,來監聽Message的事件:
private final int UPDATE_UI = 1;
private Handler mHandler = new MainHandler();
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI: {
Log.i("TTSDeamon", "UPDATE_UI");
showTextView.setText(editText.getText().toString());
ShowAnimation();
break;
}
default:
break;
}
}
}
或者:
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_UI: {
Log.i("TTSDeamon", "UPDATE_UI");
showTextView.setText(editText.getText().toString());
ShowAnimation();
break;
}
default:
break;
}
}
}
當子線程的狀態發生變化,則在子線程中發出Message,通知更新UI。
mHandler.sendEmptyMessageDelayed(UPDATE_UI, 0);
在我們的程序中,很多Callback方法有時候並不是運行在主線程當中的,所以如果在Callback方法中更新UI失敗,也可以採用上面的方法。
4. Android進程間和線程間通信方式
進程:是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。
線程:是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。線程自己基本上不擁有系統資源,只擁有一些在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。
區別:
(1)、一個程序至少有一個進程,一個進程至少有一個線程;
(2)、線程的劃分尺度小於進程,使得多線程程序的並發性高;
(3)、進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,但線程之間沒有單獨的地址空間,一個線程死掉就等於整個進程死掉。
---------------------
一、Android進程間通信方式
1.Bundle
由於Activity,Service,Receiver都是可以通過Intent來攜帶Bundle傳輸數據的,所以我們可以在一個進程中通過Intent將攜帶數據的Bundle發送到另一個進程的組件。
缺點:無法傳輸Bundle不支持的數據類型。
2.ContentProvider
ContentProvider是Android四大組件之一,以表格的方式來儲存數據,提供給外界,即Content Provider可以跨進程訪問其他應用程序中的數據。用法是繼承ContentProvider,實現onCreate,query,update,insert,delete和getType方法,onCreate是負責創建時做一些初始化的工作,增刪查改的方法就是對數據的查詢和修改,getType是返回一個String,表示Uri請求的類型。注冊完後就可以使用ContentResolver去請求指定的Uri。
3.文件
兩個進程可以到同一個文件去交換數據,我們不僅可以保存文本文件,還可以將對象持久化到文件,從另一個文件恢復。要注意的是,當並發讀/寫時可能會出現並發的問題。
4.Broadcast
Broadcast可以向android系統中所有應用程序發送廣播,而需要跨進程通訊的應用程序可以監聽這些廣播。
5.AIDL方式
Service和Content Provider類似,也可以訪問其他應用程序中的數據,Content Provider返回的是Cursor對象,而Service返回的是Java對象,這種可以跨進程通訊的服務叫AIDL服務。
AIDL通過定義服務端暴露的介面,以提供給客戶端來調用,AIDL使伺服器可以並行處理,而Messenger封裝了AIDL之後只能串列運行,所以Messenger一般用作消息傳遞。
6.Messenger
Messenger是基於AIDL實現的,服務端(被動方)提供一個Service來處理客戶端(主動方)連接,維護一個Handler來創建Messenger,在onBind時返回Messenger的binder。
雙方用Messenger來發送數據,用Handler來處理數據。Messenger處理數據依靠Handler,所以是串列的,也就是說,Handler接到多個message時,就要排隊依次處理。
7.Socket
Socket方法是通過網路來進行數據交換,注意的是要在子線程請求,不然會堵塞主線程。客戶端和服務端建立連接之後即可不斷傳輸數據,比較適合實時的數據傳輸
二、Android線程間通信方式
一般說線程間通信主要是指主線程(也叫UI線程)和子線程之間的通信,主要有以下兩種方式:
1.AsyncTask機制
AsyncTask,非同步任務,也就是說在UI線程運行的時候,可以在後台的執行一些非同步的操作;AsyncTask可以很容易且正確地使用UI線程,AsyncTask允許進行後台操作,並在不顯示使用工作線程或Handler機制的情況下,將結果反饋給UI線程。但是AsyncTask只能用於短時間的操作(最多幾秒就應該結束的操作),如果需要長時間運行在後台,就不適合使用AsyncTask了,只能去使用Java提供的其他API來實現。
2.Handler機制
Handler,繼承自Object類,用來發送和處理Message對象或Runnable對象;Handler在創建時會與當前所在的線程的Looper對象相關聯(如果當前線程的Looper為空或不存在,則會拋出異常,此時需要在線程中主動調用Looper.prepare()來創建一個Looper對象)。使用Handler的主要作用就是在後面的過程中發送和處理Message對象和讓其他的線程完成某一個動作(如在工作線程中通過Handler對象發送一個Message對象,讓UI線程進行UI的更新,然後UI線程就會在MessageQueue中得到這個Message對象(取出Message對象是由其相關聯的Looper對象完成的),並作出相應的響應)。
三、Android兩個子線程之間通信
面試的過程中,有些面試官可能會問Android子線程之間的通信方式,由於絕大部分程序員主要關注的是Android主線程和子線程之間的通信,所以這個問題很容易讓人懵逼。
主線程和子線程之間的通信可以通過主線程中的handler把子線程中的message發給主線程中的looper,或者,主線程中的handler通過post向looper中發送一個runnable。但looper默認存在於main線程中,子線程中沒有Looper,該怎麼辦呢?其實原理很簡單,把looper綁定到子線程中,並且創建一個handler。在另一個線程中通過這個handler發送消息,就可以實現子線程之間的通信了。
子線程創建handler的兩種方式:
方式一:給子線程創建Looper對象:
new Thread(new Runnable() {
public void run() {
Looper.prepare(); // 給這個Thread創建Looper對象,一個Thead只有一個Looper對象
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), "handleMessage", Toast.LENGTH_LONG).show();
}
};
handler.sendEmptyMessage(1);
Looper.loop(); // 不斷遍歷MessageQueue中是否有消息
};
}).start();
---------------------
方式二:獲取主線程的looper,或者說是UI線程的looper:
new Thread(new Runnable() {
public void run() {
Handler handler = new Handler(Looper.getMainLooper()){ // 區別在這!!!
@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), "handleMessage", Toast.LENGTH_LONG).show();
}
};
handler.sendEmptyMessage(1);
};
}).start();
---------------------
5. Android中進程與線程及如何在子線程中操作U
一個Android 程序默認情況只有一個進程,但是一個進程可以有多個線程。其中有一個UI 線程也稱為UI主線程,UI Thread在Android程序運行的時候就被創建,主要是負責控制UI界面的顯示、更新和控制項交互。所有的Android應用程序組件----包括Activity、Service、Broadcast Receiver都在應用程序的主線程中運行。因此,任何組件中的費時操作處理都可能阻塞所有其他的組件、包括Service和可見的Activity。
在Android 中,對未響應的定義是:Activity對一個輸入事件在5s內沒有響應,或者Broadcast Receiver在10s內沒有完成他的onReceive處理程序。對於任何不用直接和用戶界面進行交互的重要處理,使用後台線程技術處理是非常重要的,將文件操作、網路交互、資料庫、復雜計算調度到後台線程中完成非常重要,以免阻塞主線程。
方法一:Thread+Handler
開啟一個新線程:new Thread(new MyThread()).start();
//通過Handler物件,通知主線程UI Thread來更新界面
Handler myHandler=new Handler(){
//此方法主要用於更新UI
public void handleMessage(Message msg){
switch(msg.what){
//根據Thread返回的標識進行相應的處理
}
}
};
class MyThread implements Runnable{
public void run(){
int what=0;
//將耗時操作放到這里處理
//to-do
6. 安卓開發線程和進程講解
本教程為大家介紹安卓開發中的線程和進程,安卓平台中當首次啟動運行一個組件的時候,Android會相應的啟動了一個進程。默認的,所有的組件和程序運行在這個進程和線程中,也可以安排組件在其他的進程或者線程中運行。
進程:組件運行的進程由manifest file控制。組件的節點activity, service, receiver, 和 provider 都包含一個 process 屬性。這個屬性可以設置組件運行的進程:可以配置組件在一個獨立進程運行,或者多個組件在同一個進程運行。甚至可以多個程序在一個進程中運行——如果這些程序共享一個User ID並給定同樣的許可權。 節點也包含 process 屬性,用來設置程序中所有組件的默認進程。
所有的組件在此進程的主線程中實例化,系統對這些組件的調用從主線程中分離。並非每個對象都會從主線程中分離。一般來說,響應例如View.onKeyDown()用戶操作的方法和通知的方法也在主線程中運行。這就表示,組件被系統調用的時候不應該長時間運行或者阻塞操作(如網路操作或者計算大量數據),因為這樣會阻塞進程中的其他組件。可以把這類操作從主線程中分離。
當更加常用的進程無法獲取足夠內存,Android可能會關閉不常用的進程。下次啟動程序的時候會重新啟動進程。
當決定哪個進程需要被關閉的時候, Android會考慮哪個對用戶更加有用。如Android會傾向於關閉一個長期不顯示在界面的旦頌殲進程來支持一個經常顯示在界面的進程。
線程:即使為組件分配了不同的進程,有時候也需要再分配線程。比如用戶界面需要很快對用戶進行響應,因此某些費時的操作,如網路連接、下載或者非常佔用伺服器時間的操作應該放到其他線程。
線程通過java的標准對象Thread 創建. Android 提供了很多方便的管理線程的方法:— Looper 在線程中運行一個消息循環; Handler 傳遞一個消息; HandlerThread 創建一個帶有消息循環的線程。
遠程調用Remote procere calls
Android有一個遠程調用(RPCs) 的輕量級機制— 通過這個機制,方法可以在本地調用,在遠程執行(在其他進程執行),還可以返回一個值。要實現這個需求,必須分解方法調用,並且所有要傳遞的數據必須是操作系統可以訪問的級別。從本地的進程和內存地址傳送到遠程的進程和內存地櫻正址並在遠程處理和返回。返回值必須向相反的方向傳遞。Android提供了做以上操作的代碼,所以開發者可以專注於實現RPC的介面。
一個RPC介面只能包含方法。所有的方法都是同步執行的(直到遠程方法返回,本地方法才結束阻塞),沒有返回值的時候也是如此。
簡單來說,這個機制是這樣的:使用IDL (interface definition language)定義你想要實現的介面, aidl 工具可以生成用於java的介面定義,本地和遠程都要使用這個定義。它包含2個類,
inner類包含了所有的管理遠程程序(符合IDL描述的介面)所需要的代碼。所有的inner類實現了IBinder 介面.其中一個在本地使用,可以不管它的代碼;另外一個叫做Stub繼承了 Binder 類。為了實現遠程調用,這個類包含RPC介面。開發者可以繼承Stub類來實現需要的方法。
一般來說,遠程進程會被一個service管理(因為service可以通知操作系統這個進程的信息並和其他進程通信),它也會包含aidl 工具產生的介面文件,Stub類實現了遠處那個方法。服務的客戶端只需要aidl 工具產生的介面文件。
以下是如何連接服務和客戶端調用:
·服務的客戶端(本地)會實現onServiceConnected() 和onServiceDisconnected() 方法,這樣,當客戶端連接或者斷開連接的時候可以獲取到通知。通過 bindService() 獲取到服務的連接。
· 服務的 onBind() 方法中可以接收或者拒絕連接,取決它收到的intent (intent通過 bindService()方法連接到服務). 如果服務接收了連接,會返回一個Stub類的實例.
· 如果服務接受了連接,Android會調用客戶端的onServiceConnected() 方法,模沖並傳遞一個Ibinder對象(系統管理的Stub類的代理),通過這個代理,客戶端可以連接遠程的服務。
以上的描述省略很多RPC的機制。請參見Designing a Remote Interface Using AIDL 和 IBinder 類。
線程安全的方法
在某些情況下,方法可能調用不止一個的線程,因此需要注意方法的線程安全。
對於可以遠程調用的方法,也要注意這點。當一個調用在Ibinder對象中的方法的程序啟動了和Ibinder對象相同的進程,方法就在Ibinder的進程中執行。但是,如果調用者發起另外一個進程,方法在另外一個線程中運行,這個線程在和IBinder對象在一個線程池中;它不會在進程的主線程中運行。例如,一個service從主線程被調用onBind() 方法,onBind() 返回的對象(如實現了RPC的Stub子類)中的方法會被從線程池中調用。因為一個服務可能有多個客戶端請求,不止一個線程池會在同一時間調用IBinder的方法。因此IBinder必須線程安全。
簡單來說,這個機制是這樣的:使用IDL (interface definition language)定義你想要實現的介面, aidl 工具可以生成用於java的介面定義,本地和遠程都要使用這個定義。它包含2個類,
inner類包含了所有的管理遠程程序(符合IDL描述的介面)所需要的代碼。所有的inner類實現了IBinder 介面.其中一個在本地使用,可以不管它的代碼;另外一個叫做Stub繼承了 Binder 類。為了實現遠程調用,這個類包含RPC介面。開發者可以繼承Stub類來實現需要的方法。
一般來說,遠程進程會被一個service管理(因為service可以通知操作系統這個進程的信息並和其他進程通信),它也會包含aidl 工具產生的介面文件,Stub類實現了遠處那個方法。服務的客戶端只需要aidl 工具產生的介面文件。
以下是如何連接服務和客戶端調用:
·服務的客戶端(本地)會實現onServiceConnected() 和onServiceDisconnected() 方法,這樣,當客戶端連接或者斷開連接的時候可以獲取到通知。通過 bindService() 獲取到服務的連接。
· 服務的 onBind() 方法中可以接收或者拒絕連接,取決它收到的intent (intent通過 bindService()方法連接到服務). 如果服務接收了連接,會返回一個Stub類的實例.
· 如果服務接受了連接,Android會調用客戶端的onServiceConnected() 方法,並傳遞一個Ibinder對象(系統管理的Stub類的代理),通過這個代理,客戶端可以連接遠程的服務。
線程安全的方法
在某些情況下,方法可能調用不止一個的線程,因此需要注意方法的線程安全。
對於可以遠程調用的方法,也要注意這點。當一個調用在Ibinder對象中的方法的程序啟動了和Ibinder對象相同的進程,方法就在Ibinder的進程中執行。但是,如果調用者發起另外一個進程,方法在另外一個線程中運行,這個線程在和IBinder對象在一個線程池中;它不會在進程的主線程中運行。例如,一個service從主線程被調用onBind() 方法,onBind() 返回的對象(如實現了RPC的Stub子類)中的方法會被從線程池中調用。因為一個服務可能有多個客戶端請求,不止一個線程池會在同一時間調用IBinder的方法。因此IBinder必須線程安全。
簡單來說,一個content provider 可以接收其他進程的數據請求。即使ContentResolver和ContentProvider類沒有隱藏了管理交互的細節,ContentProvider中響應這些請求的方法(query(), insert(), delete(), update(), and getType() )— 是在content provider的線程池中被調用的,而不是ContentProvider的本身進程。因為這些方法可能是同時從很多線程池運行的,所以這些方法必須要線程安全。
7. android中什麼時候會選擇用廣播來進行線程間的通
Android 的廣播機制
在 Android
裡面有各種各樣的廣播,比如電池的使用狀態,電話的接收和簡訊的接收都會產生一個廣播,應用程序開發者也可以監聽這些廣播並做出程序邏輯的處理。下面我畫一張粗略的圖來幫助大家理解廣播的運行機制。
Android 中有各式各樣的廣播,各種廣播在Android 系統中運行,當系統/應用程序運行時便會向 Android 注冊各種廣播,Android
接收到廣播會便會判斷哪種廣播需要哪種事件,然後向不同需要事件的應用程序注冊事件,不同的廣播可能處理不同的事件也可能處理相同的廣播事件,這時就需要 Android
系統為我們做篩選。
案例分析:
一個經典的電話黑名單,首先通過將黑名單號碼保存在資料庫裡面,當來電時,我們接收到來電廣播並將黑名單號碼與資料庫中的某個數據做匹配,如果匹配的話則做出相應的處理,比如掛掉電話、比如靜音等等。。。
Demo 分析:
下面通過一個小DEMO 來講解一下廣播在Android
中如何編寫,在Demo中我們設置了一個按鈕為按鈕設置點擊監聽通過點擊發送廣播,在後台中接收到廣播並列印LOG信息。代碼如下:
BroadCastActivity 頁面代碼
public class BroadCastActivity extends Activity {
public static final String ACTION_INTENT_TEST =
"com.terry.broadcast.test";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn = (Button) findViewById(R.id.Button01);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(ACTION_INTENT_TEST);
sendBroadcast(intent);
}
});
}
}
接收器代碼如下:
public class myBroadCast extends BroadcastReceiver {
public myBroadCast() {
Log.v("BROADCAST_TAG", "myBroadCast");
}
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.v("BROADCAST_TAG", "onReceive");
}
}
Android 廣播的生命周期
在上面的接收器中,繼承了BroadcastReceiver 並重寫了它的onReceive 並構造了一個函數,下面通過圖片來一步一步認識
Android 廣播的生命周期。當我點擊一下按鈕,它向Android 發送了一個廣播,如下圖:
這時我們再點擊一下按鈕,它還是會再向 Android 系統發送廣播,此時日誌信息如下:
下面本人畫一張圖像,描述了Android 中廣播的生命周期,其次它並不像Activity 一樣復雜,運行原理很簡單如下圖:
下面來看一下SDK給出的解釋:
大意為:如果一個廣播處理完onReceive 那麼系統將認定此對象將不再是一個活動的對象,也就會finished掉它。
至此,大家應該能明白 Android 的廣播生命周期的原理,代碼也不用多介紹,很簡單的一個發送廣播並處理廣播的Demo。
Android 如何判斷並篩選廣播?
前 面說過 Android 的廣播有各式各樣,那麼Android
系統是如何幫我們處理我們需要哪種廣播並為我們提供相應的廣播服務呢?這里有一點需要大家注意,每實現一個廣播接收類必須在我們應用程序中的 manifest
中顯式的註明哪一個類需要廣播,並為其設置過濾器,如下圖:
Tip:action 代表一個要執行的動作,在Andriod 中有很action 比如 ACTION_VIEW,ACTION_EDIT
那麼有些人會問了,如果我在一個廣播接收器中要處理多個動作呢?那要如何去處理?
在Android 的接收器中onReceive 以經為我們想到的,同樣的你必須在Intent-filter
裡面注冊該動作,可以是系統的廣播動作也可以是自己需要的廣播,之後你之需要在onReceive
方法中,通過intent.getAction()判斷傳進來的動作即可做出不同的處理,不同的動作。具體大家可以去嘗試測試一下。
小結:
在Android 中如果要發送一個廣播必須使用sendBroadCast 向系統發送對其感興趣的廣播接收器中。
使用廣播必須要有一個intent 對象必設置其action動作對象
使用廣播必須在配置文件中顯式的指明該廣播對象
每次接收廣播都會重新生成一個接收廣播的對象
在BroadCast 中盡量不要處理太多邏輯問題,建議復雜的邏輯交給Activity 或者 Service 去處理
Android廣播機制(兩種注冊方法)
在android下,要想接受廣播信息,那麼這個廣播接收器就得我們自己來實現了,我們可以繼承BroadcastReceiver,就可以有一個廣播接受器了。有個接受器還不夠,我們還得重寫BroadcastReceiver裡面的onReceiver方法,當來廣播的時候我們要干什麼,這就要我們自己來實現,不過我們可以搞一個信息防火牆。具體的代碼:
public class SmsBroadCastReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
Bundle bundle = intent.getExtras();
Object[] object = (Object[])bundle.get("ps");
SmsMessage sms[]=new SmsMessage[object.length];
for(int i=0;i
{
sms[0] = SmsMessage.createFromP((byte[])object[i]);
Toast.makeText(context, "來自"+sms[i].getDisplayOriginatingAddress()+"
的消息是:"+sms[i].getDisplayMessageBody(), Toast.LENGTH_SHORT).show();
}
//終止廣播,在這里我們可以稍微處理,根據用戶輸入的號碼可以實現簡訊防火牆。
abortBroadcast();
}
}
當實現了廣播接收器,還要設置廣播接收器接收廣播信息的類型,這里是信息:android.provider.Telephony.SMS_RECEIVED
我們就可以把廣播接收器注冊到系統裡面,可以讓系統知道我們有個廣播接收器。這里有兩種,一種是代碼動態注冊:
//生成廣播處理
smsBroadCastReceiver = new SmsBroadCastReceiver();
//實例化過濾器並設置要過濾的廣播
IntentFilter intentFilter = new
IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//注冊廣播
BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver,
intentFilter);
一種是在AndroidManifest.xml中配置廣播
package="spl.broadCastReceiver"
android:versionCode="1"
android:versionName="1.0">
android:label="@string/app_name">
兩種注冊類型的區別是:
1)第一種不是常駐型廣播,也就是說廣播跟隨程序的生命周期。
2)第二種是常駐型,也就是說當應用程序關閉後,如果有信息廣播來,程序也會被系統調用自動運行。
BroadcastReceiver用於監聽被廣播的事件
必須被注冊,有兩種方法:
1、在應用程序的代碼中注冊
注冊BroadcastReceiver:
registerReceiver(receiver,filter);
取消注冊BroadcastReceiver:
unregisterReceiver(receiver);
當BroadcastReceiver更新UI,通常會使用這樣的方法注冊。啟動Activity時候注冊BroadcastReceiver,Activity不可見時候,取消注冊。
2、在androidmanifest.xml當中注冊
使用這樣的方法注冊弊端:它會始終處於活動狀態,畢竟是手機開發,cpu和電源資源比較少,一直處於活動耗費大,不利。
8. Android中的線程和線程池
一、除了Thread外,扮演線程角色的還有:AsyncTask和IntentService,同時HandlerThread也扮演特殊的線程。
IntentService:內部採用HandlerThread來執行,像一個後台線程,同時是一個服務,不容易被系統殺死。
二、HandlerThread的run方法是一個無限循環
三、IntentService中任務是排隊執行的
四、AsyncTask
1、Android1.6之前串悄段桐行執行任務,1.6時候採用線程池裡的並行,Android3.0開始又開始串列(為了避免並發錯誤),單任可以並行。
2、AsyncTask必須在UI線程調用(不過這個不是絕對的,和版本有關燃腔系,API 16之前,API 16到 22, API 22以後) 參考一
原因:內部有靜態Handler,採用UI線程的Looper來處理消息,這就是為什麼AsyncTask必須在UI線程調用,因為子線程默認沒有Looper無法創建下面的Handler,程序會直接Crash
3、AsyncTask中有兩個線程池和一個Handler,一個線程池用啟坦於任務排隊,一個線程池用於真正的執行任務,InternalHandler用於將
執行環境從線程池切換到主線程
AsyncTask串列與並行
五、線程池
線程池中多餘的線程是如何回收的