作為Android開發者,你真的熟悉Activity嗎?

世鋒日上發表於2016-08-04

學過android的人都知道,activity是最常用的四大元件之一,但你真的瞭解透徹activity了嗎?接下來,本人將從activity的正常和異常生命週期、啟動模式、IntentFilter匹配原則、activity的過渡動畫等方面做個總結。

一、 activity的生命週期

正常生命週期

1.正常開啟單個Activity,然後退出應用:

這種情況是最普通的狀況,Activity的生命週期會按照上圖從上到下的方式走。即:onCreate –> onStart –> onResume –> 執行–> 按返回鍵結束程式–> onPause–>onStop–>onDestory

2.開啟一個Activity A,然後再開啟另一個Activity B

對於A:onCreate –> onStart –> onResume –> A執行 –> A發出開啟B的Intent –> onPause–>B可見–>onStop
此時,會開啟B,B同樣會經歷一個完整的Activity生命週期。等B結束,A再度可見的時候,A會經歷:onRestart–>onStart–>onResume

注意:B這個Activity是在A的onPause執行後才變成可見狀態的,所以為了不影響B的顯示,最好不要在onPause裡執行一些耗時操作,可以考慮將這些操作放到onStop裡,這時B已經可見了。

異常情況生命週期

情況1.資源相關的系統配置發生改變

資源相關的系統配置發生改變,舉個例子。當前Activity處於豎屏狀態的時候突然轉成橫屏,系統配置發生了改變,Activity就會銷燬並且重建,其onPause, onStop, onDestory均會被呼叫。因為實在異常情況下終止的,所以系統會呼叫onSaveInstanceState來儲存當前Activity狀態。這個方法是在onStop之前,與onPause沒有固定的時序關係。當Activity重建的時候系統會把onSaveInstanceState所儲存的Bundle作為物件傳遞給onRestoreInstanceState和onCreate方法。

情況2:資源記憶體不足導致低優先順序Activity被殺死

Activity優先順序

前臺Activity——正在和使用者互動的Activity,優先順序最高
可見但非前臺Activity——Activity中彈出的對話方塊導致Activity可見但無法互動
後臺Activity——已經被暫停的Activity,優先順序最低
系統記憶體不足是,會按照以上順序殺死Activity,並通過onSaveInstanceState和onRestoreInstanceState這兩個方法來儲存和恢復資料。

二、activity的啟動模式

四種啟動模式分別是standard(標準模式)、singleTop(棧頂複用模式)、singleTask(棧內複用模式)、singleInstance(單例項模式 – 加強的singleTask模式)

standard

  • 系統預設啟動模式
  • 不論存在與否,都會重新建立一個新的例項
  • 多例項實現,誰啟動了這個Activity、那麼這個Activity就執行在啟動它那個Activity所在棧

singleTop

  • 判斷需要啟動的Activity是否為任務棧棧頂 ,如果是,則不會重新建立,如果不是,則會重新建立
  • 不重新建立時候,該Activity的 onNewIntent(Intent intent) 方法會被回撥,通過該方法的引數,可以取出當前請求的資訊;
  • 系統可能會殺死該Activity,殺死之後,啟動情況與第一次啟動相同,所以有必要在onCreate與onNewIntent方法中呼叫同一個處理資料的方法

    運用場景:常運用於通知欄彈出Notification,點選Notification跳轉到指定的Activity,設定singleTop模式

singleTask

  • 判斷Activity所需任務棧內是否已經存在,如果存在,就把該Activity切換到棧頂(會導致在它之上的都會出棧)
  • 如果所需任務棧都不存在,就會先建立任務棧再建立該Activity
  • 可以理解為 頂置Activity+singleTop 的模式

    運用場景:可用來退出整個應用。主介面activity設為singleTas模式,要退出應用時轉到主activity,從而將主activity之上的activity都清除,然後重寫主activity的onNewIntent()方法,在裡面加上finish(),即可退出所有activity。這種模式還適用於做瀏覽器、微博之類的應用

