天天看點

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/yangchong211/YCBlogs
  • 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起于忽微,量變引起質變!所有的筆記将會更新到GitHub上,同時保持更新,歡迎同行提出或者push不同的看法或者筆記!

  • 在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"

  • 背景的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時
      • 螢幕方向切換時

  • 如何避免配置改變時Activity重建
    • 為了避免由于配置改變導緻Activity重建,可在AndroidManifest.xml中對應的Activity中設定android:configChanges="orientation|screenSize"。此時再次旋轉螢幕時,該Activity不會被系統殺死和重建,隻會調用onConfigurationChanged。是以,當配置程式需要響應配置改變,指定configChanges屬性,重寫onConfigurationChanged方法即可。
    • 使用場景,比如視訊播放器橫豎屏切換播放視訊,就需要設定這種屬性。具體可以看我封裝的視訊播放器庫,位址: https://github.com/yangchong211/YCVideoPlayer
  • 優先級低的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恢複到被摧毀之前的狀态。

  • 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元件,而頁面的生命周期方法也就是在這個方法中進行調用。

  • 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而言的。

  • 兩個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。
      • 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");
          }
      }           

  • 常見的标記為:
    • FLAG_ACTIVITY_SINGLE_TOP:對應singleTop啟動模式
    • FLAG_ACTIVITY_NEW_TASK :對應singleTask模式

  • 同一程式不同的Activity是否可以放在不同的Task任務棧中?
    • 可以的。比如:啟動模式裡有個Singleinstance,可以運作在另外的單獨的任務棧裡面。用這個模式啟動的activity,在記憶體中隻有一份,這樣就不會重複的開啟。
    • 也可以在激活一個新的activity時候,給intent設定flag,Intent的flag添加FLAG_ACTIVITY_NEW_TASK,這個被激活的activity就會在新的task棧裡面

  • 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裡發送廣播 在廣播裡再次開啟這個服務,雙程序守護

  • 關于service中onDestroy()什麼時候會被執行?
    • 當調用了startService()方法後,又去調用stopService()方法,這時服務中的onDestroy()方法就會執行,表示服務已經銷毀了。
    • 類似地,當調用了 bindService()方法後,又去調用unbindService()方法,onDestroy()方法也會執行,這兩種情況都很好了解。
  • 一個Activty先start一個Service後,再bind時會回調什麼方法?
  • 先start後bind操作service,此時如何做才能回調Service的destory()方法?
    • 完全有可能對一個服務既調用了startService()方法,又調用了bindService()方法的,這種情況下該如何才能讓服務銷毀掉呢?根據Android系統的機制,一個服務隻要被啟動或者被綁定了之後,就會一直處于運作狀态,必須要讓以上兩種條件同時不滿足,服務才能被銷毀。
    • 這種情況下要同時調用stopService()和unbindService()方法,onDestroy()方法才會執行這樣就把服務的生命周期完整地走了一遍。

  • 是否能在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();
              }
          });
      }           

  • Activity如何與Service通信?
    • 方法一:
      • 添加一個繼承Binder的内部類,并添加相應的邏輯方法。重寫Service的onBind方法,傳回我們剛剛定義的那個内部類執行個體。Activity中建立一個ServiceConnection的匿名内部類,并且重寫裡面的onServiceConnected方法和onServiceDisconnected方法,這兩個方法分别會在活動與服務成功綁定以及解除綁定的時候調用,在onServiceConnected方法中,我們可以得到一個剛才那個service的binder對象,通過對這個binder對象進行向下轉型,得到我們那個自定義的Binder執行個體,有了這個執行個體,做可以調用這個執行個體裡面的具體方法進行需要的操作了
    • 方法二
      • 通過BroadCast(廣播)的形式,當我們的進度發生變化的時候我們發送一條廣播,然後在Activity的注冊廣播接收器,接收到廣播之後更新視圖

  • ActivityManagerService是Android中最核心的服務,主要負責系統中四大元件的啟動、切換、排程及應用程序的管理和排程等工作,其職責與作業系統中的程序管理和排程子產品類似。
  • https://blog.csdn.net/dutedehuai/article/details/53495185

  • Android中哪些事件需要用到廣播?
    • Android中:系統在運作過程中,會産生會多事件,那麼某些事件産生時,比如:電量改變、收發短信、撥打電話、螢幕解鎖、開機,系統會發送廣播,隻要應用程式接收到這條廣播,就知道系統發生了相應的事件,進而執行相應的代碼。使用廣播接收者,就可以收聽廣播
  • 廣播的生命周期是怎樣的?
    • a.廣播接收者的生命周期非常短暫的,在接收到廣播的時候建立,onReceive()方法結束之後銷毀;
    • b.廣播接收者中不要做一些耗時的工作,否則會彈出 Application No Response錯誤對話框;
    • c.最好也不要在廣播接收者中建立子線程做耗時的工作,因為廣播接收者被銷毀後程序就成為了空程序,很容易被系統殺掉;
    • d.耗時的較長的工作最好放在服務中完成;

  • 廣播有幾種形式
    • 普通廣播:一種完全異步執行的廣播,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,是以它們接收的先後是随機的。
    • 有序廣播:一種同步執行的廣播,在廣播發出之後,同一時刻隻會有一個廣播接收器能夠收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播才會繼續傳遞,是以此時的廣播接收器是有先後順序的,且優先級(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是比對的,如果找到了,就把該廣播接收者的程序啟動起來。

  • 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的方式。

  • 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使用,這樣就不會消耗更多的記憶體

  • 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;
    }           

  • 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 
        } 
    }           

  • 為什麼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中,完成顯示的工作。
  • 何時将DecorView設定為VISIBLE?并且顯示出來?
    • 也是在handleResumeActivity中
    • 現将DecorView設定為不可見
    • wm.addView(): 将DecorView添加到Window總
    • 然後執行makeVisible讓DecorView可見

  • 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()方法。

  • 表示不同密度的圖檔資源,像素從高到低依次排序為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檔案中)

  • 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,否則将會出錯。

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

  • 序列化的方式有哪些
    • Parcelable
      • Parcelable是Android特有的一個實作序列化的接口,在Parcel内部包裝了可序列化的資料,可以在Binder中自由傳輸。序列化的功能由writeToParcel方法來完成,最終通過Parcel的一系列write方法完成。反序列化功能由CREAOR來完成,其内部标明了如何建立序列化對象和數組,并通過Parcel的一系列read方法來完成反序列化的過程。
    • Serializable
      • Serializable是Java提供的一個序列化接口,是一個空接口,用于标示對象是否可以支援序列化,通過ObjectOutputStrean及ObjectInputStream實作序列化和反序列化的過程。注意可以為需要序列化的對象設定一個serialVersionUID,在反序列化的時候系統會檢測檔案中的serialVersionUID是否與目前類的值一緻,如果不一緻則說明類發生了修改,反序列化失敗。是以對于可能會修改的類最好指定serialVersionUID的值。

  • 界面的重新整理為什麼需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()

  • 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.關于我的部落格

繼續閱讀