Android四大組件(一):Activity

Activity是Android應用中負責與用戶交互的組件,給Android應用提供可視化用戶界面。Activity是Window的容器,Activity包含一個getWindow()方法,返回該Activity所包含的窗口。

創建Activity

  • 需要在清單文件中為其配置一個activity標簽

  • 標簽中如果帶有這個子節點,則會在系統中多創建一個快捷圖標

       <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
    
  • 一個應用程序可以在桌面創建多個快捷圖標。

  • activity的名稱、圖標可以和應用程序的名稱、圖標不相同

      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name"
    

Activity的生命周期

Activity的四個狀態

Activity的四個狀態
  • 活動狀態:當前Activity位于前臺,用戶可見,可以獲得焦點進行交互。
  • 暫停狀態:其他Activity位于前臺,該Activity依然可見,只是不能獲得焦點進行交互,常見如彈出PopupWindow。
  • 停止狀態:該Activity不可見,失去焦點。
  • 銷毀狀態:該Activity結束,或Activity所在的Dalvik進程結束。

Activity生命周期及回調方法

Activity生命周期及相關的回調方法

  • onCreate(Bundle savedStatus):創建Activity時被回調;
  • onStart():啟動Activity時被回調,Activity已經顯示在屏幕,但沒有得到焦;
  • onRestart():重新啟動Activity時被回調,Activity從不可見變成可見時會執行此方法;
  • onResume():恢復Activity時被回調, Activity得到焦點,可以與用戶交互;
  • onPause():暫停Activity時被回調,Activity失去焦點,無法再與用戶交互,但依然可見;(可用于保護界面當前狀態)
  • onStop():停止Activity時被回調,Activity不可見,進入后臺;
  • onDestroy():銷毀Activity時被回調。

Activity實踐中的生命周期方法回調過程

  • 啟動Activity:onCreate->onStart->onResume;
  • 點擊Home鍵返回系統桌面:onPause->onStop;
  • 點擊應用列表的圖標重新進入應用:onRestart->onStart->onResume;
  • 點擊返回鍵(或程序調用finish()方法):onPause->onStop->onDestory;

Activity跳轉時的生命周期方法回調過程

Activity跳轉時生命周期

橫豎屏切換的生命周期
默認情況下 ,橫豎屏切換, 銷毀當前的activity,重新創建一個新的activity。在一些特殊的應用程序常見下,比如游戲,不希望橫豎屏切換activity被銷毀重新創建。

  • 默認情況(manifest清單文件中不對Activity的configChanges屬性做任何設置):
    1、android3.2之前的版本:onSaveInstanceState->onPause->onStop->onDestroy->onCreate->onStart->onResume;
    2、android3.2以后的版本:onPause->onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onResume;

  • 設置讓Activity對橫豎屏切換不敏感(清單文件中設置android:configChanges="orientation"):
    1、android3.2之前的版本:調用onConfigurationChanged(不會重建Activity,也不會調用任何生命周期方法)
    2、android3.2之后的版本,又分為兩種情況:
    ①targetSdkVersion<=12:調用onConfigurationChanged(不會重建Activity,也不會調用任何生命周期方法);
    ②targetSdkVersion>12:onPause->onSaveInstanceState->onStop->onDestroy->onCreate->onStart->onResume(配置的android:configChanges="orientation"沒起作用);
    ③如果targetSdkVersion>12時,想讓橫豎屏切換時不重建Activity,還得配置screenSize,也就是android:configChanges="orientation|screenSize"(因為google在android3.2中添加了screensize改變的通知,在轉屏的時候,不僅是orientation發生了改變,screensize同樣也發生了改變),這是最保險的做法。

  • 開發實踐中禁用掉橫豎屏重建Activity:
    1、直接將Activity的橫豎屏寫死(簡單、但是用戶體驗不友好,具體根據產品需求):

//①通過清單文件配置Activity
android:screenOrientation="landscape"http://始終橫屏
android:screenOrientation="portrait"http://始終豎屏

//②在代碼中實現:
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//始終橫屏
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //始終豎屏

2、通過清單文件配置Activity不再敏感橫豎屏切換(相對更友好安全的做法,參見上文這也是兼容性更好的做法,如果需要在橫豎屏切換時做業務邏輯處理,可以在onConfigurationChanged方法中實現):
在manifest.xml中配置activity:

android:configChanges="orientation|screenSize"

Activity的啟動模式

所有的Activity都將存放在任務棧中,通過設置Activity的啟動模式,可以修改任務棧的排列模式。我們可以在可以通過adb指令來查看系統的任務棧情況:adb shell dumpsys activity。 adb shell dumpsys activity activities | findstr "ResumedActivity"(這是用來查看當前活躍的棧頂Activity)。

Activity的四種啟動模式

  • Standard 標準啟動模式:Android創建Activity時的默認模式,假設沒有為Activity設置啟動模式的話,默覺得標準模式。每次啟動一個Activity都會又一次創建一個新的實例入棧,無論這個實例是否存在。
