01.Android之基礎組件問題

目錄介紹

  • 1.0.0.1 說下Activity的生命週期?屏幕旋轉時生命週期?異常條件會調用什麼方法?
  • 1.0.0.2 後臺的Activity被系統回收怎麼辦?說一下onSaveInstanceState()和onRestoreInstanceState()方法特點?
  • 1.0.0.3 如何避免配置改變時Activity重建?優先級低的Activity在內存不足被回收後怎樣做可以恢復到銷燬前狀態?
  • 1.0.0.4 app切換到後臺,當前activity會走onDestory方法嗎?一般在onstop方法裏做什麼?什麼情況會導致app會被殺死?
  • 1.0.0.5 Activity的啓動過程是有幾種方式?從桌面launcher上點擊應用圖標會幹啥,調用startActivty()又會做什麼?
  • 1.0.0.6 說下Activity的四種啓動模式?singleTop和singleTask的區別以及應用場景?任務棧的作用是什麼?
  • 1.0.0.7 兩個Activity之間怎麼傳遞數據?intent和bundle有什麼區別?爲什麼有了intent還要設計bundle?
  • 1.0.0.8 知道哪些Activity啓動模式的標記位?flag是幹什麼用的,什麼時候用到?
  • 1.0.1.0 同一程序不同的Activity是否可以放在不同的Task任務棧中?
  • 1.0.1.1 介紹一下Service,啓動Service有幾種方式,生命週期是怎樣的?說一下onStartCommand()的作用?service如何殺不死?
  • 1.0.1.2 一個Activty先start一個Service後,再bind時會回調什麼方法?此時如何做才能回調Service的destory()方法?
  • 1.0.1.3 bindService是一個異步的過程嗎?綁定service大概需要經歷那些過程?
  • 1.0.1.4 是否能在Service進行耗時操作?如果非要可以怎麼做,如何避免service線程卡頓?service裏面可以彈土司嗎?
  • 1.0.1.5 Activity如何與Service通信?Service的生命週期與啓動方法有什麼區別?
  • 1.0.2.0 是否瞭解ActivityManagerService,它發揮什麼作用,說一下AMS啓動流程?
  • 1.0.2.1 Android中哪些事件需要用到廣播?廣播的生命週期是怎樣的?
  • 1.0.2.3 廣播有幾種形式?他們分別有什麼特點,如何使用廣播?廣播是怎麼實現不同進程之間通信的?
  • 1.0.2.8 Fragment與Activity之間是如何傳值的?Fragment與Fragment之間是如何傳值的?
  • 1.0.2.9 Activity創建Fragment的方式是什麼?FragmentPageAdapter和FragmentPageStateAdapter的區別?
  • 1.0.3.0 fragment 特點?說一下Fragment的生命週期?如何解決getActivity爲null的異常問題?
  • 1.0.3.1 在fragment中爲什麼有時getActivity()會爲null?Fragment試圖爲什麼有的時候會重疊,怎麼產生的,又如何解決?
  • 1.0.3.2 爲什麼fragment傳遞數據不用構造方法傳遞?FragmentManager , add 和 replace 有什麼區別?
  • 1.0.3.9 Activitiy啓動流程中performLaunchActivity的作用?
  • 1.0.4.0 Intent是什麼?Intent可以傳遞哪些數據?傳遞對象的時候爲什麼要實例化?
  • 1.0.4.1 mipmap系列中xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi和ldpi存在怎樣的關係?
  • 1.0.4.2 res目錄和assets目錄的區別?R文件是如何生成的,主要有什麼作用?
  • 1.0.4.3 Context是什麼?Context有哪些類型,分別作用是什麼?Context下有哪些子類?哪些場景只能用activity上下文?
  • 1.0.4.4 ActivityThread的main()的流程大概是怎麼樣的?
  • 1.0.5.0 序列化的方式有哪些?效率對比有何優勢?如何做性能上分析的?
  • 1.0.5.9 界面的刷新爲什麼需16.6ms?畫面的顯示需要哪些步驟?界面保持不變時還會16.6ms刷新一次屏幕嗎?
  • 1.0.6.0 Android中日誌級別有哪幾種?開發中需要注意什麼問題,打印日誌源碼分析原理是什麼?

好消息

  • 博客筆記大彙總【15年10月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,當然也在工作之餘收集了大量的面試題,長期更新維護並且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計500篇[近100萬字],將會陸續發表到網上,轉載請註明出處,謝謝!
  • 鏈接地址:https://github.com/yangchong2...
  • 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起於忽微,量變引起質變!所有的筆記將會更新到GitHub上,同時保持更新,歡迎同行提出或者push不同的看法或者筆記!