singleInstance

  • 擁有singleTask的所有特性之外,此模式Activity只能單獨地位於一個新的任務棧中
  • 也就是,Activity啟動之後,就會獨自在一個新的任務棧中,下次肯定不會重新建立該Activity,除非被系統殺死

    運用場景:這種模式常運用於需要與程式分離的介面,如在SetupWizard(安裝嚮導)中呼叫緊急呼叫就是適用這種模式

三、Intent和Intent-filter的匹配規則

1、Intent和Intent Filter的介紹

Intent是抽象的資料結構,包含了一系列描述某個操作的資料,使得程式在執行時可以在程式中不同元件間通訊或啟動不同的應用程式。

可以通過startActivity(Intent)啟動一個Activity,sendBroadcast(Intent))傳送廣播傳送給感興趣的BroadcastReceiver元件,startService(android.content.Intent))啟動service,bindService()繫結服務。

Intent Filter顧名思義就是Intent的過濾器,元件通過定義Intent Filter可以決定哪些隱式Intent可以和該元件進行通訊
Intent分為隱式(implicit)Intent和顯式(explicit)Intent。

顯式Intent通常用於在程式內部元件間通訊,已經明確的定義目標元件的資訊,所以不需要系統決策哪個目標元件處理Intent,如下

Intent intent =new Intent(CRListDemo.this, GoogleMapDemo.class);
startActivity(intent);

其中CRListDemo和GoogleMapDemo都是使用者自定義的元件,
隱式Intent不指明目標元件的class,只定義希望的Action及Data等相關資訊,由系統決定使用哪個目標元件。如傳送簡訊

2.Intent和Intent-filter的匹配規則

Android系統通過對Intent和目標元件在AndroidManifest檔案中定義的(也可以在程式中定義Intent Filter)進行匹配決定和哪個目標元件通訊。如果某元件未定義則只能通過顯式的Intent進行通訊。Intent的三個屬性用於對目標元件選取的決策,分別是Action、Data(Uri和Data Type)、Category。匹配規則如下

  • action匹配規則:要求intent中的action 存在 且 必須和過濾規則中的其中一個相同 區分大小寫;
  • category匹配規則:系統會預設加上一個android.intent.category.DEAFAULT,所以intent中可以不存在category,但如果存在就必須匹配其中一個;
  • data匹配規則:data由兩部分組成,mimeType和URI,要求和action相似。如果沒有指定URI,URI但預設值為content和file(schema)

3.利用Intent呼叫其他常見程式

a. 傳送簡訊

Uri uri = Uri.parse("smsto:15800000000");
Intent i=newIntent(Intent.ACTION_SENDTO, uri);
i.putExtra("sms_body", "The SMS text");
startActivity(i);

b. 打電話

Uri dial = Uri.parse("tel:15800000000");
Intent i=newIntent(Intent.ACTION_DIAL, dial);
startActivity(i);

c. 傳送郵件

Uri email = Uri.parse("mailto:abc@126.com;def@126.com");
Intent i=newIntent(Intent.ACTION_SENDTO, email);
startActivity(i);

d. 拍照

Intent i =newIntent(MediaStore.ACTION_IMAGE_CAPTURE);
String folderPath= Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "AndroidDemo" +File.separator;
String filePath= folderPath + System.currentTimeMillis() + ".jpg";newFile(folderPath).mkdirs();
File camerFile=newFile(filePath);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(camerFile));
startActivityForResult(i,1);

e. 瀏覽網頁

Uri web = Uri.parse("http://www.google.com");
Intent i=newIntent(Intent.ACTION_VIEW, web);
startActivity(i);

f. 檢視聯絡人

Intent i =newIntent();
i.setAction(Intent.ACTION_GET_CONTENT);
i.setType("vnd.android.cursor.item/phone");
startActivityForResult(i,1);

四、activity過渡動畫的五種實現

1.使用overridePendingTransition方法實現Activity跳轉動畫

overridePendingTransition方法是Activity中提供的Activity跳轉動畫方法,通過該方法可以實現Activity跳轉時的動畫效果,簡單例子如下:

Intent intent =newIntent(MainActivity.this, SecondActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_in_left);

注意:overridePendingTransition在startActivity或者是finish方法立刻執行才有效

2、使用style的方式定義Activity的切換動畫

(1)定義Application的style

<!-- 系統Application定義 -->
<application 
Android:allowBackup="true"
Android:icon="@mipmap/ic_launcher" 
Android:label="@string/app_name" 
Android:supportsRtl="true" 
Android:theme="@style/AppTheme">

(2)定義具體的AppTheme樣式

其中這裡的windowAnimationStyle就是我們定義Activity切換動畫的style。而@anim/slide_in_top就是我們定義的動畫檔案,也就是說通過為Appliation設定style,然後為windowAnimationStyle設定動畫檔案就可以全域性的為Activity的跳轉配置動畫效果。

<!-- Base application theme. --> 
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
 <!-- Customize your theme here. --> 
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> 
<item name="colorAccent">@color/colorAccent</item>
<item name="Android:windowAnimationStyle">@style/activityAnim</item>
</style><!-- 使用style方式定義activity切換動畫 -->
 <style name="activityAnim">
 <item name="Android:activityOpenEnterAnimation">@anim/slide_in_top</item>
 <item name="Android:activityOpenExitAnimation">@anim/slide_in_top</item> </style>

而在windowAnimationStyle中存在四種動畫:

  • activityOpenEnterAnimation
    用於設定開啟新的Activity並進入新的Activity展示的動畫
  • activityOpenExitAnimation
    用於設定開啟新的Activity並銷燬之前的Activity展示的動畫
  • activityCloseEnterAnimation
    用於設定關閉當前Activity進入上一個Activity展示的動畫
  • activityCloseExitAnimation
    用於設定關閉當前Activity時展示的動畫

3.使用ActivityOptions切換動畫實現Activity跳轉動畫

通過overridePendingTransition方法基本上可以滿足我們日常中對Activity跳轉動畫的需求了,但MD風格出來之後,overridePendingTransition這種老舊、生硬的方式怎麼能適合我們的MD風格的App呢?google在新的sdk中給我們提供了另外一種Activity的過度動畫——ActivityOptions。並且提供了相容包——ActivityOptionsCompat。ActivityOptionsCompat是一個靜態類,提供了相應的Activity跳轉動畫效果,通過其可以實現不少炫酷的動畫效果。

(1)在跳轉的Activity中設定contentFeature

@Override protected void onCreate(Bundle savedInstanceState) { 
super.onCreate(savedInstanceState); 
// 設定contentFeature,可使用切換動畫 
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); 
Transition explode = TransitionInflater.from(this).inflateTransition(Android.R.transition.explode);
getWindow().setEnterTransition(explode); 
setContentView(R.layout.activity_three); }

(2)在startActivity執行跳轉邏輯的時候呼叫startActivity的重寫方法,執行ActivityOptions.makeSceneTransitionAnimation方法

/** * 點選按鈕,實現Activity的跳轉操作 * 通過Android5.0及以上程式碼的方式實現activity的跳轉動畫 */
button3.setOnClickListener(new View.OnClickListener() { 
@Override public void onClick(View v) { 
Intent intent = new Intent(MainActivity.this, ThreeActivity.class); 
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle()); } });

activity跳轉動畫效果

(四)使用ActivityOptions之後內建的動畫效果通過style的方式

這種方式其實就是通過style的方式展示和使用ActivityOptions過度動畫,下面是實現通過定義style方式定義過度動畫的步驟:

(1)編寫過度動畫檔案

<explode xmlns:Android="http://schemas.Android.com/apk/res/Android" 
Android:duration="300" />

首先我們需要在Application專案res目錄下新建一個transition目錄,然後建立資原始檔,然後使用這些系統自帶的過渡動畫效果,這裡設定了過度時長為300ms。

(2)定義style檔案

