Android四大組件之Activity

一、簡介


Activity的繼承樹
Activity的繼承樹

Activity是Android四大組件之一,對應(yīng)著應(yīng)用的一個(gè)個(gè)界面。Activity實(shí)現(xiàn)了Window.CallbackKeyEvent.Callback兩個(gè)接口,所以用戶通過屏幕點(diǎn)擊或點(diǎn)擊按鍵可以和Activity交互。

創(chuàng)建Activity就是繼承Activity創(chuàng)建子類,必須在清單文件注冊

二、使用


1. 創(chuàng)建Activity

/**
 * 繼承Activity,創(chuàng)建子類
 */
public class MainActivity extends Activity {

    /**
     * 生命周期方法:首次創(chuàng)建Activity時(shí)調(diào)用,可以做一些初始化工作。始終后接 onStart()
     * 必須重寫,需要通過setContentView()設(shè)置界面
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 設(shè)置布局
        setContentView(R.layout.activity_main);
        // The activity is being created.
    }
    
    /**
     * 生命周期方法:正在啟動(dòng)Activity,在 Activity 即將對用戶可見之前調(diào)用。如果 Activity
     * 轉(zhuǎn)入前臺,則后接 onResume(),如果 Activity轉(zhuǎn)入隱藏狀態(tài),則后接 onStop()
     */
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    
    /**
     * 生命周期方法:Activity已經(jīng)啟動(dòng),在 Activity即將開始與用戶進(jìn)行交互之前調(diào)用。始終后接 onPause()
     */
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    
    /**
     * 生命周期方法:正在停止,當(dāng)系統(tǒng)即將開始繼續(xù)另一個(gè) Activity 時(shí)調(diào)用。
     * 不能做耗時(shí)操作,因?yàn)楸仨毾葓?zhí)行完舊Activity的onPause(),才能執(zhí)行新Activity的onResume()。
     * 如果 Activity 返回前臺,則后接 onResume(),如果 Activity 轉(zhuǎn)入對用戶不可見狀態(tài),則后接 onStop()
     */
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    
    /**
     * 生命周期方法:即將停止,在 Activity 對用戶不再可見時(shí)調(diào)用。
     * 可以做一些重量級的回收操作,同樣不能太耗時(shí)。
     * 如果 Activity 恢復(fù)與用戶的交互,則后接 onRestart(),如果 Activity 被銷毀,則后接 onDestroy()。
     */
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    
    /**
     * 生命周期方法:即將銷毀,在 Activity 被銷毀前調(diào)用。做一些最終的資源釋放
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
    
    /**
     * 生命周期方法:正在重新啟動(dòng)Activity,在 Activity 已停止并即將再次啟動(dòng)前調(diào)用。始終后接 onStart()
     */
    @Override
    protected void onRestart() {
        super.onRestart();
    }
}

2. 編寫界面

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:text="Hello Activity"
        android:textSize="16sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
    
</android.support.constraint.ConstraintLayout>

3. 清單文件注冊

<application>
    ...
    <!--launchMode啟動(dòng)模式-->
    <activity android:name=".MainActivity"  android:launchMode="singleTask">
        <intent-filter>
            <!--指示這是主要入口點(diǎn),且不要求輸入任何 Intent 數(shù)據(jù)-->
            <action android:name="android.intent.action.MAIN" />
            <!--指示此 Activity 的圖標(biāo)應(yīng)放入系統(tǒng)的應(yīng)用啟動(dòng)器,未設(shè)置icon,則使用application標(biāo)簽下的icon-->
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    ...
</application>

4. 啟動(dòng)Activity

上面的Activity,因?yàn)樵O(shè)置了上面的filter,標(biāo)識了是程序的入口,安裝到手機(jī)上后,打開程序,就會為我們啟動(dòng)上面這個(gè)MainActivity。

重復(fù)上面步驟,創(chuàng)建SecondActivity,清單文件注冊。

 <activity android:name=".SecondActivity">
    <!--隱式啟動(dòng)時(shí)設(shè)置-->
    <intent-filter>
        <action android:name="acom.test.activity" />
        <!--為了能接受隱式啟動(dòng),必須添加,DEFAULT-->
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
  • 顯示啟動(dòng)
