Activity 系列博客
- 《 Activity 的組成》
- 《Android Activity——啟動過程探索(一)》
- 《Android Activity——啟動過程探索(二)》
- 《Android Activity——啟動過程探索(三)》
- 《Activity 啟動模式及任務(wù)棧探究》
- 《 Activity常見問題》
Activity常見問題
通過這篇博客窥突,我們能知道以下問題:
-
Activity
各種情況下的生命周期 - 彈出
Dialog
對Activity
生命周期有什么影響镶奉? -
onActivityResult()
在哪兩個生命周期之間回調(diào)曲聂? -
Activity
在onResume()
之后才顯示的原因是什么? - 通過 Sheme 協(xié)議打開
Activity
-
Activity
什么時候會發(fā)生重建斜纪? - 在
Activity
的onCreate()
方法里寫死循環(huán)會 ANR 嗎拯坟?
1. Activity 生命周期方法相關(guān)
生命周期的回調(diào)方法
Activity生命周期的回調(diào)主要有 onCreate()
咪橙、onRestart()
层扶、onStart()
晴及、onResume()
都办、onPause()
、onStop()
虑稼、onDestory()
幾個方法琳钉。
調(diào)用時機說明:
-
onCreate()
:系統(tǒng)創(chuàng)建Activity
時觸發(fā),用來初始化 Activity 的基本組件蛛倦。 -
onRestart()
:當處于“已停止”狀態(tài)的Activity
即將重啟時歌懒,系統(tǒng)就會調(diào)用此回調(diào)。 -
onStart()
:Activity
將進入“已啟動”狀態(tài)溯壶,并對用戶可見及皂。 -
onResume()
:系統(tǒng)會在Activity
開始與用戶互動之前調(diào)用此回調(diào)甫男。此時,該Activity
位于Activity
堆棧的頂部验烧,并會捕獲所有用戶輸入 -
onPause()
:當Activity
失去焦點并進入“已暫桶宀担”狀態(tài)時,系統(tǒng)就會調(diào)用此回調(diào)碍拆。 -
onStop()
:當Activity
對用戶不再可見時若治,系統(tǒng)就會調(diào)用此回調(diào)。 -
onDestory()
:系統(tǒng)會在銷毀Activity
之前調(diào)用此回調(diào)感混。此回調(diào)是Activity
接收的最后一個回調(diào)端幼。通常,實現(xiàn)onDestroy()
是為了確保該Activity
的所有資源弧满。
onStart()
和 onStop()
是從 Activity
是否可見來回調(diào)的静暂,onResume()
和 onPause()
是從 Activity
是否位于前臺來回調(diào)的。
在生命周期里幾種狀態(tài):
Activity
的整個生命周期發(fā)生在onCreate()
與onDestroy()
之間,在onCreate()
中執(zhí)行“全局”狀態(tài)設(shè)置(例如狀態(tài)布局)谱秽,并在onDestroy()
中釋放資源洽蛀。Activity
的可見生命周期發(fā)生在onStart()
與onStop()
之間,在這段時間內(nèi)Activity
對用戶可見疟赊。在整個生命周期中郊供,當 Activity 在對用戶可見和隱藏倆種狀態(tài)中交替變化時,系統(tǒng)會多次調(diào)用onStart()
和onStop()
近哟。Activity
的前臺生命周期發(fā)生在onResume()
與onPause()
之間驮审,Activity
位于其他Activity
之前(棧頂位置),可與用戶交互并具有輸入焦點吉执。但狀態(tài)改變頻繁疯淫,系統(tǒng)會多次調(diào)用onResume()
和onPause()
,建議做些輕量級操作戳玫。
打開Activity時兩個Activity的生命周期方法的回調(diào)順序
當一個 Activity
啟動另一個 Activity
時熙掺,它們都會經(jīng)歷生命周期轉(zhuǎn)換。第一個 Activity
停止運行并進入“已暫凸舅蓿”或“已停止”狀態(tài)币绩,同時創(chuàng)建另一個 Activity
。
Activity
A 啟動Activity
B府阀,BActivity
的launchMode
為standard
或者 BActivity
沒有可復(fù)用的實例時: A.onPause()
-> B.onCreate()
-> B.onStart()
-> B.onResume()
-> A.onStop()
--> A.onDestory()
(如果需要關(guān)閉Activity A[A 被移出棧])Activity
A 啟動Activity
B缆镣,BActivity
的launchMode
為singleTop
且 BActivity
已經(jīng)在棧頂時(一些特殊情況如通知欄點擊、連點试浙、BActivity
自己打開自己)董瞻,此時只有 B 頁面自己有生命周期變化:B.onPause()
-> B.onNewIntent()
-> B.onResume()
當 B
Activity
的launchMode
為singleInstance
,singleTask
且對應(yīng)的 BActivity
有可復(fù)用的實例時:A.onPause()
-> B.onNewIntent()
-> B.onRestart()
-> B.onStart()
-> B.onResume()
-> A.onStop()
--> A.onDestory()
(如果需要關(guān)閉Activity A[A 被移出棧])當 B
Activity
的Theme
為Dialog
時:A.onPause()
-> B.onCreate()
-> B.onStart()
-> B.onResume()
(注意前一個Activity
不會回調(diào)onStop()
田巴,因為只有在Activity
切到后臺(不在Activity棧頂)不可見才會回調(diào)onStop()
钠糊;而彈出Dialog
主題的Activity
時前一個頁面還是可見的挟秤,只是失去了焦點而已所以僅有onPause()
回調(diào))
2. 彈出 Dialog
對 Activity
生命周期有什么影響?
先下結(jié)論:通過 《Android Activity——啟動過程探索(一)》 《Android Activity——啟動過程探索(二)》 《Android Activity——啟動過程探索(三)》 我們知道Activity
生命周期回調(diào)都是通過ActivityTaskManagerService
(ATMS)(在Android 10之前是通過 ActivityManagerService
(AMS))來回調(diào)的眠蚂。但是彈出 Dialog
、Toast
斗躏、PopupWindow
本質(zhì)上都直接是通過 WindowManager.addView()
顯示的(沒有經(jīng)過 ATMS/AMS)逝慧,所以不會對生命周期有任何影響。
Dialog顯示過程源碼解析
源碼版本為 Android 10(Api 29)啄糙,不同Android版本可能有一些差別
-
構(gòu)造方法中獲取
WindowManager
和創(chuàng)建PhoneWindow
并通過PhoneWindow
的setWindowManager()
方法關(guān)聯(lián)public Dialog(@NonNull Context context, @StyleRes int themeResId) { this(context, themeResId, true); } Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { // 獲取 WindManager笛臣,實際為實現(xiàn)類 WindowManagerImpl mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); // 創(chuàng)建 PhoneWindow 對象 final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setOnWindowSwipeDismissedCallback(() -> { if (mCancelable) { cancel(); } }); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this); }
-
Dialog#show()
方法源碼實現(xiàn)public void show() { mCanceled = false; if (!mCreated) { dispatchOnCreate(null); // 沒有創(chuàng)建過的話,調(diào)用 onCreate() 方法 } else { // 創(chuàng)建過的話隧饼,就不在調(diào)用 onCreate() 回調(diào)沈堡,只是修改配置 final Configuration config = mContext.getResources().getConfiguration(); mWindow.getDecorView().dispatchConfigurationChanged(config); } onStart(); // 調(diào)用 onStart() 回調(diào)方法 mDecor = mWindow.getDecorView(); // 通過 PhoneWindow 獲取 DecorView ... // 省略,設(shè)置默認logo燕雁、icon以及確定軟鍵盤模式等 // 通過 WindowManager(實際為WindowManagerImpl對象)將DecorView增加窗體上 mWindowManager.addView(mDecor, l); ... // 設(shè)置軟鍵盤模式 mShowing = true; // 修改顯示標記為 true sendShowMessage(); } // WindowManagerImpl 的 addView() 方法 @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); // 調(diào)用 WindowManagerGlobal 的 addView() 方法 mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); } // WindowManagerGlobal 的 addView() 方法核心代碼 public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ViewRootImpl root; View panelParentView = null; synchronized (mLock) { // 創(chuàng)建 ViewRootImpl 對象 root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); // 調(diào)用 ViewRootImpl 的 setView() 方法 root.setView(view, wparams, panelParentView); } } // ViewRootImpl 的 setView() 方法核心代碼 public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; // 調(diào)用 requestLayout() 方法诞丽,進行布局(包括measue、layout拐格、draw) requestLayout(); mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); // 通過調(diào)用 Session 的 addToDisplay() 方法 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel, mTempInsets); setFrame(mTmpFrame); } } }
ViewRootImpl
的 setView()
方法說明:
mWindowSession
是通過WindowManagerGlobal
的getWindowSession()
方法獲取到的僧免,返回的是通過WindowManagerService
的openSession()
方法創(chuàng)建的Session
對象mWindow 對象為
ViewRootImpl
的 內(nèi)部類static class W extends IWindow.Stub
,通過定義可以看出來捏浊,是一個Binder
對象-
Session
對象的addToDisplay()
方法Override public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, InsetsState outInsetsState) { // mService 為 WindowManagerService懂衩,window 就是上面的 `ViewRootImpl` 的 內(nèi)部類 `static class W extends IWindow.Stub` return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel, outInsetsState); }
通過這個方法就能夠?qū)崿F(xiàn)客戶端與 WindowManagerService
的雙向調(diào)用了(應(yīng)用端通過 mWindowSession 調(diào)用 WMS, WMS 通過 mWindow (一個 Binder 對象) 調(diào)用應(yīng)用端)
DialogFragment源碼解析
注意:該部分源碼來自 support v4 包
看 DialogFragment
的定義
class DialogFragment extends Fragment
發(fā)現(xiàn)就是直接繼承了 Fragment
金踪,那么就可以肯定浊洞,Fragment
有的 DialogFragment
都有,像生命周期方法等胡岔,又因為我們可以通過 getDialog()
方法獲取到 Dialog
法希。所以可以肯定它包含一個了 Dialog
。接著我們先看 show()
方法
public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
}
發(fā)現(xiàn)就是將 DialogFragment
顯示出來靶瘸,那我們接著找一下铁材,看看 Dialog
是在哪里創(chuàng)建的。發(fā)現(xiàn)如下代碼:
@Override
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
mDialog = onCreateDialog(savedInstanceState);
return (LayoutInflater) mActivity.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new Dialog(getActivity(), getTheme());
}
發(fā)現(xiàn)是在 getLayoutInflater()
方法中創(chuàng)建的奕锌,那么這個 getLayoutInflater()
方法又是在哪里被調(diào)用的呢著觉?通過查找我們發(fā)現(xiàn)這個方法是在 FragmentManager
中調(diào)用的,調(diào)用完這個方法之后就會調(diào)用 Fragment
的 onViewCreated()
方法惊暴;而 方法的返回值 LayoutInflater
就是 Fragment
中回調(diào)方法 onCreateView()
的參數(shù)饼丘。
分析到這里,我們就知道了辽话,DialogFragment
實際上是一個 Fragment
肄鸽,只是他在 onViewCreated()
方法之前通過 getLayoutInflater()
方法創(chuàng)建了一個 Dialog
卫病,也就是一個普通的 Fragment
中包含了一個 Dialog
。那么又出現(xiàn)了問題典徘,我們剛剛看了 show()
方法蟀苛,發(fā)現(xiàn)并沒有調(diào)用 Dialog。show()
方法逮诲,那么 Dialog
是怎么顯示的呢帜平?我們前面就說過,DialogFragment
實際上是一個 Fragment
梅鹦,Fragment
有的 DialogFragment
都有裆甩;我們都知道Fragment
有一個生命周期方法 onStart()
是在用戶可見時被調(diào)用的,那么 Dialog.show()
方法是否在該方法種調(diào)用了齐唆,看一下代碼:
@Override
public void onStart() {
super.onStart();
if (mDialog != null) {
mViewDestroyed = false;
mDialog.show();
}
}
果然嗤栓,在 Fragment
的生命周期方法 onStart()
回調(diào)中,調(diào)用了 Dialog.show()
方法箍邮,那么當我么調(diào)用 DialogFragment
的 show()
方法時茉帅,把將 DialogFragment
顯示出來了,那么 Fragment
的生命周期方法 onStart()
自然會被回調(diào)锭弊,Dialog
自然也就顯示出來了担敌。Fragment
的生命周期方法回調(diào)并非由 ActivityManagerService
(AMS)【Android 10 以上 ActivityTaskManagerService
(ATMS)】來控制,而是通過 FragmentManager
回掉的廷蓉,也不會影響到 Activity
的生命周期全封。
3. onActivityResult 在哪兩個生命周期之間回調(diào)?
onActivityResult 不屬于 Activity 的生命周期桃犬。但是答案很簡單刹悴,因為在 onActivityResult()
方法的注釋中就寫著答案:「You will receive this call immediately before onResume() when your activity is re-starting.」 所以 onActivityResult()
回調(diào)先于該 Activity
的所有生命周期回調(diào),從 B Activity
返回 A Activity
的生命周期調(diào)用為:
B.onPause()
-> A.onActivityResult()
-> A.onRestart()
-> A.onStart()
-> A.onResume()
4. Activity 在 onResume 之后才顯示的原因是什么攒暇?
通過《Activity 的組成》我們知道了在 Activity
中調(diào)用 setContentView()
方法會將我們的 View
添加到 DecorView
中土匀,DecorView
也是一個控件,他要加載到界面上讓用戶可見形用,是通過 Window
(也就是 PhoneWindow
)來完成的就轧,所以我們只要知道 PhoneWindow
是在什么時候?qū)?DecorView
加載出來的,就知道了用戶是在什么時候才可見布局信息田度。那么妒御,到底是什么時候開始將 DecorView
添加到 PhoneWindow
中的了, 在 《Android Activity——啟動過程探索(二)》 中镇饺,我們說到了 Activity
的回調(diào) onResume()
方法乎莉,中間有一段是會調(diào)用 ActivityThread#handleResumeActivity()
方法,我們來看一下這個方法的源碼(省略大部分代碼之后的核心代碼):
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
// 回調(diào) onResume() 方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow(); // 獲取PhoneWindow并賦值
View decor = r.window.getDecorView(); // 獲取 DecorView 控件
decor.setVisibility(View.INVISIBLE);
// 獲取ViewManager,也就是WindowManager(因為interface WindowManager extends ViewManager)
ViewManager wm = a.getWindowManager();
a.mWindowAdded = true;
wm.addView(decor, l); // 關(guān)鍵點
}
}
方法說明:
-
ViewManager wm = a.getWindowManager()
方法最終返回的是WindowManagerImpl
對象惋啃,原因如下:-
Activity.getWindowManager()
方法返回Activity
的成員變量mWindowManager
-
Activity
的成員變量mWindowManager
在Activity
的attach()
方法中被賦值mWindowManager = mWindow.getWindowManager()
哼鬓,調(diào)用的是Window
的getWindowManager()
方法 -
Window
的getWindowManager()
方法返回的是他的成員變量mWindowManager
-
Window
的成員變量mWindowManager
在他的setWindowManager()
方法中被賦值mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this)
,所以得出結(jié)論a.getWindowManager()
方法最終返回的是WindowManagerImpl
對象
-
關(guān)鍵點
wm.addView(decor, l)
調(diào)用的就是WindowManagerImpl#addView()
方法了边灭,之后的調(diào)用過程在當前文章前面【Dialog#show()
方法源碼實現(xiàn)】 部分已經(jīng)說過了异希,就不在重復(fù)了。
通過以上分析绒瘦,我們就知道了實際上 Activity
中的布局內(nèi)容是在 onResume()
回調(diào)之后才掛載到 Window
上的称簿,也就是這個時候才是對用戶可見的。
5. 通過 URL Sheme 協(xié)議打開 Activity
URL Scheme 是一種頁面內(nèi)跳轉(zhuǎn)協(xié)議椭坚,通過這個協(xié)議可以比較方便的跳轉(zhuǎn)到app某一個頁面予跌,也可以通過 h5 打開App頁面搏色。
URL Sheme 的格式:[scheme]://[host][:port]/[path]?[query]
- scheme:協(xié)議名稱 [scheme]:// 為必填善茎,其他為非必填
- host:主機名(域名)
- port:端口號
- path:路徑
- query:參數(shù)(多個參數(shù)之間用 & 分割,類似 get 請求)
使用
1. APP端需要能通過 sheme
打開的 Activity
進行配置 intent
過濾器(在清單文件中配置)
<activity android:name=".TestActivity">
<!--Android 接收外部跳轉(zhuǎn)過濾器-->
<!--要想在別的App上能成功調(diào)起App频轿,必須添加intent過濾器-->
<intent-filter>
<!-- 協(xié)議部分配置 ,注意需要跟web配置相同-->
<data
android:host="action.test.activity"
android:pathPrefix="/test"
android:port="999"
android:scheme="testapp" />
<!--下面這幾行也必須得設(shè)置-->
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
data屬性說明:
scheme: 協(xié)議名稱垂涯,必填,其他為非必填(由開發(fā)人員自定義)
host: 域名
port:端口
path: 完整的路徑航邢,如:http://example.com/blog/abc.html耕赘,這里將 path 設(shè)置為 /blog/abc.html 才能夠進行匹配
pathPrefix:路徑的開頭部分,拿上來的 Uri 來說膳殷,這里將 pathPrefix 設(shè)置為 /blog 就能進行匹配了
-
pathPattern:用表達式來匹配整個路徑操骡,匹配符如下:
- “” 用來匹配0次或更多,如:“a” 可以匹配“a”赚窃、“aa”册招、“aaa”...
- “.” 用來匹配任意字符,如:“.” 可以匹配“a”勒极、“b”是掰,“c”...
- 因此 “.*” 就是用來匹配任意字符0次或更多
2. 在 Activity
獲取 sheme
信息:
Uri uri = getIntent().getData();
if (uri != null) {
// 完整的url信息
Log.i(TAG, "url:" + uri);
// scheme部分
String scheme = uri.getScheme();
Log.i(TAG, "scheme:" + scheme);
// host部分
String host = uri.getHost();
Log.i(TAG, "host:" + host);
// port部分
int port = uri.getPort();
Log.i(TAG, "port:" + port);
// 訪問路勁
String path = uri.getPath();
Log.i(TAG, "path:" + path);
List<String> pathSegments = uri.getPathSegments();
Log.i(TAG, "pathSegments:" + pathSegments);
// Query部分
String query = uri.getQuery();
Log.i(TAG, "query:" + query);
//獲取指定參數(shù)值
String params1 = uri.getQueryParameter("params1");
String params2 = uri.getQueryParameter("params2");
Log.i(TAG, "params1:" + params1 + " params2:" + params2);
}
3. 打開 Activity
的方式
1. 通過原生代碼打開
String uri = "testapp://action.test.activity:999/test?params1=1¶ms2=true";
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri));
startActivity(intent);
1. 通過h5代碼打開(使用瀏覽器打開包含如下代碼的 html 文件)
<html>
<title>測試</title>
<body>
<a href="testapp://action.test.activity:999/test?params1=1¶ms2=true">打開APP</a>
</body>
</html>
6. Activity 什么時候會發(fā)生重建?
發(fā)生時機:
- 系統(tǒng)資源回收:內(nèi)存不足辱匿,低優(yōu)先級的Activity會被殺死
- 配置發(fā)生變化:當系統(tǒng)配置發(fā)生變化時键痛,比如屏幕方向、語言的改變
橫豎屏切換不銷毀重建的方式
在清單文件中為該 Activity
配置 android:configChanges
屬性匾七。屬性值 orientation|screenSize
(Api級別為13以下時只需要配置 orientation
即可) 對應(yīng)著旋轉(zhuǎn)屏幕絮短,locale
對應(yīng)著語言變化。如此昨忆,在配置發(fā)生變化時戚丸,不會導(dǎo)致重建,而是走 onConfigurationChanged()
回調(diào)方法。
保存和恢復(fù)住狀態(tài)
保存和恢復(fù)狀態(tài)分別通過 onSaveInstanceState()
方法和 onRestoreInstanceState()
方法實現(xiàn)限府。
-
Activity
的onSaveInstanceState()
執(zhí)行在onStop()
之前夺颤,與onPause()
沒有固定的時序關(guān)系;onRestoreInstanceState()
執(zhí)行在onStart()
之后胁勺。不止Activity
有這兩個方法世澜,每個View
也有這兩個方法,用來保存View
的信息署穗。 - 正常情況下的活動銷毀并不會調(diào)用這兩個方法寥裂,只有當活動異常銷毀并且有機會重現(xiàn)展示的時候才會進行調(diào)用。
- 在
Activity
中onRestoreInstanceState()
和onCreate()
都可以進行數(shù)據(jù)恢復(fù)工作案疲,但建議采用在onRestoreInstanceState()
中去恢復(fù)(因為調(diào)用了這個方法封恰,用來存取數(shù)據(jù)的Bundle
肯定不為null
)。 - 在
onSaveInstanceState()
和onRestoreInstanceState()
這兩個方法中褐啡,系統(tǒng)會默認為我們進行一定的恢復(fù)工作诺舔,比如:會為布局中的每個View
調(diào)用相應(yīng)的onSaveInstanceState()
方法,讓每個視圖都能提供有關(guān)自身的應(yīng)保存信息备畦。
具體調(diào)用時機說明
Activity#onSaveInstanceState()
方法調(diào)用時機:
- 當用戶按下HOME鍵時
- 從最近應(yīng)用中選擇運行其他的程序時
- 按下電源按鍵(關(guān)閉屏幕顯示)時
- 從當前activity啟動一個新的activity時
- 屏幕方向切換時(無論豎屏切橫屏還是橫屏切豎屏都會調(diào)用)
- 被系統(tǒng)回收時
Activity#onRestoreInstanceState()
方法調(diào)用時機:
- 被系統(tǒng)回收且重建時
- 屏幕方向切換時(無論豎屏切橫屏還是橫屏切豎屏都會調(diào)用)
- 語言切換時
7. 在 Activity 的 onCreate()
方法里寫死循環(huán)會 ANR 嗎低飒?
ANR全稱:Application Not Responding,也就是應(yīng)用程序無響應(yīng)
Android 中產(chǎn)生 ANR 的原因:
- Service TimeOut: service 未在規(guī)定時間執(zhí)行完成:前臺服務(wù) 20s懂盐,后臺 200s
- BroadCastQueue TimeOut: 未在規(guī)定時間內(nèi)未處理完廣播:前臺廣播 10s 內(nèi), 后臺 60s 內(nèi)
- ContentProvider TimeOut: publish 在 10s 內(nèi)沒有完成
- Input Dispatching timeout: 5s 內(nèi)未響應(yīng)鍵盤輸入褥赊、觸摸屏幕等事件
根據(jù)以上 ANR 產(chǎn)生的原因,說明在 Activity
的 onCreate()
方法中寫死循環(huán)是不會發(fā)生ANR的莉恼,但是主線程被死循環(huán)一直占用了拌喉,所以當再有其他事件產(chǎn)生時,就不能及時響應(yīng)了俐银,從而導(dǎo)致ANR發(fā)生尿背。