1.0.0.1 說下Activity的生命週期?屏幕旋轉時生命週期?異常條件會調用什麼方法?

  • 在Activity的生命週期涉及到七大方法,分別是:

    • onCreate()表示Activity 正在創建,常做初始化工作,如setContentView界面資源、初始化數據
    • onStart()表示Activity 正在啓動,這時Activity 可見但不在前臺,無法和用戶交互
    • onResume()表示Activity 獲得焦點,此時Activity 可見且在前臺並開始活動
    • onPause()表示Activity 正在停止,可做 數據存儲、停止動畫等操作
    • onStop()表示activity 即將停止,可做稍微重量級回收工作,如取消網絡連接、註銷廣播接收器等
    • onDestroy()表示Activity 即將銷燬,常做回收工作、資源釋放
    • onRestart()表示當Activity由後臺切換到前臺,由不可見到可見時會調用,表示Activity 重新啓動
  • 屏幕旋轉時生命週期

    • 屏幕旋轉時候,如果不做任何處理,activity會經過銷燬到重建的過程。一般這種效果都不是想要的。比如視頻播放器就經常會涉及屏幕旋轉場景。技術博客大總結
    • 第一種情況:當前的Activity不銷燬【設置Activity的android:configChanges="orientation|keyboardHidden|screenSize"時,切屏不會重新調用各個生命週期,只會執行onConfigurationChanged方法】

      <activity
          android:name=".activity.VideoDetailActivity"
          android:configChanges="orientation|keyboardHidden|screenSize"
          android:screenOrientation="portrait"/>
      • 執行該方法
      //重寫旋轉時方法,不銷燬activity
      @Override
      public void onConfigurationChanged(Configuration newConfig) {
          super.onConfigurationChanged(newConfig);
      }
    • 第二種情況:銷燬當前的Activity後重建,這種也儘量避免。【不設置Activity的android:configChanges時,切屏會重新調用各個生命週期,默認首先銷燬當前activity,然後重新加載】
  • 異常條件會調用什麼方法

    • 當非人爲終止Activity時,比如系統配置發生改變時導致Activity被殺死並重新創建、資源內存不足導致低優先級的Activity被殺死,會調用 onSavaInstanceState() 來保存狀態。該方法調用在onStop之前,但和onPause沒有時序關係。
    • 有人會問,onSaveInstanceState()與onPause()的區別,onSaveInstanceState()適用於對臨時性狀態的保存,而onPause()適用於對數據的持久化保存。
    • 當異常崩潰後App又重啓了,這個時候會走onRestoreInstanceState()方法,可以在該方法中取出onSaveInstanceState()保存的狀態數據。
  • 什麼時候會引起異常生命週期

    • 資源相關的系統配置發生改變或者資源不足:例如屏幕旋轉,當前Activity會銷燬,並且在onStop之前回調onSaveInstanceState保存數據,在重新創建Activity的時候在onStart之後回調onRestoreInstanceState。其中Bundle數據會傳到onCreate(不一定有數據)和onRestoreInstanceState(一定有數據)。技術博客大總結
    • 防止屏幕旋轉的時候重建,在清單文件中添加配置:android:configChanges="orientation"

1.0.0.2 後臺的Activity被系統回收怎麼辦?說一下onSaveInstanceState()和onRestoreInstanceState()方法特點?

  • 後臺的Activity被系統回收怎麼辦?

    • Activity中提供了一個 onSaveInstanceState()回調方法,這個方法會保證一定在活動被回收之前調用,可以通過這個方法來解決活動被回收時臨時數據得不到保存的問題。onSaveInstanceState()方法會攜帶一個Bundle類型的參數,Bundle提供了一系列的方法用於保存數據,比如可以使用putString()方法保存字符串,使用putInt()方法保存整型數據。每個保存方法需要傳入兩個參數,第一個參數是鍵,用於後面從 Bundle中取值,第二個參數是真正要保存的內容。技術博客大總結
  • 說一下onSaveInstanceState()和onRestoreInstanceState()方法特點?

    • Activity的 onSaveInstanceState()和onRestoreInstanceState()並不是生命週期方法,它們不同於onCreate()、onPause()等生命週期方法,它們並不一定會被觸發。

      //保存數據
      @Override
      protected void onSaveInstanceState(Bundle outBundle) {
          super.onSaveInstanceState(outBundle);
           outBundle.putBoolean("Change", mChange);
      }
      
      //取出數據
      @Override 
      protected void onRestoreInstanceState(Bundle savedInstanceState) {
          super.onRestoreInstanceState(savedInstanceState);
          mChange = savedInstanceState.getBoolean("Change");
      }
      
      //或者在onCreate方法取數據也可以
      //onCreate()方法其實也有一個Bundle類型的參數。這個參數在一般情況下都是null,
      //但是當活動被系統回收之前有通過 onSaveInstanceState()方法來保存數據的話,這個參就會帶有之前所保存的全部數據
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          if (savedInstanceState != null) {
              String data = savedInstanceState.getString("data");
          }
      }
  • 什麼時候會觸發走這兩個方法?

    • 當應用遇到意外情況(如:內存不足、用戶直接按Home鍵)由系統銷燬一個Activity,onSaveInstanceState() 會被調用。但是當用戶主動去銷燬一個Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被調用。除非該activity是被用戶主動銷燬的,通常onSaveInstanceState()只適合用於保存一些臨時性的狀態,而onPause()適合用於數據的持久化保存。
  • onSaveInstanceState()被執行的場景有哪些?

    • 系統不知道你按下HOME後要運行多少其他的程序,自然也不知道activityA是否會被銷燬,因此係統都會調用onSaveInstanceState(),讓用戶有機會保存某些非永久性的數據。以下幾種情況的分析都遵循該原則當用戶按下HOME鍵時

      • 長按HOME鍵,選擇運行其他的程序時
      • 鎖屏時
      • 從activity A中啓動一個新的activity時
      • 屏幕方向切換時