startActivity(new Intent(MainActivity.this, SecondActivity.class));
  • 隱式啟動(dòng)
// intent,設(shè)置action,action是自定義的字符串
Intent intent = new Intent("com.test.activity");
startActivity(intent);
  • 帶返回值的啟動(dòng)
// MainActivity啟動(dòng)SecondActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
// 方法不同
startActivityForResult(intent,1);
// SecondActivity返回?cái)?shù)據(jù)
Intent intent = new Intent(this,MainActivity.class);
intent.putExtra("test","返回的數(shù)據(jù)");
setResult(RESULT_OK,intent);
/**
 * 重寫Activity的該方法,接收返回?cái)?shù)據(jù)
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    // 判斷請求碼
    switch (requestCode){
        case 1:
            // 判斷返回碼
            if(resultCode == RESULT_OK){
                String test = data.getStringExtra("test");
                Log.e("MainActivity",test); // 將會輸出 “返回的數(shù)據(jù)”
            }
            break;
    }
}

startActivity()本質(zhì)也是調(diào)用startActivityForResult():

@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {
        // Note we want to go through this call for compatibility with
        // applications that may have overridden the method.
        startActivityForResult(intent, -1);
    }
}

三、詳解生命周期

1. 7個(gè)生命周期

Activity生命周期
Activity生命周期

小知識:

  1. onStart()和onStop()是從Activity是否可見的角度來回調(diào)的,onResume()和onPause()是從Activity是否位于前臺的角度回調(diào)的

2.Dialog、Popupwindow 和 Toast 不會對Activity的生命周期造成影響,可以仔細(xì)品讀上面onPause()生命周期的解讀,個(gè)人理解是Activity之間的相互切換才會導(dǎo)致生命周期方法的回調(diào)

2. 2個(gè)最常用的生命周期方法

  • onCreate()
    是必須實(shí)現(xiàn)的,用來初始化Activity,通過setContentView()來使用布局資源定義UI

  • onPause()
    用來在用戶離開時(shí)保存或提交數(shù)據(jù)

3. 3種生命周期階段

  • 完整生命周期:發(fā)生在 onCreate() 調(diào)用與 onDestroy() 調(diào)用之間。

  • 可見生命周期: 發(fā)生在 onStart() 調(diào)用與 onStop() 調(diào)用之間。在這段時(shí)間,用戶可以在屏幕上看到 Activity 。

  • 前臺生命周期: 發(fā)生在 onResume() 調(diào)用與 onPause() 調(diào)用之間。在這段時(shí)間,Activity 位于屏幕上的所有其他 Activity 之前,并具有用戶輸入焦點(diǎn)。

4. 3種存在狀態(tài)

  • 運(yùn)行
    onResume()執(zhí)行后,此 Activity 位于屏幕前臺并具有用戶焦點(diǎn)。

  • 暫停
    onPause()執(zhí)行后,另一個(gè) Activity 位于屏幕前臺并具有用戶焦點(diǎn),但此 Activity 仍可見。比如:被一個(gè)透明背景的Activity或一個(gè)dialog樣式的Activity覆蓋

  • 停止
    onStop()執(zhí)行后,該 Activity 被另一個(gè) Activity 完全遮蓋(該 Activity 目前位于“后臺”)。

小知識:

  1. 死亡: onDestory()執(zhí)行后 ,已經(jīng)不算存在狀態(tài)了
  1. 以上三種Activity,從上到下優(yōu)先級順序?yàn)?1 ,2,3,在系統(tǒng)內(nèi)存不足情況下,系統(tǒng)回收的優(yōu)先級為3,2,1
  2. 如果一個(gè)進(jìn)程沒有四大組件,那么它將很快被殺死。所以,一些后臺工作最好放在Service中進(jìn)行,保證進(jìn)程的優(yōu)先級,不會被輕易殺死

5. 切換時(shí)生命周期變換

Activity切換生命周期
Activity切換生命周期

6. 與Fragment之間生命周期變換

真正的生命周期順序,應(yīng)該在重寫的生命周期方法中的super()之前打印Log,才是真實(shí)的順序,如下:

四、Activity異常銷毀


1. 發(fā)生異常的情況

  • 資源相關(guān)系統(tǒng)配置發(fā)生改變導(dǎo)致Activity被殺死并重建
    重啟行為旨在通過利用與新設(shè)備配置匹配的備用資源自動(dòng)重新加載您的應(yīng)用,來幫助它適應(yīng)新配置。
  • 資源內(nèi)存不足導(dǎo)致優(yōu)先級低的Activity被殺死

2. 發(fā)生異常時(shí)的2個(gè)回調(diào)方法

  • onSaveInstanceState() 保存狀態(tài)
    在Activity異常終止的情況下,才會被調(diào)用,如旋轉(zhuǎn)屏幕配置發(fā)生變更,這個(gè)方法調(diào)用在onStop()之前

  • onRestoreInstanceState() 恢復(fù)狀態(tài)
    異常終止后被重新創(chuàng)建會被調(diào)用,調(diào)用在onStart()之后

系統(tǒng)會自動(dòng)幫我們做一些恢復(fù)工作,如Activity的視圖結(jié)構(gòu),其中的文本框輸入內(nèi)容等,因?yàn)閂iew都有以上兩個(gè)方法。
流程:委托思想
Activity異常 > Activity > window > DecorView > 各個(gè)子view,調(diào)用

3. 橫豎屏切換Activity的生命周期

上面提到,資源配置變更導(dǎo)致Activity重啟的原因是:通過利用與新設(shè)備配置匹配的備用資源自動(dòng)重新加載您的應(yīng)用,來幫助它適應(yīng)新配置。
那么,我們同樣可聲明 Activity 自行處理配置變更,這樣可以阻止系統(tǒng)重啟 Activity。如下:

<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">

測試環(huán)境:Android 5.0

1. 未設(shè)置android:configChanges
   橫豎屏切換都是,銷毀,重建;onPause > onStop > onDestory > onCreate > onStart > onResume
  
2. 設(shè)置android:configChanges="orientation"
   同上,銷毀,重建
    
3. 設(shè)置android:configChanges="orientation|keyboardHidden"
   同上,銷毀,重建
   
4. 設(shè)置android:configChanges="orientation|keyboardHidden|screenSize"
    或
   android:configChanges="orientation|screenSize"
   只回調(diào)onConfigurationChanged()方法

小知識:

  1. 只是想監(jiān)測到橫豎屏的變化只要使用orientation + screenSize兩個(gè)屬性就行了,keyboardHidden是監(jiān)測軟鍵盤變化的
  2. 因?yàn)?Android 3.2(API 級別 13)開始,當(dāng)設(shè)備在縱向和橫向之間切換時(shí),“屏幕尺寸”也會發(fā)生變化。若要避免由于設(shè)備方向改變而導(dǎo)致運(yùn)行時(shí)重啟,則除了 "orientation" 值以外,您還必須添加 "screenSize" 值
  3. 有個(gè)說法是切換成豎屏調(diào)用兩次生命周期的,這個(gè)網(wǎng)上有說是2.2版本是這樣,未親測

五、Activity啟動(dòng)模式


1. 4種啟動(dòng)模式

  • Standard 默認(rèn)模式

每啟動(dòng)一次Activity,就創(chuàng)建一個(gè)Activity實(shí)例

  • SingleTop 棧頂復(fù)用

任務(wù)棧頂有該Activity實(shí)例,回調(diào)onNewIntent(); 棧頂無實(shí)例,創(chuàng)建新實(shí)例

應(yīng)用場景:登錄頁面、接收推送的資訊詳情頁

  • SingleTask 棧內(nèi)復(fù)用

該Activity的taskAffity屬性指定的任務(wù)棧,如果不存在,則創(chuàng)建該任務(wù)棧,創(chuàng)建Activity實(shí)例;任務(wù)棧存在,如果棧內(nèi)有該Activity實(shí)例,回調(diào)onNewIntent(),并將任務(wù)棧中在其上面的Activity全部彈出棧; 棧內(nèi)無實(shí)例,創(chuàng)建新Activity實(shí)例

應(yīng)用場景:主頁、瀏覽器打開網(wǎng)頁的Activity(設(shè)置SingleTask,設(shè)置IntentFilter允許隱式啟動(dòng))

正常情況,點(diǎn)擊返回鍵都會返回上一個(gè)Activity,但存在這樣一個(gè)特殊情況:


SingleTask的特殊示例
SingleTask的特殊示例
  • SingleInstance 單一實(shí)例

SingleTask的加強(qiáng)版,如果存在該Activity實(shí)例,則回調(diào)onNewIntent();無實(shí)例,則創(chuàng)建新的任務(wù)棧和新的Activity實(shí)例,該實(shí)例是全局的,該任務(wù)棧中也將有且僅有這一個(gè)Activity實(shí)例,通過該Activity啟動(dòng)的其他Activity也將在其他單獨(dú)的任務(wù)棧中創(chuàng)建

應(yīng)用場景:來電提醒、鬧鐘提醒這種全局唯一的頁面,項(xiàng)目中還未用到過

復(fù)用Activity,回調(diào)onNewIntent()的情況:

A為SingleTask,B為Standard,啟動(dòng)順序:A > B > A ,生命周期切換如下
A : onCreate() > onStart() > onResume() > onPause()
B : onCreate() > onStart() > onResume()
A : onStop()
B : onPause()
A : onNewIntent() > onRestart() > onStart() > onResume()
B : onStop() > onDestroy()

2. 設(shè)置啟動(dòng)模式的2種方式

  • 清單文件

在清單文件中聲明 Activity 時(shí),您可以使用 <activity> 元素的 launchMode 屬性指定 Activity 應(yīng)該如何與任務(wù)關(guān)聯(lián)。

通過launchMode屬性,指定上述四種啟動(dòng)模式
  • Intent 標(biāo)志

啟動(dòng) Activity 時(shí),您可以通過在傳遞給 startActivity() 的 Intent 中加入相應(yīng)的標(biāo)志,
例如: intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

常用Flag:

* **FLAG_ACTIVITY_SINGLE_TOP**
    
        和SingleTop作用相同
    
* **FLAG_ACTIVITY_CLEAR_TOP**

        沒有該Activity實(shí)例的情況下,沒有意義,創(chuàng)建新實(shí)例;
        
        存在該Activity實(shí)例的情況下,和該啟動(dòng)模式有關(guān):
        Standard:銷毀任務(wù)棧中已存在的該Activity自身和它之上的所有Activity,重建實(shí)例
        SingleTop: 銷毀任務(wù)棧中其上的所有Activity,并回調(diào)onNewIntent()
        SingleTask:同上,無意義,和不設(shè)置一樣,回調(diào)onNewIntent(),就是SingTask模式
        SingleInstance: 同上,就是SingleInstance模式
        
* **FLAG_ACTIVITY_NEW_TASK**

        旨在找到該Activity屬性taskAffity所指定的任務(wù)棧,創(chuàng)建實(shí)例,默認(rèn)是和包名相同名稱的任務(wù)棧。
    
    **FLAG_ACTIVITY_NEW_TASK不等于 SingleTask**
        
        和要啟動(dòng)的Activity的啟動(dòng)模式有關(guān):
        
        Standard:
        不指定taskAffity,在包名任務(wù)棧,始終創(chuàng)建新的實(shí)例;
        指定與包名不同任務(wù)棧,不存在的話,創(chuàng)建任務(wù)棧和新的實(shí)例;存在后無反應(yīng),startActivity無效
        
        SingleTop:
        不指定taskAffity,在包名任務(wù)棧,始終創(chuàng)建新的實(shí)例;
        指定與包名不同任務(wù)棧,不存在的話,創(chuàng)建任務(wù)棧和新的實(shí)例;存在后無反應(yīng),startActivity無效
        
        SingleTask: 
        在taskAffity指定的任務(wù)棧中,銷毀其上的Activity,回調(diào)onNewIntnet(),不設(shè)置該Flag也一樣,就是Singletask效果
        
        SingleInstance:
        在taskAffity指定的任務(wù)棧中,SingleInstance效果,回調(diào)onNewIntent,不設(shè)置該Flag也一樣
     
     **FLAG_ACTIVITY_NEW_TASK + FLAG_ACTIVITY_CLEAR_TOP也不等于 SingleTask**
     
        和要啟動(dòng)的Activity的啟動(dòng)模式有關(guān):
     
        Standard:銷毀任務(wù)棧中已存在的該Activity自身和它之上的所有Activity,重建實(shí)例
        SingleTop:SingleTask效果,銷毀其上的Activity,回調(diào)onNewIntnet()
        SingleTask:singletask效果,銷毀其上的Activity,回調(diào)onNewIntnet(),不設(shè)置該Flag也一樣
        SingleInstance:SingleInstance效果,回調(diào)onNewIntent,不設(shè)置該Flag也一樣
        
以上,測試環(huán)境為Android P

3. taskAffity 和 allowTaskReparenting

taskAffinity表示與 Activity 有著親和關(guān)系的任務(wù)。也就是Activity應(yīng)該存在的任務(wù)棧。屬性取字符串值,該值必須不同于在 <manifest> 元素中聲明的默認(rèn)軟件包名稱,因?yàn)橄到y(tǒng)使用包名稱標(biāo)識應(yīng)用的默認(rèn)任務(wù)關(guān)聯(lián)。

在兩種情況下,關(guān)聯(lián)會起作用:

  • 啟動(dòng) Activity 的 Intent 包含 FLAG_ACTIVITY_NEW_TASK 標(biāo)志

將會在taskAffinity所指定的任務(wù)棧中創(chuàng)建Activity。
效果參考上述FLAG_ACTIVITY_NEW_TASK

  • allowTaskReparenting = true

該屬性含義是:是否允許 Activity 在與它有著親和關(guān)系的任務(wù)(taskAffity指定)到達(dá)前臺時(shí),轉(zhuǎn)移到那個(gè)任務(wù)。也就是Activity應(yīng)該存在的任務(wù)棧。

試驗(yàn)場景:
A應(yīng)用,B應(yīng)用,B應(yīng)用中的C Activity, C設(shè)置allowTaskReparenting = true

在A中打開C,startActivity 時(shí),設(shè)置FLAG_ACTIVITY_NEW_TASK,如果不設(shè)置沒有轉(zhuǎn)移任務(wù)的效果

結(jié)論:
如果B應(yīng)用任務(wù)棧存在,C直接在B中創(chuàng)建實(shí)例,位于棧頂 ;只要A任務(wù)切換到后臺,再回到前臺,都看不到C了,看到的是C之前的A應(yīng)用中自己的頁面;中間不管是點(diǎn)擊桌面B的laucher按鈕,還是從任務(wù)棧中將B切換到前臺,顯示的都是C

如果B應(yīng)用任務(wù)棧不存在,會創(chuàng)建C的taskAffity所指定的任務(wù)棧,默認(rèn)是B的包名;同樣A切換到后臺再回前臺,只能看到C之前的頁面; 啟動(dòng)B應(yīng)用時(shí),會直接顯示C頁面

六、Intent和IntentFilter


1. Intent的作用

Intent 是一個(gè)消息傳遞對象,您可以使用它從其他應(yīng)用組件請求操作。四大組件中有Activity、Service、BroadcastReceiver需要使用Intent來啟動(dòng)。

2. Intent的分類

  • 顯式Intent
    Activity的顯式啟動(dòng),就是使用顯式Intent;

    需要指定目標(biāo)組件名,也是類名;

    使用顯式Intent,系統(tǒng)將立即啟動(dòng) Intent 對象中指定的應(yīng)用組件

  • 隱式Intent
    Activity的隱式啟動(dòng),就是使用隱式Intent;

    不需要指定類名,需要制定Action,一個(gè)Intent只能有一個(gè)Action;

    使用隱式Intent,Android 系統(tǒng)通過將 Intent 的內(nèi)容與在設(shè)備上其他應(yīng)用的清單文件中聲明的 Intent 過濾器進(jìn)行比較,從而找到要啟動(dòng)的相應(yīng)組件,如果多個(gè) Intent 過濾器兼容,則系統(tǒng)會顯示一個(gè)對話框,支持用戶選取要使用的應(yīng)用。

    如果沒有找到對應(yīng)的組件,會崩潰,處理方式如下:

// 創(chuàng)建一個(gè)隱式Intent
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");

// 校驗(yàn)系統(tǒng)是否存在對應(yīng)的Activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}

3. IntentFilter是什么

Intent 過濾器是應(yīng)用清單文件中的一個(gè)表達(dá)式,它指定該組件要接收的 Intent 類型。

如果 Activity 聲明 Intent 過濾器,您可以使其他應(yīng)用能夠直接使用某一特定類型的 Intent 啟動(dòng) Activity。同樣,如果您沒有為 Activity 聲明任何 Intent 過濾器,則 Activity 只能通過顯式 Intent 啟動(dòng)。

// 我們最熟悉的過濾器
<intent-filter>
    <action android:name="android.intent.action.MAIN" />

    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

* ACTION_MAIN 操作指示這是主要入口點(diǎn),且不要求輸入任何 Intent 數(shù)據(jù)。
* CATEGORY_LAUNCHER 類別指示此 Activity 的圖標(biāo)應(yīng)放入系統(tǒng)的應(yīng)用啟動(dòng)器。 如果 <activity> 元素未使用 icon 指定圖標(biāo),則系統(tǒng)將使用 <application> 元素中的圖標(biāo)。

特別注意:
為了確保應(yīng)用的安全性,啟動(dòng) Service 時(shí),請始終使用顯式 Intent,且不要為服務(wù)聲明 Intent 過濾器。使用隱式 Intent 啟動(dòng)服務(wù)存在安全隱患,因?yàn)槟鸁o法確定哪些服務(wù)將響應(yīng) Intent,且用戶無法看到哪些服務(wù)已啟動(dòng)。從 Android 5.0(API 級別 21)開始,如果使用隱式 Intent 啟動(dòng)Service,系統(tǒng)會引發(fā)異常(Service Intent must be explicit)。

4. 匹配規(guī)則

  • action
<intent-filter>
    <action android:name="android.intent.action.EDIT" />
    <action android:name="com.example.action.a" />
    ...
</intent-filter>

action是一個(gè)字符串,系統(tǒng)預(yù)定了一些,我們也可自定義。

action匹配要求如果IntentFilter中有action,則Intent中必須存在action且必須和IntentFilter中的其中一個(gè)action匹配,另外action字符串區(qū)分大小寫。

  • category
<intent-filter>
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="com.example.category.a" />
    ...
</intent-filter>

category是一個(gè)字符串,系統(tǒng)預(yù)定了一些,我們也可自定義。

category匹配要求Intent中可以沒有category,但intent可以有多個(gè)category,所以如果一旦有category,則Intent的每個(gè)category都必須是IntentFilter中的一個(gè)。

另外startActivity和startActivityForResult都默認(rèn)為Intent增加一個(gè)intent.addCategory(Intent.CATEGORY_DEFAULT);,所以,為了我們的Activity能夠接受隱式調(diào)用時(shí),需要給我們的Activity 的IntentFilter增加這個(gè)Default category

  • data
// 合并寫法
<intent-filter>
    <data android:mimeType="video/mpeg" android:scheme="http" android:host="www.baidu.com" ... />
    ...
</intent-filter>
// 分開寫法
<intent-filter>
    <data android:mimeType="video/mpeg" />
    <data android:scheme="http"/> 
    <data android:host="www.baidu.com"/>
    ...
</intent-filter>

// 都一樣

data由MimeType和Uri構(gòu)成

  • Uri構(gòu)成:
    <scheme>://<host>:<port>/<path> 例如:http://www.baidu.com:80/a

    • scheme: Uri模式,如http,file,content等
    • host: 主機(jī)名
    • port: 端口號
    • path: 路徑

    線性依賴關(guān)系,scheme未指定,則后面其他參數(shù)無效,

  • MIME:
    媒體類型,由兩部分組成,前面是數(shù)據(jù)的大類別,例如聲音audio、圖象image等,后面定義具體的種類,用來表示不同的文本、圖片、視頻等類型。如:text/plain、image/jpeg

data匹配要求如果IntentFilter中有data,則Intent必須含有data,并且要完全匹配IntentFilter中的其中一項(xiàng)。

IntentFilter中Uri默認(rèn)為content或file,如果未指定Uri,Intent種的scheme部分必須為content或file,才能匹配。

Intent需要設(shè)置Uri和MimeType時(shí),要使用setDataAndType()方法,因?yàn)閱为?dú)調(diào)用setData()setType(),會互相清除數(shù)據(jù)。

七、任務(wù)和返回棧


任務(wù)是指在執(zhí)行特定作業(yè)時(shí)與用戶交互的一系列 Activity,啟動(dòng)Activity為根Activity,其它為子Activity。 Android系統(tǒng)中所有的 Activity 按照各自的打開順序排列在堆棧(即返回棧)中。 堆棧中的 Activity 永遠(yuǎn)不會重新排列,僅推入和彈出堆棧:由當(dāng)前 Activity 啟動(dòng)時(shí)推入堆棧;用戶使用“返回”按鈕退出時(shí)彈出堆棧。 因此,返回棧以“后進(jìn)先出”對象結(jié)構(gòu)運(yùn)行。

圖片.png-59.9kB
圖片.png-59.9kB

任務(wù)分為前臺任務(wù)(正在與用戶交互的)和后臺任務(wù),后臺可以同時(shí)運(yùn)行多個(gè)任務(wù)。但是,如果用戶同時(shí)運(yùn)行多個(gè)后臺任務(wù),則系統(tǒng)資源緊張時(shí)可能會開始銷毀后臺 Activity,以回收內(nèi)存資源,從而導(dǎo)致 Activity 狀態(tài)丟失

圖片.png-27.9kB
圖片.png-27.9kB

清理返回棧
如果用戶長時(shí)間離開任務(wù),則系統(tǒng)會清除所有 Activity 的任務(wù),根 Activity 除外。 當(dāng)用戶再次返回到任務(wù)時(shí),僅恢復(fù)根 Activity。系統(tǒng)這樣做的原因是,經(jīng)過很長一段時(shí)間后,用戶可能已經(jīng)放棄之前執(zhí)行的操作,返回到任務(wù)是要開始執(zhí)行新的操作。

您可以使用下列幾個(gè) Activity 屬性修改此行為:

  • alwaysRetainTaskState

如果在任務(wù)的根 Activity 中將此屬性設(shè)置為 "true",則不會發(fā)生剛才所述的默認(rèn)行為。即使在很長一段時(shí)間后,任務(wù)仍將所有 Activity 保留在其堆棧中。

  • clearTaskOnLaunch

如果在任務(wù)的根 Activity 中將此屬性設(shè)置為 "true",則每當(dāng)用戶離開任務(wù)然后返回時(shí),系統(tǒng)都會將堆棧清除到只剩下根 Activity。 換而言之,它與 alwaysRetainTaskState 正好相反。 即使只離開任務(wù)片刻時(shí)間,用戶也始終會返回到任務(wù)的初始狀態(tài)。

  • finishOnTaskLaunch

此屬性類似于 clearTaskOnLaunch,但它對單個(gè) Activity 起作用,而非整個(gè)任務(wù)。 此外,它還有可能會導(dǎo)致任何 Activity 停止,包括根 Activity。 設(shè)置為 "true" 時(shí),Activity 仍是任務(wù)的一部分,但是僅限于當(dāng)前會話。如果用戶離開然后返回任務(wù),則任務(wù)將不復(fù)存在。

八、Activity使用小技巧


1. 啟動(dòng)Activity的最佳寫法

A啟動(dòng)B,需要傳遞參數(shù),自己開發(fā)需要每次去看B需要接收哪些參數(shù);分工合作,別人開發(fā)B,你需要問他需要哪些參數(shù),參數(shù)代表什么含義。所以,我們可以在B中定義一個(gè)靜態(tài)方法,如下:

 /**
 * 啟動(dòng) B Activity
 * @param context 源組件
 * @param title 標(biāo)題
 * @param id id
 */
