Activity常見問題

Activity 系列博客

Activity常見問題

通過這篇博客窥突,我們能知道以下問題:

  1. Activity 各種情況下的生命周期
  2. 彈出 DialogActivity 生命周期有什么影響镶奉?
  3. onActivityResult() 在哪兩個生命周期之間回調(diào)曲聂?
  4. ActivityonResume() 之后才顯示的原因是什么?
  5. 通過 Sheme 協(xié)議打開 Activity
  6. Activity 什么時候會發(fā)生重建斜纪?
  7. ActivityonCreate() 方法里寫死循環(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府阀,B ActivitylaunchModestandard 或者 B Activity 沒有可復(fù)用的實例時: A.onPause() -> B.onCreate() -> B.onStart() -> B.onResume() -> A.onStop() --> A.onDestory()(如果需要關(guān)閉Activity A[A 被移出棧])

  • Activity A 啟動 Activity B缆镣,B ActivitylaunchModesingleTop且 B Activity 已經(jīng)在棧頂時(一些特殊情況如通知欄點擊、連點试浙、B Activity自己打開自己)董瞻,此時只有 B 頁面自己有生命周期變化:B.onPause() -> B.onNewIntent() -> B.onResume()

  • 當 B ActivitylaunchModesingleInstancesingleTask 且對應(yīng)的 B Activity 有可復(fù)用的實例時:A.onPause() -> B.onNewIntent() -> B.onRestart() -> B.onStart() -> B.onResume() -> A.onStop() --> A.onDestory()(如果需要關(guān)閉Activity A[A 被移出棧])

  • 當 B ActivityThemeDialog 時:A.onPause() -> B.onCreate() -> B.onStart() -> B.onResume()(注意前一個 Activity 不會回調(diào) onStop()田巴,因為只有在 Activity 切到后臺(不在Activity棧頂)不可見才會回調(diào) onStop()钠糊;而彈出 Dialog 主題的 Activity 時前一個頁面還是可見的挟秤,只是失去了焦點而已所以僅有 onPause() 回調(diào))

2. 彈出 DialogActivity 生命周期有什么影響?

先下結(jié)論:通過 《Android Activity——啟動過程探索(一)》 《Android Activity——啟動過程探索(二)》 《Android Activity——啟動過程探索(三)》 我們知道Activity生命周期回調(diào)都是通過ActivityTaskManagerService(ATMS)(在Android 10之前是通過 ActivityManagerService(AMS))來回調(diào)的眠蚂。但是彈出 DialogToast斗躏、PopupWindow 本質(zhì)上都直接是通過 WindowManager.addView() 顯示的(沒有經(jīng)過 ATMS/AMS)逝慧,所以不會對生命周期有任何影響。

Dialog顯示過程源碼解析

源碼版本為 Android 10(Api 29)啄糙,不同Android版本可能有一些差別

  1. 構(gòu)造方法中獲取 WindowManager 和創(chuàng)建 PhoneWindow 并通過 PhoneWindowsetWindowManager() 方法關(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);
     }
    
  2. 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);
             }
         }
     }
    

ViewRootImplsetView() 方法說明:

  1. mWindowSession 是通過 WindowManagerGlobalgetWindowSession() 方法獲取到的僧免,返回的是通過 WindowManagerServiceopenSession() 方法創(chuàng)建的 Session 對象

  2. mWindow 對象為 ViewRootImpl 的 內(nèi)部類 static class W extends IWindow.Stub,通過定義可以看出來捏浊,是一個 Binder 對象

  3. 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)用 FragmentonViewCreated() 方法惊暴;而 方法的返回值 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)用 DialogFragmentshow() 方法時茉帅,把將 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)鍵點
    }
}