1.0.0.3 如何避免配置改變時Activity重建?優先級低的Activity在內存不足被回收後怎樣做可以恢復到銷燬前狀態?

  • 如何避免配置改變時Activity重建

    • 爲了避免由於配置改變導致Activity重建,可在AndroidManifest.xml中對應的Activity中設置android:configChanges="orientation|screenSize"。此時再次旋轉屏幕時,該Activity不會被系統殺死和重建,只會調用onConfigurationChanged。因此,當配置程序需要響應配置改變,指定configChanges屬性,重寫onConfigurationChanged方法即可。
    • 使用場景,比如視頻播放器橫豎屏切換播放視頻,就需要設置這種屬性。具體可以看我封裝的視頻播放器庫,地址:https://github.com/yangchong2...
  • 優先級低的Activity在內存不足被回收後怎樣做可以恢復到銷燬前狀態

    • 優先級低的Activity在內存不足被回收後重新打開會引發Activity重建。Activity被重新創建時會調用onRestoreInstanceState(該方法在onStart之後),並將onSavaInstanceState保存的Bundle對象作爲參數傳到onRestoreInstanceState與onCreate方法。因此可通過onRestoreInstanceState(Bundle savedInstanceState)和onCreate((Bundle savedInstanceState)來判斷Activity是否被重建,並取出數據進行恢復。但需要注意的是,在onCreate取出數據時一定要先判斷savedInstanceState是否爲空。
  • 如何判斷activity的優先級?技術博客大總結

    • 除了在棧頂的activity,其他的activity都有可能在內存不足的時候被系統回收,一個activity越處於棧底,被回收的可能性越大.如果有多個後臺進程,在選擇殺死的目標時,採用最近最少使用算法(LRU)。

1.0.0.4 app切換到後臺,當前activity會走onDestory方法嗎?一般在onstop方法裏做什麼?什麼情況會導致app會被殺死,這時候會走onDestory嗎?

  • app切換到後臺,當前activity會走onDestory方法嗎?

    • 不會走onDestory方法,會先後走onPause和onStop方法。
  • 一般在onstop方法裏做什麼?

    • 比如。寫輪播圖的時候,會在onstop方法裏寫上暫停輪播圖無限輪播,在onStart方法中會開啓自動無限輪播。
    • 再比如,寫視頻播放器的時候,當app切換到後臺,則需要停止視頻播放,也是可以在onstop中處理的。關於視頻播放器,可以看我這個開源項目:視頻播放器
  • 什麼情況會導致app會被殺死,這時候會走onDestory嗎?

    • 系統資源不足,會導致app意外被殺死。應用只有在進程存活的情況下才會按照正常的生命週期進行執行,如果進程突然被kill掉,相當於System.exit(0); 進程被殺死,根本不會走(activity,fragment)生命週期。只有在進程不被kill掉,正常情況下才會執行ondestory()方法。
  • activity被回收如何恢復

    • 當系統內存不足時, activity會被回收,我們其實可以覆寫onSaveInstanceState()方法。onSaveInstanceState()方法接受一個Bundle類型的參數, 開發者可以將狀態數據存儲到這個Bundle對象中,這樣即使activity被系統摧毀,當用戶重新啓動這個activity而調用它的onCreate()方法時,上述的Bundle對象會作爲實參傳遞給onCreate()方法,開發者可以從Bundle對象中取出保存的數據, 然後利用這些數據將activity恢復到被摧毀之前的狀態。

1.0.0.5 Activity的啓動過程是有幾種方式?從桌面launcher上點擊應用圖標會幹啥,調用startActivty()又會做什麼?

  • Activity的啓動過程是怎樣的,有幾種方式?

    • 注意是啓動過程,不是生命週期。技術博客大總結
    • app啓動的過程有兩種情況,第一種是從桌面launcher上點擊相應的應用圖標,第二種是在activity中通過調用startActivity來啓動一個新的activity。
  • 從桌面launcher上點擊應用圖標會幹啥,調用startActivty()又會做什麼?

    • 創建一個新的項目,默認的根activity都是MainActivity,而所有的activity都是保存在堆棧中的,啓動一個新的activity就會放在上一個activity上面,而我們從桌面點擊應用圖標的時候,由於launcher本身也是一個應用,當我們點擊圖標的時候,系統就會調用startActivitySately(),一般情況下,我們所啓動的activity的相關信息都會保存在intent中,比如action,category等等。
    • 我們在安裝這個應用的時候,系統也會啓動一個PackaManagerService的管理服務,這個管理服務會對AndroidManifest.xml文件進行解析,從而得到應用程序中的相關信息,比如service,activity,Broadcast等等,然後獲得相關組件的信息。
    • 當我們點擊應用圖標的時候,就會調用startActivitySately()方法,而這個方法內部則是調用startActivty(),而startActivity()方法最終還是會調用startActivityForResult()這個方法。而在startActivityForResult()這個方法。因爲startActivityForResult()方法是有返回結果的,所以系統就直接給一個-1,就表示不需要結果返回了。
    • 而startActivityForResult()這個方法實際是通過Instrumentation類中的execStartActivity()方法來啓動activity,Instrumentation這個類主要作用就是監控程序和系統之間的交互。而在這個execStartActivity()方法中會獲取ActivityManagerService的代理對象,通過這個代理對象進行啓動activity。啓動會就會調用一個checkStartActivityResult()方法,如果說沒有在配置清單中配置有這個組件,就會在這個方法中拋出異常了。
    • 當然最後是調用的是Application.scheduleLaunchActivity()進行啓動activity,而這個方法中通過獲取得到一個ActivityClientRecord對象,而這個ActivityClientRecord通過handler來進行消息的發送,系統內部會將每一個activity組件使用ActivityClientRecord對象來進行描述,而ActivityClientRecord對象中保存有一個LoaderApk對象,通過這個對象調用handleLaunchActivity來啓動activity組件,而頁面的生命週期方法也就是在這個方法中進行調用。

1.0.0.6 說下Activity的四種啓動模式?singleTop和singleTask的區別以及應用場景?任務棧的作用是什麼?

  • Activity的四種啓動模式

    • standard標準模式:每次啓動一個Activity就會創建一個新的實例
    • singleTop棧頂複用模式:如果新Activity已經位於任務棧的棧頂,就不會重新創建,並回調 onNewIntent(intent) 方法
    • singleTask棧內複用模式:只要該Activity在一個任務棧中存在,都不會重新創建,並回調 onNewIntent(intent) 方法。如果不存在,系統會先尋找是否存在需要的棧,如果不存在該棧,就創建一個任務棧,並把該Activity放進去;如果存在,就會創建到已經存在的棧中
    • singleInstance單實例模式:具有此模式的Activity只能單獨位於一個任務棧中,且此任務棧中只有唯一一個實例
  • singleTop和singleTask的區別以及應用場景

    • singleTop:同個Activity實例在棧中可以有多個,即可能重複創建;該模式的Activity會默認進入啓動它所屬的任務棧,即不會引起任務棧的變更;爲防止快速點擊時多次startActivity,可以將目標Activity設置爲singleTop
    • singleTask:同個Activity實例在棧中只有一個,即不存在重複創建;可通過android:taskAffinity設定該Activity需要的任務棧,即可能會引起任務棧的變更;常用於主頁和登陸頁
  • singleTop或singleTask的Activity在以下情況會回調onNewIntent()

    • singleTop:如果新Activity已經位於任務棧的棧頂,就不會重新創建,並回調 onNewIntent(intent) 方法
    • singleTask:只要該Activity在一個任務棧中存在,都不會重新創建,並回調 onNewIntent(intent) 方法
  • 任務棧的作用是什麼?技術博客大總結

    • 它是存放 Activity 的引用的,Activity不同的啓動模式,對應不同的任務棧的存放;可通過 getTaskId()來獲取任務棧的 ID,如果前面的任務棧已經清空,新開的任務棧ID+1,是自動增長的;首先來看下Task的定義,Google是這樣定義Task的:Task實際上是一個Activity棧,通常用戶感受的一個Application就是一個Task。從這個定義來看,Task跟Service或者其他Components是沒有任何聯繫的,它只是針對Activity而言的。

1.0.0.7 兩個Activity之間怎麼傳遞數據?intent和bundle有什麼區別?爲什麼有了intent還要設計bundle?

  • 兩個Activity之間怎麼傳遞數據?

    • 基本數據類型可以通過Intent傳遞數據
    • 把數據封裝至intent對象中

      Intent intent = new Intent(content, MeActivity.class);
      intent.putExtra("goods_id", goods_id);
      content.startActivity(intent);
    • 把數據封裝至bundle對象中技術博客大總結
    • 把bundle對象封裝至intent對象中

      Bundle bundle = new Bundle();
      bundle.putString("malename", "李志");
      intent.putExtras(bundle);
      startActivity(intent); 
  • intent和bundle有什麼區別?

    • Intent傳遞數據和Bundle傳遞數據是一回事,Intent傳遞時內部還是調用了Bundle。

      public @NonNull Intent putExtra(String name, String value) {
          if (mExtras == null) {
              mExtras = new Bundle();
          }
          mExtras.putString(name, value);
          return this;
      }
  • 爲什麼有了intent還要設計bundle?

    • 兩者比較

      • Bundle只是一個信息的載體,內部其實就是維護了一個Map<String,Object>。
      • Intent負責Activity之間的交互,內部是持有一個Bundle的。
    • bundle使用場景

      • Fragment之間傳遞數據;比如,某個Fragment中點擊按鈕彈出一個DialogFragment。最便捷的方式就是通過Fragment.setArguments(args)傳遞參數。
      public static void showFragmentDialog(String title, String content, boolean is_open, AppCompatActivity activity) {
          ServiceDialogFragment mainDialogFragment = new ServiceDialogFragment();
          Bundle bundle = new Bundle();
          bundle.putString("title", title);
          bundle.putString("content", content);
          bundle.putBoolean("is_open",is_open);
          mainDialogFragment.setArguments(bundle);
          mainDialogFragment.show(activity.getSupportFragmentManager());
      }
      
      @Override
      public void onCreate(Bundle savedInstanceState) {
          setLocal(Local.CENTER);
          super.onCreate(savedInstanceState);
          Bundle bundle = getArguments();
          if (bundle != null) {
              title = bundle.getString("title");
              content = bundle.getString("content");
              is_open = bundle.getBoolean("is_open");
          }
      }

1.0.0.8 知道哪些Activity啓動模式的標記位?flag是幹什麼用的,什麼時候用到?

  • 常見的標記爲:

    • FLAG_ACTIVITY_SINGLE_TOP:對應singleTop啓動模式
    • FLAG_ACTIVITY_NEW_TASK :對應singleTask模式

1.0.1.0 同一程序不同的Activity是否可以放在不同的Task任務棧中?

  • 同一程序不同的Activity是否可以放在不同的Task任務棧中?

    • 可以的。比如:啓動模式裏有個Singleinstance,可以運行在另外的單獨的任務棧裏面。用這個模式啓動的activity,在內存中只有一份,這樣就不會重複的開啓。
    • 也可以在激活一個新的activity時候,給intent設置flag,Intent的flag添加FLAG_ACTIVITY_NEW_TASK,這個被激活的activity就會在新的task棧裏面

1.0.1.1 介紹一下Service,啓動Service有幾種方式,生命週期是怎樣的?說一下onStartCommand()的作用?service如何殺不死?

  • Service分爲兩種

    • 本地服務,屬於同一個應用程序,通過startService來啓動或者通過bindService來綁定並且獲取代理對象。如果只是想開個服務在後臺運行的話,直接startService即可,如果需要相互之間進行傳值或者操作的話,就應該通過bindService。
    • 遠程服務(不同應用程序之間),通過bindService來綁定並且獲取代理對象。
  • 對應的生命週期如下:

    • context.startService() ->onCreate()- >onStartCommand()->Service running--調用context.stopService() ->onDestroy()
    • context.bindService()->onCreate()->onBind()->Service running--調用>onUnbind() -> onDestroy()
  • 注意

    • Service默認是運行在main線程的,因此Service中如果需要執行耗時操作(大文件的操作,數據庫的拷貝,網絡請求,文件下載等)的話應該在子線程中完成。
  • Service生命週期解釋技術博客大總結

    • onCreate():服務第一次被創建時調用
    • onStartComand():服務啓動時調用
    • onBind():服務被綁定時調用
    • onUnBind():服務被解綁時調用
    • onDestroy():服務停止時調用
  • 說一下onStartCommand()的作用?
  • service如何殺不死?

    • 1.onStartCommand方法,返回START_STICKY(粘性)當service因內存不足被kill,當內存又有的時候,service又被重新創建
    • 2.設置優先級,在服務裏的ondestory裏發送廣播 在廣播裏再次開啓這個服務,雙進程守護

1.0.1.2 一個Activty先start一個Service後,再bind時會回調什麼方法?此時如何做才能回調Service的destory()方法?

  • 關於service中onDestroy()什麼時候會被執行?

    • 當調用了startService()方法後,又去調用stopService()方法,這時服務中的onDestroy()方法就會執行,表示服務已經銷燬了。
    • 類似地,當調用了 bindService()方法後,又去調用unbindService()方法,onDestroy()方法也會執行,這兩種情況都很好理解。
  • 一個Activty先start一個Service後,再bind時會回調什麼方法?

  • 先start後bind操作service,此時如何做才能回調Service的destory()方法?

    • 完全有可能對一個服務既調用了startService()方法,又調用了bindService()方法的,這種情況下該如何才能讓服務銷燬掉呢?根據Android系統的機制,一個服務只要被啓動或者被綁定了之後,就會一直處於運行狀態,必須要讓以上兩種條件同時不滿足,服務才能被銷燬。
    • 這種情況下要同時調用stopService()和unbindService()方法,onDestroy()方法纔會執行這樣就把服務的生命週期完整地走了一遍。技術博客大總結

1.0.1.3 bindService是一個異步的過程嗎?綁定service大概需要經歷那些過程?

1.0.1.4 是否能在Service進行耗時操作?如果非要可以怎麼做,如何避免service線程卡頓?service裏面可以彈土司嗎?

  • 是否能在Service進行耗時操作?

    • 默認情況,如果沒有顯示的指定service所運行的進程,Service和Activity是運行在當前app所在進程的mainThread(UI主線程)裏面。
    • service裏面不能執行耗時的操作(網絡請求,拷貝數據庫,大文件),在Service裏執行耗時操作,有可能出現主線程被阻塞(ANR)的情況。
  • 如果非要可以怎麼做,如何避免service線程卡頓?

    • 需要在子線程中執行 new Thread(){}.start();
  • service裏面可以彈土司嗎?

    • 可以,但是有條件。一般很少這樣做……技術博客大總結
    • 條件是,service裏面彈toast需要添加到主線程裏執行。

      @Override  
      public void onCreate(){  
          handler = new Handler(Looper.getMainLooper());                          
          System.out.println("service started");  
          handler.post(new Runnable() {    
              @Override    
              public void run() {    
                  Toast.makeText(getApplicationContext(), "Test",Toast.LENGTH_SHORT).show();
              }
          });
      }

1.0.1.5 Activity如何與Service通信?Service的生命週期與啓動方法有什麼區別?

  • Activity如何與Service通信?

    • 方法一:

      • 添加一個繼承Binder的內部類,並添加相應的邏輯方法。重寫Service的onBind方法,返回我們剛剛定義的那個內部類實例。Activity中創建一個ServiceConnection的匿名內部類,並且重寫裏面的onServiceConnected方法和onServiceDisconnected方法,這兩個方法分別會在活動與服務成功綁定以及解除綁定的時候調用,在onServiceConnected方法中,我們可以得到一個剛纔那個service的binder對象,通過對這個binder對象進行向下轉型,得到我們那個自定義的Binder實例,有了這個實例,做可以調用這個實例裏面的具體方法進行需要的操作了
    • 方法二

      • 通過BroadCast(廣播)的形式,當我們的進度發生變化的時候我們發送一條廣播,然後在Activity的註冊廣播接收器,接收到廣播之後更新視圖

1.0.2.0 是否瞭解ActivityManagerService,它發揮什麼作用,說一下AMS啓動流程?

  • ActivityManagerService是Android中最核心的服務,主要負責系統中四大組件的啓動、切換、調度及應用進程的管理和調度等工作,其職責與操作系統中的進程管理和調度模塊類似。
  • https://blog.csdn.net/dutedeh...

1.0.2.1 Android中哪些事件需要用到廣播?廣播的生命週期是怎樣的?

  • Android中哪些事件需要用到廣播?

    • Android中:系統在運行過程中,會產生會多事件,那麼某些事件產生時,比如:電量改變、收發短信、撥打電話、屏幕解鎖、開機,系統會發送廣播,只要應用程序接收到這條廣播,就知道系統發生了相應的事件,從而執行相應的代碼。使用廣播接收者,就可以收聽廣播
  • 廣播的生命週期是怎樣的?

    • a.廣播接收者的生命週期非常短暫的,在接收到廣播的時候創建,onReceive()方法結束之後銷燬;
    • b.廣播接收者中不要做一些耗時的工作,否則會彈出 Application No Response錯誤對話框;
    • c.最好也不要在廣播接收者中創建子線程做耗時的工作,因爲廣播接收者被銷燬後進程就成爲了空進程,很容易被系統殺掉;
    • d.耗時的較長的工作最好放在服務中完成;

1.0.2.3 廣播有幾種形式?他們分別有什麼特點,如何使用廣播?廣播是怎麼實現不同進程之間通信的?

  • 廣播有幾種形式

    • 普通廣播:一種完全異步執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,因此它們接收的先後是隨機的。
    • 有序廣播:一種同步執行的廣播,在廣播發出之後,同一時刻只會有一個廣播接收器能夠收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播纔會繼續傳遞,所以此時的廣播接收器是有先後順序的,且優先級(priority)高的廣播接收器會先收到廣播消息。有序廣播可以被接收器截斷使得後面的接收器無法收到它。
    • 本地廣播:發出的廣播只能夠在應用程序的內部進行傳遞,並且廣播接收器也只能接收本應用程序發出的廣播。
    • 粘性廣播:這種廣播會一直滯留,當有匹配該廣播的接收器被註冊後,該接收器就會收到此條廣播。
  • 廣播的兩種註冊形式技術博客大總結

    • 廣播的註冊有兩種方法:一種在活動裏通過代碼動態註冊,另一種在配置文件裏靜態註冊。兩種方式的相同點是都完成了對接收器以及它能接收的廣播值這兩個值的定義;不同點是動態註冊的接收器必須要在程序啓動之後才能接收到廣播,而靜態註冊的接收器即便程序未啓動也能接收到廣播,比如想接收到手機開機完成後系統發出的廣播就只能用靜態註冊了。
  • 動態註冊

    • 需要使用廣播接收者時,執行註冊的代碼,不需要時,執行解除註冊的代碼。安卓中有一些廣播接收者,必須使用代碼註冊,清單文件註冊是無效的。

      public class MainActivity extends Activity {
          private IntentFilter intentFilter;
          private NetworkChangeReceiver networkChangeReceiver;
          @Override
          protected void onCreate(Bundle savedInstanceState) {
                 super.onCreate(savedInstanceState);
                 setContentView(R.layout.activity_main);
                 intentFilter = new IntentFilter();
                 intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
                 networkChangeReceiver = new NetworkChangeReceiver();
                 registerReceiver(networkChangeReceiver, intentFilter);
          }
          @Override
          protected void onDestroy() {
                 super.onDestroy();
                 unregisterReceiver(networkChangeReceiver);
          }
          class NetworkChangeReceiver extends BroadcastReceiver {
                  @Override
                  public void onReceive(Context context, Intent intent) {
                       Toast.makeText(context, "network changes",Toast.LENGTH_SHORT).show();
                  }
          }
      }
  • 靜態註冊

    • 可以使用清單文件註冊。廣播一旦發出,系統就會去所有清單文件中尋找,哪個廣播接收者的action和廣播的action是匹配的,如果找到了,就把該廣播接收者的進程啓動起來。

1.0.2.8 Fragment與Activity之間是如何傳值的?Fragment與Fragment之間是如何傳值的?

  • Fragment與Activity之間是如何傳值的?

    • 1.Activity向Fragment傳值:

      • 步驟:
      • 要傳的值,放到bundle對象裏;
      • 在Activity中創建該Fragment的對象fragment,通過調用
      • fragment.setArguments()傳遞到fragment中;
      • 在該Fragment中通過調用getArguments()得到bundle對象,就能得到裏面的值。
    • 2.Fragment向Activity傳值:

      • 第一種:

        • 在Activity中調用getFragmentManager()得到fragmentManager,,調用findFragmentByTag(tag)或者通過findFragmentById(id)
        • FragmentManager fragmentManager = getFragmentManager();
        • Fragment fragment = fragmentManager.findFragmentByTag(tag);
      • 第二種:

        • 通過回調的方式,定義一個接口(可以在Fragment類中定義),接口中有一個空的方法,在fragment中需要的時候調用接口的方法,值可以作爲參數放在這個方法中,然後讓Activity實現這個接口,必然會重寫這個方法,這樣值就傳到了Activity中
  • Fragment與Fragment之間是如何傳值的?

    • 第一種:

      • 通過findFragmentByTag得到另一個的Fragment的對象,這樣就可以調用另一個的方法了。
    • 第二種:

    • 第三種:

      • 通過setArguments,getArguments的方式。

1.0.2.9 Activity創建Fragment的方式是什麼?FragmentPageAdapter和FragmentPageStateAdapter的區別?

  • Activity創建Fragment的方式是什麼?

    • 靜態創建具體步驟

      • 首先我們同樣需要註冊一個xml文件,然後創建與之對應的java文件,通過onCreatView()的返回方法進行關聯,最後我們需要在Activity中進行配置相關參數即在Activity的xml文件中放上fragment的位置。
    • 動態創建具體步驟

      • (1)創建待添加的碎片實例
      • (2)獲取FragmentManager,在活動中可以直接通過調用 getSupportFragmentManager()方法得到。
      • (3)開啓一個事務,通過調用beginTransaction()方法開啓。
      • (4)向容器內添加或替換碎片,一般使用repalce()方法實現,需要傳入容器的id和待添加的碎片實例。
      • (5)提交事務,調用commit()方法來完成。
  • FragmentPageAdapter和FragmentPageStateAdapter的區別?

    • FragmnetPageAdapter在每次切換頁面時,只是將Fragment進行分離,適合頁面較少的Fragment使用以保存一些內存,對系統內存不會多大影響
    • FragmentPageStateAdapter在每次切換頁面的時候,是將Fragment進行回收,適合頁面較多的Fragment使用,這樣就不會消耗更多的內存

1.0.3.0 fragment 特點?說一下Fragment的生命週期?如何解決getActivity爲null的異常問題?

  • fragment 特點

    • Fragment可以作爲Activity界面的一部分組成出現;
    • 可以在一個Activity中同時出現多個Fragment,並且一個Fragment也可以在多個Activity中使用;
    • 在Activity運行過程中,可以添加、移除或者替換Fragment;
    • Fragment可以響應自己的輸入事件,並且有自己的生命週期,它們的生命週期會受宿主Activity的生命週期影響。
  • Fragment從創建到銷燬整個生命週期中涉及到的方法依次爲:

    • onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->onPause()->onStop()->onDestroyView()->onDestroy()->onDetach(),其中和Activity有不少名稱相同作用相似的方法,而不同的方法有:

      • onAttach():當Fragment和Activity建立關聯時調用
      • onCreateView():當Fragment創建視圖時調用
      • onActivityCreated():當與Fragment相關聯的Activity完成onCreate()之後調用
      • onDestroyView():在Fragment中的佈局被移除時調用
      • onDetach():當Fragment和Activity解除關聯時調用
  • 如何解決getActivity爲null的異常問題技術博客大總結

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        activity = (PhoneNumActivity) context;
    }
    
    @Override
    public void onDetach() {
        super.onDetach();
        activity = null;
    }