Standard啟動模式
  • SingleTop 棧頂復用模式:假設你在當前的Activity中又要啟動同類型的Activity,此時建議將此類型Activity的啟動模式指定為SingleTop,能夠降低Activity的創建,節省內存。
    ①如果需要創建的Activity已經不處于棧頂,將會創建一個新的Activity入棧,同Standard模式一樣。
SingleTop模式Activity位于棧頂

②如果須要創建的Activity已經處于棧頂時,此時不會再創建新的Activity,而是直接復用棧頂的Activity,保證棧頂如果存在,不會重復創建。此時Activity的onCreate、onStart不會被系統調用,由于它并沒有發生改變。但是它的的 onNewIntent會被回調。

SingleTop模式Activity不在棧頂
  • SingleTask 棧內復用模式: 當開啟activity的時候,就去檢查在任務棧里面是否有實例已經存在,如果有實例存在就復用這個已經存在的activity,并且把這個activity上面的所有的別的activity都清空讓此Activity位于棧頂,并且回調Activity的onNewIntent方法。保證整個任務棧里面只有一個實例存在。如果不存在同Standard模式一樣。(如果一個activity的創建需要占用大量的系統資源一般配置這個activity為singletask的啟動模式,一般應用的主界面也會配置為SingleTask模式) 。
SingleTask模式復用
  • SingleInstance啟動模式非常特殊,是全局單例模式,是一種加強的SingleTask模式。它除了具有SingleTask的所有特性外,還有一個特點,SingleInstance啟動的activity會運行在自己的任務棧里面,并且這個任務棧里面只有一個實例存在。一般應用開發很少用到,如果你要保證一個activity在整個手機操作系統里面只有一個實例存在,使用singleInstance。比如Launch、鎖屏鍵、電話撥打界面等。
    ①我們可以試著做一個SingleInstance啟動模式的小猜想:假設我們從MainActivity ->(跳轉到)SingleInstanceActivity ->(跳轉到) ->SecondActivity。 然后我們點擊返回按鈕,頁面的返回順序會是怎樣?如果你試了之后不理解其原理,下文有關于任務棧的詳細介紹。

任務棧的相關理解
在上文的啟動模式中我們多次提到任務棧這個概念,任務是一個activity的集合,它用棧(后進先出)的方式來管理activity;這個棧被稱為返回棧(back stack),棧里的activity順序是按打開順序放置。

①當用戶在home界面點擊應用圖標時候,這個應用的任務就會被轉移到前臺,如果這個應用的任務是空的,說明最近這個應用沒有被啟動過,系統就會去創建一個新的任務,將該應用的主activity放入到返回棧中。

②當一個activity啟動了一個新activity的時候,新的activity會被放置到返回棧的棧頂并獲取焦點(如果是SingleInstance啟動模式,新的Activity會新建一個棧并獨享);前一個activity仍然保留在任務棧,但處于停止狀態。

③當用戶按下返回鍵的時候,處于棧頂的activity會被移除掉,前一個activity就會重新回到棧頂的位置。我們只能向棧頂添加activity或者將棧頂的activity移除掉。

④如果一直按返回鍵,返回棧中的activity會一個一個的被移除,最終返回到主屏幕,這時候返回棧中activity全被清空,對應的任務也就不存在了。

  • 當打開一個應用,對應的任務處于前臺;這時候點擊home鍵回到主屏幕,任務就被轉移到后臺;當任務處于后臺狀態的時候,返回棧中的activity都進入停止狀態,但在返回棧中的順序不會變,每個activity的信息和數據都在;當處于內存不足的情況下有可能會被銷毀回收(涉及到進程優先級問題)。
通過SingleInstance啟動模式理解任務棧

啟動模式的使用方式
1、在Manifest.xml清單文件中靜態指定啟動模式:

        <activity
            android:name=".MainActivity"
            android:launchMode="singleTask"/>