public static void actionStart(Context context,String title,String id){
    Intent intent = new Intent(context,SecondActivity.class);
    intent.putExtra("title",title);
    intent.putExtra("id",id);
    context.startActivity(intent);
}

在A中調(diào)用如下:

SecondActivity.actionStart(this,"啟動(dòng)Activity的最佳寫法","888");

2. 快速退出程序

詳情見百度搜索Activity管理類第一條,思路是:

創(chuàng)建一個(gè)集合,在BaseActivity的onCreate()方法中添加Activity,在onDestroy()中刪除Activity,退出程序時(shí),遍歷集合中所有Activity調(diào)用finish(),將該應(yīng)用的所有Activity銷毀

3. Activity的Dialog樣式

<activity android:name=".Activity.FirstActivity" 
          android:theme="@android:style/Theme.Dialog"/>

// 繼承AppCompatActivity時(shí)使用
<activity android:name=".Activity.FirstActivity"
          android:theme="@style/Theme.AppCompat.Dialog"/>

4. Activity轉(zhuǎn)場動(dòng)畫

通過重寫overridePendingTransition(int enterAnim, int exitAnim)實(shí)現(xiàn)的。

參數(shù):

  • enterAnim,將要打開的Activity的進(jìn)入動(dòng)畫id
  • exitAnim,將要退出的Activity的退出動(dòng)畫id