1.0.3.1 在fragment中爲什麼有時getActivity()會爲null?Fragment試圖爲什麼有的時候會重疊,怎麼產生的,又如何解決?

  • getActivity()空指針:

    • 這種情況一般發生在在異步任務裏調用getActivity(),而Fragment已經onDetach(),此時就會有空指針,解決方案是在Fragment裏使用一個全局變量mActivity,在onAttach()方法裏賦值,這樣可能會引起內存泄漏,但是異步任務沒有停止的情況下本身就已經可能內存泄漏,相比直接crash,這種方式顯得更妥當一些。
  • Fragment視圖重疊:

    • 在類onCreate()的方法加載Fragment,並且沒有判斷saveInstanceState==null或if(findFragmentByTag(mFragmentTag) == null),導致重複加載了同一個Fragment導致重疊。(PS:replace情況下,如果沒有加入回退棧,則不判斷也不會造成重疊,但建議還是統一判斷下)
    @Override 
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    // 在頁面重啓時,Fragment會被保存恢復,而此時再加載Fragment會重複加載,導致重疊 ;
        if(saveInstanceState == null){
        // 或者 if(findFragmentByTag(mFragmentTag) == null)
           // 正常情況下去 加載根Fragment 
        } 
    }

1.0.3.2 爲什麼fragment傳遞數據不用構造方法傳遞?FragmentManager , add 和 replace 有什麼區別?

  • 爲什麼fragment傳遞數據不用構造方法傳遞?

    • activity給fragment傳遞數據一般不通過fragment的構造方法來傳遞,會通過setArguments來傳遞,因爲當橫豎屏會調用fragment的空參構造函數,數據丟失。
  • FragmentManager , add 和 replace 有什麼區別?

    • 使用FragmentTransaction的時候,它提供了這樣兩個方法,一個add,一個replace,add和replace影響的只是界面,而控制回退的,是事務。
    • add 是把一個fragment添加到一個容器container裏。replace是先remove掉相同id的所有fragment,然後在add當前的這個fragment。技術博客大總結
    • 在大部分情況下,這兩個的表現基本相同。因爲,一般,咱們會使用一個FrameLayout來當容器,而每個Fragment被add 或者 replace 到這個FrameLayout的時候,都是顯示在最上層的。所以你看到的界面都是一樣的。但是,使用add的情況下,這個FrameLayout其實有2層,多層肯定要比一層的來得浪費,所以還是推薦使用replace。當然有時候還是需要使用add的。比如要實現輪播圖的效果,每個輪播圖都是一個獨立的Fragment,而他的容器FrameLayout需要add多個Fragment,這樣他就可以根據提供的邏輯進行輪播了。而至於返回鍵的時候,這個跟事務有關,跟使用add還是replace沒有任何關係。
    • replace()方法會將被替換掉的那個Fragment徹底地移除掉,因此最好的解決方案就是使用hide()和show()方法來隱藏和顯示Fragment,這就不會讓Fragment的生命週期重走一遍了。

