android阻塞
Ⅰ Android如何阻塞一個線程讓其等待一個時間發生之後再繼續執行
你所謂的線程阻塞是指的ui線程嗎?這應該是從你在開發的經驗以及測試當中去體驗的,如果你說是用代碼去判斷線程阻塞的話,估計比較復雜,也沒那個必要,android的機制在出現ui線程阻塞的話會出現anr給予用戶提示,出現這樣的情況是開發者在開發過程中就得去避免的!
Ⅱ Android廣播阻塞、延遲問題
最近項目中,多次碰到app研發人員反饋廣播從發送到接收器接收,間隔時間太長,要求系統進行優化,特別是開機階段。對此,專門閱讀了一下廣播從發送到接收這個流程的源碼,以徹底搞明白怎樣讓自己發送的廣播盡快到達接收器。
涉及到的源碼類不多,主要就是ActivityManagerService.java 和 BroadcastQueue.java。發送廣播進程調用發送介面,通過IPC到達AMS,AMS根據Intent是否配置Intent.FLAG_RECEIVER_FOREGROUND,選擇當前廣播加入前台廣播隊列還是後台廣播隊列。根據當前廣播是否有序,將廣播加入廣播隊列的串列列表還是並行列表。廣播隊列和廣播隊列中的廣播列表是影響廣播接收時間的主要因素。
BroadcastQueue廣播隊列,負責將廣播發送給廣播接收器。AMS中有兩個成員變數,
BroadcastQueue mFgBroadcastQueue;//前台廣播隊列
BroadcastQueue mBgBroadcastQueue;//後台廣播隊列
前台廣播隊列和後台廣播隊列的區別有兩處:1 超時時間,前台10s,後台60s. 2 是否延遲廣播等待前一個廣播進程完成。這兩個區別已經說明前台廣播對廣播接收器要求更高,響應時間更短,如果廣播要排隊,時間上前台廣播更短。同時系統默認使用後台廣播隊列,所以前台廣播隊列處理的廣播要少,避免了可能的大量廣播排隊情況。
廣播隊列中的列表
//存放無序並發送給動態廣播接收器的廣播任務
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<BroadcastRecord>();
//存放無序發送給靜態廣播接收器的廣播任務或者存放有序廣播任務
final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<BroadcastRecord>();
mParallelBroadcasts 此列表中存放的是無序廣播動態廣播接收器任務,廣播隊列會在處理任務時通過嵌套循環,把每個廣播通過ipc發送到關注它的所有進程。所有無序廣播+動態廣播接收器,廣播不需要排隊。這種情況是最快能讓廣播到達目標進程的方式。
mOrderedBroadcasts存放的廣播任務特點:廣播有序,或者廣播接收器是靜態注冊的。此種類型的廣播全部要在mOrderedBroadcasts中排隊,廣播之間按時間先後,同一個廣播不同廣播接收器按優先順序。mOrderedBroadcasts存放的廣播必須等一個廣播任務處理完畢才能處理下一個,中間可能包含進程的啟動等。
由此可見,廣播最快的情況是前台廣播、無序廣播、動態注冊廣播接收器。最糟糕的情況是:後台廣播、有序或靜態注冊廣播接收器、廣播接收器優先順序低。如果一個應用只是簡單的靠注冊一個靜態廣播接收器拉起進程,對應的正是最糟糕的情況。如果又發生在開機階段,自然延遲嚴重。
如果必須注冊靜態廣播接收器,縮短時間的辦法為:配置Intent.FLAG_RECEIVER_FOREGROUND,加入前台廣播隊列,設置廣播優先順序
源碼:
廣播發送:Context .sendBroadcast ->ActivityManagerNative.broadcastIntent->ActivityManagerService.broadcastIntent->ActivityManagerService.broadcastIntentLocked.到此階段,跟發送廣播的進程通信結束。此階段AMS完成的工作主要是根據Intent查找該廣播對應的動態廣播接收器、靜態廣播接收器、以此發送該廣播使用的廣播隊列。
private final int broadcastIntentLocked(
......//許可權檢查
......//特殊系統廣播進行必要處理
if (sticky) {//粘性廣播處理
......
//查找靜態注冊的接收器
receivers = collectReceiverComponents(intent, resolvedType, users);
if (intent.getComponent() == null) {
// 查找動態廣播接收器
registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
}
//動態廣播接收器
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
//確定隊列
final BroadcastQueue queue = broadcastQueueForIntent(intent);
//創建廣播任務BroadcastRecord
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
appOp, registeredReceivers, resultTo, resultCode, resultData, map,
ordered, sticky, false, userId);
......
//廣播任務加入並行列表中
queue.(r);
//啟動非同步發送廣播任務
queue.scheleBroadcastsLocked();
registeredReceivers = null;
NR = 0;
......
while (it < NT && ir < NR) {
......
//根據優先順序排序
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
//獲取廣播隊列
BroadcastQueue queue = broadcastQueueForIntent(intent);
//創建廣播任務
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermission, appOp, receivers, resultTo, resultCode,
resultData, map, ordered, sticky, false, userId);
//加入到廣播隊列串列列表中
queue.enqueueOrderedBroadcastLocked(r);
//啟動非同步發送任務
queue.scheleBroadcastsLocked();
廣播隊列處理廣播:
final void processNextBroadcast(boolean fromMsg) {
......
//並行列表,遍歷廣播任務
while (mParallelBroadcasts.size() > 0) {
final int N = r.receivers.size();
//遍歷接收器
for (int i=0; i<N; i++) {
//IPC調用發送給目標進程
(r, (BroadcastFilter)target, false);
}
}
//有串列廣播任務正在執行
if (mPendingBroadcast != null) {
//接收廣播的目標進程正常
if (!isDead) {
// It's still alive, so keep waiting 繼續等待目前進程反饋
return;
}
}
//取出第一個廣播
r = mOrderedBroadcasts.get(0);//判斷是否超時,
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
//廣播超時
broadcastTimeoutLocked(false);//超時處理,終止當前廣播,啟動下一個任務。
}
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
//所有廣播任務執行完畢
}
int recIdx = r.nextReceiver++;//下一個廣播接收器
r.dispatchTime = r.receiverTime;//設置派發時間
setBroadcastTimeoutLocked(timeoutTime);//啟動超時計時
if (nextReceiver instanceof BroadcastFilter){//動態廣播接收器
(r, filter, r.ordered);//發送
return;
}
.//靜態廣播
ResolveInfo info =
(ResolveInfo)nextReceiver;
......
//檢查進程是否已啟動
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid, false);
if (app != null && app.thread != null) { /進程啟動
processCurBroadcastLocked(r, app);//發送靜態廣播
return;
}
if ((r.curApp=mService.startProcessLocked(targetProcess,//啟動進程
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast", r.curComponent,
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {
//進程啟動失敗
}
//標志正在發送的串列廣播
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;//正在發送的廣播任務對應的接收器索引
}
Ⅲ 如何判斷android 線程阻塞
你所謂的線程阻塞是指的UI線程嗎?這應該是從你在開發的經驗以及測試當中去體驗的,如果你說是用代碼去判斷線程阻塞的話,估計比較復雜,也沒那個必要,android的機制在出現UI線程阻塞的話會出現ANR給予用戶提示,出現這樣的情況是開發者在開發過程中就得去避免的!