這個(gè)方法在startActivity(Intent)或finish()之后會被立即調(diào)用。

動(dòng)畫使用補(bǔ)間動(dòng)畫,在res/anim下創(chuàng)建動(dòng)畫文件,需要以 <alpha> <scale> <translate> <rotate> <set>為根節(jié)點(diǎn)。

下面我們以MainActivity打開SecondActivity示例:

left_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="2000"/>
    <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="2000" />
</set>

right_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="100%p" android:toXDelta="0" android:duration="2000"/>
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="2000" />
</set>

MainActivity使用

Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.right_in,R.anim.left_out);

left_in.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="-100%p" android:toXDelta="0" android:duration="2000"/>
    <alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="2000" />
</set>

right_out.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0" android:toXDelta="100%p" android:duration="2000"/>
    <alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="2000" />
</set>

SecondActivity使用

@Override
public void onBackPressed() {
    // 這里會調(diào)用finish
    super.onBackPressed();
    overridePendingTransition(R.anim.left_in,R.anim.right_out);
}

如上,即可實(shí)現(xiàn)在打開和退出SecondActivity時(shí)有動(dòng)畫效果,不過,要是每個(gè)Activity都這樣寫會很麻煩,所以,升級版如下:

value/styles.xml

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <!--這個(gè)屬性會對Activity切換動(dòng)畫有影響,如果動(dòng)畫失效,請檢查是否有該屬性-->
        <!--<item name="android:windowIsTranslucent">true</item>-->
        <!-- 設(shè)置activity切換動(dòng)畫 -->
        <item name="android:windowAnimationStyle">@style/ActivityAnim</item>
    </style>
    
    <!--設(shè)置Activity切換動(dòng)畫樣式-->
    <style name="ActivityAnim" parent="@android:style/Animation">
        <item name="android:activityOpenEnterAnimation">@anim/right_in</item>
        <item name="android:activityOpenExitAnimation">@anim/left_out</item>
        <item name="android:activityCloseEnterAnimation">@anim/left_in</item>
        <item name="android:activityCloseExitAnimation">@anim/right_out</item>
    </style>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myandroiddemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"> <!--應(yīng)用主題-->
        
</manifest>

以上,通過設(shè)置一個(gè)Activity切換樣式,然后設(shè)置給我們應(yīng)用的主題,最后應(yīng)用主題,這樣我們的應(yīng)用就會有Activity切換效果了,而不需要每個(gè)Activity都去調(diào)用overridePendingTransition方法了。


個(gè)人總結(jié),水平有限,如果有錯(cuò)誤,希望大家能給留言指正!如果對您有所幫助,可以幫忙點(diǎn)個(gè)贊!如果轉(zhuǎn)載,希望可以留言告知并在顯著位置保留草帽團(tuán)長的署名和標(biāo)明文章出處!最后,非常感謝您的閱讀!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容