1.0.3.9 Activitiy啓動流程中performLaunchActivity的作用?Activity啓動流程中handleResumeActivity的作用?

  • Activitiy啓動流程中performLaunchActivity的作用?

    • 從ActivityClientRecord中獲取到待啓動的Activity的組件信息
    • 使用類加載器創建Activity對象
    • 通過LoadedApk的方法創建Applicayiton對象,該對象唯一,不會重複創建。
    • 會創建ContextImpl並且建立Context和Activity的聯繫,以及創建PhoneWindow,建立Window和Activity的聯繫。
    • 調用Activity的onCreate()
  • Activity啓動流程中handleResumeActivity的作用?

    • 執行onStart()、onResume()—利用Instrucmentation
    • 獲取Window
    • 創建DecorView、設置爲不可見INVISIBLE、建立DecorView和Activity的聯繫。
    • 獲取Activity的WindowManager
    • 調用WindowManager.addView(decorView, ...)將DecorView添加到WM中,完成顯示的工作。
    • image
  • 何時將DecorView設置爲VISIBLE?並且顯示出來?技術博客大總結

    • 也是在handleResumeActivity中
    • 現將DecorView設置爲不可見
    • wm.addView(): 將DecorView添加到Window總
    • 然後執行makeVisible讓DecorView可見
    • image