2、在Intent中動態指定啟動模式去創建Activity:

        Intent intent = new Intent();
        intent.setClass(context, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
  • 兩種方式的區別:在Intent中動態指定的方式,優先級更高,若兩者同時存在,以動態指定方式為準;

Activity異常結束時保存狀態

Activity異常結束
Activity的異常結束是指,非人為主動結束和退出Activity的行為,一般有以下兩種情況:
1、資源系統設置變更:如橫豎屏切換,可能會導致Activity銷毀重建,或者設備語言發生變化,或者鍵盤發生變化時。我們可以在清單文件中配置Activity的configChanges屬性來避免(具體參照上文)。
2、系統資源不足時:由于虛擬機的垃圾回收機制,在系統內存不足時,會自動回收掉低優先級的進程,釋放內存保證優先級高的進程能正常運行(Android進程優先級相關知識)。

在Activity異常結束時保存和恢復數據

  • 保存數據:onSaveInstanceState(),只會在異常退出時才會回調。如果用戶顯式關閉Activity時(點擊返回,或者觸發Activity的finish方法),則系統不會回調此方法。調用的時機android3.2之前的版本:onSaveInstanceState在onPause之前回調,Android3.2之后在onPause之后回調。
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("name", "rave");
    }
  • 恢復數據:onRestoreInstanceState(),會在頁面異常關閉之后,重新進入時回調。調用時機是在onStart之后,onResume之前(沒有做過低版本測試)。
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        if(savedInstanceState != null){
            String name = savedInstanceState.getString("name");
            System.out.println(name);
        }
    }
  • 對于控件來說,在Activity異常結束時,部分系統控件會自動保存和恢復它的狀態(如EditView)。因為View類里面也有onSaveInstanceState()和onRestoreInstanceState(Parcelable state),如果要自定義控件實現該功能可以參考系統控件的做法。

Activity的之間的數據傳遞

當一個Activity啟動另一個Activity時,或者當一個Activity關閉并返回時,常常會有一些數據需要傳遞。Android的做法是將數據以鍵值對的方式存放在Bundle中,讓Intent攜帶Bundle在Activity之間完成數據傳遞。我們通過intent的putExtras()來添加數據,里面可以存放所以可序列化的類型(基本數據類型及其數組:byte、boolean、char、short、int、long、float、double、byte[]...;實現Parcelable接口的類;實現Serializable接口的類;還有這些類型的ArrayList集合類:ArrayList<? extends Parcelable>、ArrayList<Integer>、ArrayList<String>、ArrayList<CharSequence>)。???疑問這里Parcelable和Serializable有什么區別

  • Activity啟動另一個Activity傳遞數據:
Bundle bundle = new Bundle();
bundle.putBoolean("booleanValue", true);

Intent intent = new Intent();
intent.putExtras(bundle);

startActivity(intent);
//startActivityForResult(intent, 0);
  • 從Intent里面提取數據:
Bundle bundle = getIntent().getExtras();
boolean value = bundle.getBoolean("booleanValue");
  • Activity關閉并返回數據:
Bundle bundle = new Bundle();
bundle.putSerializable("booleanValue", true);

Intent intent = new Intent();
intent.putExtras(bundle);

setResult(1, intent);
finish();
  • 從返回的Activity里提取數據:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    Bundle bundle = data.getExtras();
}

Activity轉場動畫


使用Scheme方式喚起Activity

Android中啟動Activity主要有兩種方式,一種是通過顯示意圖啟動,一種是通過隱式意圖啟動。這里介紹的使用Scheme方式喚起,其實是隱式意圖啟動的一種,因為這種方式在hybird開發中很常用所以提出來單獨介紹。

android中的scheme是一種頁面內跳轉協議,是一種非常好的實現機制,通過定義自己的scheme協議,可以非常方便跳轉app中的各個頁面。客戶端應用可以通過清單文件向操作系統注冊一個 URL scheme,該 scheme 用于從瀏覽器或其他應用中啟動本應用。通過指定的 URL 字段,可以讓應用在被調起后直接打開某些特定頁面,比如商品詳情頁、活動詳情頁等等。也可以執行某些指定動作,如完成支付等。也可以在應用內通過 html 頁來直接調用顯示 app 內的某個頁面。

URL Scheme使用場景大致分以下幾種

  • 服務器下發跳轉路徑,客戶端根據服務器下發跳轉路徑跳轉相應的頁面
  • H5頁面點擊錨點,根據錨點具體跳轉路徑APP端跳轉具體的頁面
  • APP端收到服務器端下發的PUSH通知欄消息,根據消息的點擊跳轉路徑跳轉相關頁面
  • APP根據URL跳轉到另外一個APP指定頁面

URL scheme協議格式
例如:xl://goods:8888/goodsDetail?goodsId=10011002

  • xl代表該Scheme 協議名稱
  • goods代表Scheme作用于哪個地址域
  • goodsDetail代表Scheme指定的頁面
  • goodsId代表傳遞的參數
  • 8888代表該路徑的端口號

使用方式

  • 喚起外部應用的Activity:

1、必須在目標Activity的Manifest.xml中配置如下過濾器(關于Intent和Intent-filter會單獨說明):

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="artist"
        android:host="first"
        android:path="/enter"/>
</intent-filter>

2、啟動方式:

Intent intent = new Intent();
intent.setData(Uri.parse("artist://first/enter"));
startActivity(intent);
  • 喚起應用內部Activity(也可以用上面的方式喚起內部Activity),可以用下面的方式:

1、在目標Activity的Manifest.xml中配置如下過濾器

<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="artist"
        android:host="first"
        android:path="/enter"/>
</intent-filter>

2、啟動方式:

WebView.loadUrl("artist://first/enter");

相關源碼分析

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容