<!-- Base application theme. --> 
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> 
<!-- Customize your theme here. --> 
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> 
<item name="colorAccent">@color/colorAccent</item>
<item name="Android:windowEnterTransition">@transition/activity_explode</item>
<item name="Android:windowExitTransition">@transition/activity_explode</item> 
</style>

在Application的style檔案中新增:

<item name="Android:windowEnterTransition">@transition/activity_explode</item>
<item name="Android:windowExitTransition">@transition/activity_explode</item>

並指定過渡動畫效果為我們剛剛定義的過渡動畫檔案。

(3)執行跳轉邏輯

點選按鈕,實現Activity的跳轉操作 * 通過Android5.0及以上style的方式實現activity的跳轉動畫

button4.setOnClickListener(new View.OnClickListener() 
{ @Override public void onClick(View v) {
 /** * 呼叫ActivityOptions.makeSceneTransitionAnimation實現過度動畫 */ 
Intent intent = new Intent(MainActivity.this, FourActivity.class); 
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(MainActivity.this).toBundle()); 
}
 });

這樣執行之後也可以展示出Activity跳轉過度動畫了,其和通過程式碼方式實現的效果是類似的,而且這種動畫效果是全域性的。

(五)使用ActivityOptions動畫共享元件的方式實現跳轉Activity動畫

這裡的共享元件動畫效果是指將前面一個Activity的某個子View與後面一個Activity的某個子View之間有過渡效果,即在這種過度效果下實現Activity的跳轉操作。那麼如何實現兩個元件View之間實現過渡效果呢?

(1)定義共享元件

在Activity a中的button按鈕點選transitionName屬性:

<Button Android:id="@+id/button5" 
Android:layout_width="match_parent" 
Android:layout_height="wrap_content" 
Android:layout_below="@+id/button4" 
Android:layout_marginTop="10dp" 
Android:layout_marginRight="10dp" 
Android:layout_marginLeft="10dp" Android:text="元件過度動畫" 
Android:background="@color/colorPrimary" 
Android:transitionName="shareNames" />

在Activity b的佈局檔案中為元件定義transitionName屬性,這樣這兩個元件相當於有了過度對應關係,這裡需要注意的是這兩個元件的transitionName屬性的值必須是相同的。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
xmlns:Android="http://schemas.Android.com/apk/res/Android" 
Android:id="@+id/activity_second" 
Android:layout_width="match_parent" 
Android:layout_height="match_parent" 
Android:gravity="center_horizontal" Android:orientation="vertical" 
Android:transitionName="shareNames" > <TextView 
Android:layout_width="match_parent" 
Android:layout_height="match_parent" 
Android:background="@color/colorAccent" 
Android:layout_marginTop="10dp" 
Android:layout_marginBottom="10dp" />
</LinearLayout>

(2)呼叫startActivity執行跳轉動畫

點選按鈕,實現Activity的跳轉操作 * 通過Android5.0及以上共享元件的方式實現activity的跳轉動畫

button5.setOnClickListener(new View.OnClickListener() { 
@Override public void onClick(View v) { 
Intent intent = new Intent(MainActivity.this, FiveActivity.class); 
startActivity(intent, 
ActivityOptions.makeSceneTransitionAnimation(MainActivity.this, button5, "shareNames").toBundle()); } 
});

需要說明的是這裡呼叫的ActivityOptions.makeSceneTransitionAnimation方法,傳遞了三個引數,其中第一個引數為context物件,第二個引數為啟動Activity的共享元件,第三個引數為啟動Activity的共享元件transitionName屬性值。

這樣經過呼叫之後我們就實現了從Activity a跳轉到Activity b的時候a中的元件到b中元件的過度效果。

過渡動畫總結

  • overridePendingTransition方法從Android2.0開始,基本上能夠覆蓋我們activity跳轉動畫的需求;
  • ActivityOptions API是在Android5.0開始的,可以實現一些炫酷的動畫效果,更加符合MD風格;ActivityOptions還可以實現兩個Activity元件之間的過度動畫;
    過渡動畫可參考github專案地址:實現activity跳轉動畫的五種方式

相關文章