1.0.4.0 Intent是什麼?Intent可以傳遞哪些數據?傳遞對象的時候爲什麼要實例化?

  • Intent是一種運行時綁定(run-time binding)機制,它能在程序運行過程中連接兩個不同的組件。

    • 舉例:比如,有一個Activity希望打開網頁瀏覽器查看某一網頁的內容,那麼這個Activity只需要發出 WEB_SEARCH_ACTION給Android
    • Android就會根據Intent的請求內容,查詢各組件註冊時聲明的 IntentFilter,找到網頁瀏覽器的Activity來瀏覽網頁
  • Intent可以傳遞的數據基本數據類型的數據,數組,還有集合,還有序列化的對象

    • 序列化:表示將一個對象轉換成可存儲或可傳輸的狀態
    • Android中序列化對象方式:技術博客大總結

      • 第一種:JAVA中的Serialize機制,譯成串行化、序列化……,其作用是能將數據對象存入字節流當中,在需要時重新生成對象。主要應用是利用外部存儲設 備保存對象狀 態,以及通過網絡傳輸對象等。
      • 第二種:在Android系統中,定位爲針對內存受限的設備,因此對性能要求更高,另外系統中採用了新的IPC(進程間通信)機制,必然要求使用性能更出色的對象傳輸方式。