方法說明:

  1. ViewManager wm = a.getWindowManager() 方法最終返回的是 WindowManagerImpl 對象惋啃,原因如下:

    1. Activity.getWindowManager() 方法返回 Activity 的成員變量 mWindowManager
    2. Activity 的成員變量 mWindowManagerActivityattach() 方法中被賦值 mWindowManager = mWindow.getWindowManager()哼鬓,調(diào)用的是 WindowgetWindowManager() 方法
    3. WindowgetWindowManager() 方法返回的是他的成員變量 mWindowManager
    4. Window 的成員變量 mWindowManager 在他的 setWindowManager() 方法中被賦值 mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this) ,所以得出結(jié)論 a.getWindowManager() 方法最終返回的是 WindowManagerImpl 對象
  2. 關(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&params2=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&params2=true">打開APP</a>
            </body>
        </html>

6. Activity 什么時候會發(fā)生重建?

發(fā)生時機:

  1. 系統(tǒng)資源回收:內(nèi)存不足辱匿,低優(yōu)先級的Activity會被殺死
  2. 配置發(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)限府。

  1. ActivityonSaveInstanceState() 執(zhí)行在 onStop() 之前夺颤,與 onPause() 沒有固定的時序關(guān)系;onRestoreInstanceState() 執(zhí)行在 onStart() 之后胁勺。不止 Activity 有這兩個方法世澜,每個 View 也有這兩個方法,用來保存 View 的信息署穗。
  2. 正常情況下的活動銷毀并不會調(diào)用這兩個方法寥裂,只有當活動異常銷毀并且有機會重現(xiàn)展示的時候才會進行調(diào)用。
  3. ActivityonRestoreInstanceState()onCreate() 都可以進行數(shù)據(jù)恢復(fù)工作案疲,但建議采用在 onRestoreInstanceState() 中去恢復(fù)(因為調(diào)用了這個方法封恰,用來存取數(shù)據(jù)的 Bundle 肯定不為 null)。
  4. onSaveInstanceState()onRestoreInstanceState() 這兩個方法中褐啡,系統(tǒng)會默認為我們進行一定的恢復(fù)工作诺舔,比如:會為布局中的每個 View 調(diào)用相應(yīng)的 onSaveInstanceState() 方法,讓每個視圖都能提供有關(guān)自身的應(yīng)保存信息备畦。

具體調(diào)用時機說明

Activity#onSaveInstanceState() 方法調(diào)用時機:

  1. 當用戶按下HOME鍵時
  2. 從最近應(yīng)用中選擇運行其他的程序時
  3. 按下電源按鍵(關(guān)閉屏幕顯示)時
  4. 從當前activity啟動一個新的activity時
  5. 屏幕方向切換時(無論豎屏切橫屏還是橫屏切豎屏都會調(diào)用)
  6. 被系統(tǒng)回收時

Activity#onRestoreInstanceState() 方法調(diào)用時機:

  1. 被系統(tǒng)回收且重建時
  2. 屏幕方向切換時(無論豎屏切橫屏還是橫屏切豎屏都會調(diào)用)
  3. 語言切換時

7. 在 Activity 的 onCreate() 方法里寫死循環(huán)會 ANR 嗎低飒?

ANR全稱:Application Not Responding,也就是應(yīng)用程序無響應(yīng)

Android 中產(chǎn)生 ANR 的原因:

  1. Service TimeOut: service 未在規(guī)定時間執(zhí)行完成:前臺服務(wù) 20s懂盐,后臺 200s
  2. BroadCastQueue TimeOut: 未在規(guī)定時間內(nèi)未處理完廣播:前臺廣播 10s 內(nèi), 后臺 60s 內(nèi)
  3. ContentProvider TimeOut: publish 在 10s 內(nèi)沒有完成
  4. Input Dispatching timeout: 5s 內(nèi)未響應(yīng)鍵盤輸入褥赊、觸摸屏幕等事件

根據(jù)以上 ANR 產(chǎn)生的原因,說明在 ActivityonCreate() 方法中寫死循環(huán)是不會發(fā)生ANR的莉恼,但是主線程被死循環(huán)一直占用了拌喉,所以當再有其他事件產(chǎn)生時,就不能及時響應(yīng)了俐银,從而導(dǎo)致ANR發(fā)生尿背。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市悉患,隨后出現(xiàn)的幾起案子残家,更是在濱河造成了極大的恐慌,老刑警劉巖售躁,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坞淮,死亡現(xiàn)場離奇詭異,居然都是意外死亡陪捷,警方通過查閱死者的電腦和手機回窘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來市袖,“玉大人啡直,你說我怎么就攤上這事烁涌。” “怎么了酒觅?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵撮执,是天一觀的道長。 經(jīng)常有香客問我舷丹,道長抒钱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任颜凯,我火速辦了婚禮谋币,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘症概。我一直安慰自己蕾额,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布彼城。 她就那樣靜靜地躺著诅蝶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪精肃。 梳的紋絲不亂的頭發(fā)上秤涩,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天弓候,我揣著相機與錄音布持,去河邊找鬼蓉坎。 笑死,一個胖子當著我的面吹牛习柠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播照棋,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼资溃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了烈炭?” 一聲冷哼從身側(cè)響起溶锭,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎符隙,沒想到半個月后趴捅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡霹疫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年拱绑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丽蝎。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡猎拨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情红省,我是刑警寧澤额各,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站吧恃,受9級特大地震影響臊泰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蚜枢,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一缸逃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厂抽,春花似錦需频、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至藐守,卻和暖如春挪丢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卢厂。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工乾蓬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慎恒。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓任内,卻偏偏與公主長得像,于是被迫代替她去往敵國和親融柬。 傳聞我的和親對象是個殘疾皇子死嗦,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容