1.0.1.2 Activity如與Service通信?Service的生命週期與啓動方法由什麼區別?

可以通過bindService的方式,先在Activity裏實現一個ServiceConnection接口,並將該接口傳遞給bindService()方法,在ServiceConnection接口的onServiceConnected()方法
裏執行相關操作。

Service的生命週期與啓動方法由什麼區別?
    startService():開啓Service,調用者退出後Service仍然存在。
    bindService():開啓Service,調用者退出後Service也隨即退出。

Service生命週期:
    只是用startService()啓動服務:onCreate() -> onStartCommand() -> onDestory
    只是用bindService()綁定服務:onCreate() -> onBind() -> onUnBind() -> onDestory
    同時使用startService()啓動服務與bindService()綁定服務:onCreate() -> onStartCommnad() -> onBind() -> onUnBind() -> onDestory

1.1.0.4 廣播有哪些註冊方式?有什麼區別?廣播發送和接收原理是什麼[binder如何運作的]?

  • 廣播有哪些註冊方式?

    • 靜態註冊:常駐系統,不受組件生命週期影響,即便應用退出,廣播還是可以被接收,耗電、佔內存。
    • 動態註冊:非常駐,跟隨組件的生命變化,組件結束,廣播結束。在組件結束前,需要先移除廣播,否則容易造成內存泄漏。
  • 廣播發送和接收原理是什麼[binder如何運作的]?

    • 繼承BroadcastReceiver,重寫onReceive()方法。
    • 通過Binder機制向ActivityManagerService註冊廣播。
    • 通過Binder機制向ActivityMangerService發送廣播。
    • ActivityManagerService查找符合相應條件的廣播(IntentFilter/Permission)的BroadcastReceiver,將廣播發送到BroadcastReceiver所在的消息隊列中。
    • BroadcastReceiver所在消息隊列拿到此廣播後,回調它的onReceive()方法。

1.0.4.1 mipmap系列中xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi和ldpi存在怎樣的關係?

  • 表示不同密度的圖片資源,像素從高到低依次排序爲xxxhdpi>xxhdpi>xhdpi>hdpi>mdpi>ldpi,根據手機的dpi不同加載不同密度的圖片

1.0.4.2 res目錄和assets目錄的區別?

  • assets:不會在 R文件中生成相應標記,存放到這裏的資源在打包時會打包到程序安裝包中。(通過 AssetManager 類訪問這些文件)
  • res:會在 R 文件中生成 id標記,資源在打包時如果使用到則打包到安裝包中,未用到不會打入安裝包中。
  • res/anim:存放動畫資源
  • res/raw:和 asset下文件一樣,打包時直接打入程序安裝包中(會映射到 R文件中)

1.0.4.3 Context是什麼?Context有哪些類型,分別作用是什麼?Context下有哪些子類?哪些場景只能用activity上下文?

  • Context是什麼?

    • Context是一個抽象基類。在翻譯爲上下文,也可以理解爲環境,是提供一些程序的運行環境基礎信息。
  • Context有哪些類型,分別作用是什麼?

    • Context下有兩個子類,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現類。
    • ContextWrapper又有三個直接的子類,ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity,所以Activity和Service以及Application的Context是不一樣的,只有Activity需要主題,Service不需要主題。
  • Context下有哪些子類,主要是幹什麼的?

    • Context一共有三種類型,分別是Application、Activity和Service。
    • 這三個類雖然分別各種承擔着不同的作用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl類去實現的,因此在絕大多數場景下,Activity、Service和Application這三種類型的Context都是可以通用的。
    • 不過有幾種場景比較特殊,比如啓動Activity,還有彈出Dialog。出於安全原因的考慮,Android是不允許Activity或Dialog憑空出現的,一個Activity的啓動必須要建立在另一個Activity的基礎之上,也就是以此形成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是系統級別吐司),因此在這種場景下,我們只能使用Activity類型的Context,否則將會出錯。

1.0.4.4 ActivityThread的main()的流程大概是怎麼樣的?

  • ActivityThread的main()的流程大概是怎麼樣的?

    • image

1.0.5.0 序列化的方式有哪些?效率對比有何優勢?如何做性能上分析的?

  • 序列化的方式有哪些

    • Parcelable

      • Parcelable是Android特有的一個實現序列化的接口,在Parcel內部包裝了可序列化的數據,可以在Binder中自由傳輸。序列化的功能由writeToParcel方法來完成,最終通過Parcel的一系列write方法完成。反序列化功能由CREAOR來完成,其內部標明瞭如何創建序列化對象和數組,並通過Parcel的一系列read方法來完成反序列化的過程。
    • Serializable

      • Serializable是Java提供的一個序列化接口,是一個空接口,用於標示對象是否可以支持序列化,通過ObjectOutputStrean及ObjectInputStream實現序列化和反序列化的過程。注意可以爲需要序列化的對象設置一個serialVersionUID,在反序列化的時候系統會檢測文件中的serialVersionUID是否與當前類的值一致,如果不一致則說明類發生了修改,反序列化失敗。因此對於可能會修改的類最好指定serialVersionUID的值。

1.0.5.9 界面的刷新爲什麼需16.6ms?畫面的顯示需要哪些步驟?界面保持不變時還會16.6ms刷新一次屏幕嗎?

  • 界面的刷新爲什麼需16.6ms?

    • 系統每16.6ms會發出一個VSYNC信號,發出信號後,纔會開始進行測量、佈局和繪製。
    • 發出VSYNC信號時,還會將此時顯示器的buffer緩衝區的數據取出,並顯示在屏幕上。
  • 畫面的顯示需要哪些步驟?

    • CPU計算數據(View樹遍歷並執行三大流程:測量、佈局和繪製),然後將數據交給GPU“
    • GPU渲染處理,然後將數據放到Buffer中。
    • 顯示屏(display)從buffer中取出數據,並進行顯示。
  • 界面保持不變時還會16.6ms刷新一次屏幕嗎?技術博客大總結

    • 對於底層顯示器,每間隔16.6ms接收到VSYNC信號時,就會用buffer中數據進行一次顯示。所以一定會刷新。
  • 界面刷新的本質流程

    • 通過ViewRootImpl的scheduleTraversals()進行界面的三大流程。
    • 調用到scheduleTraversals()時不會立即執行,而是將該操作保存到待執行隊列中。並給底層的刷新信號註冊監聽。
    • 當VSYNC信號到來時,會從待執行隊列中取出對應的scheduleTraversals()操作,並將其加入到主線程的消息隊列中。
    • 主線程從消息隊列中取出並執行三大流程: onMeasure()-onLayout()-onDraw()

1.0.6.0 Android中日誌級別有哪幾種?開發中需要注意什麼問題,打印日誌源碼分析原理是什麼?

  • Android中日誌級別有哪幾種?

    • 1.Log.v 的輸出顏色爲黑色的,輸出大於或等於VERBOSE日誌級別的信息,也就是可見級別,一般是最低的信息提示
    • 2.Log.d的輸出顏色是藍色的,也就是調式級別,一般不會中止程序,一般是程序員爲了調試而打印的log
    • 3.Log.i的輸出爲綠色,輸出大於或等於INFO日誌級別的信息,也就是信息界級別,不會中止程序,一般是系統中執行操作的信息提示
    • 4.Log.w的輸出爲橙色, 輸出大於或等於WARN日誌級別的信息,也就是警告級別,一般不會中止程序,但是可能會影響程序執行結果
    • 5.Log.e的輸出爲紅色,僅輸出ERROR日誌級別的信息,也就是錯誤級別,一般會中止程序運行,是最嚴重的Log級別。
    • 解釋:

      • verbose
      • debug調試
      • info信息
      • warn警告
      • error誤差
  • 通過查看源代碼我們發現Log類中所有的靜態日誌方法Log.v(),Log.d(),Log.i(),Log.w(),Log.e()等方法都是底層都是調用了println方法,然後在源碼中查看,其實其內部調用的是println_native方法,也就是通過JNI調用底層的c++輸出日誌。

關於其他內容介紹

01.關於博客彙總鏈接

02.關於我的博客

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