在講解生命週期的方法以前,先放上這張官方的圖:html
這張圖片講述了 Activity 的回調的方法,下表分類講解這些方法的做用。java
生命週期方法 | 做用 |
---|---|
onCreate | 表示 Activity 正在被建立 |
onRestart | 表示 Activity 正在從新啓動 |
onStart | 表示 Activity 正在被啓動 |
onResume | 表示 Activity 已經可見 |
onPause | 表示 Activity 正在中止 |
onStop | 表示 Activity 即將中止 |
onDestroy | 表示 Activity 即將被銷燬 |
如下總結一下各類狀況下,生命週期中的回調狀況(表中的 A,B 表明的是兩個 Activity):android
狀況 | 回調 |
---|---|
第一次啓動 | onCreate() -> onStart() -> onResume() |
從 A 跳轉到不透明的 B | A_onPause() -> B_onCreate() -> B_onStart() -> B_onResume() -> A_onStop() |
從 A 跳轉到透明的 B | A_onPause() -> B_onCreate() -> B_onStart() -> B_onResume() |
從不透明的 B 再次回到 A | B_onPause() -> A_onRestart() -> A_onStart() -> A_onResume() -> B_onStop() |
從透明的 B 再次回到 A | B_onPause() -> A_onResume() -> B_onStop() -> B_onDestroy() |
用戶按 home 鍵 | onPause() -> onStop() |
按 home 鍵回後回到應用 | onRestart() -> onStart() -> onResume() |
用戶按 back 鍵回退 | onPause() -> onStop() -> onDestroy() |
這兩個方法只有在應用遇到意外狀況下才會觸發。能夠用於保存一些臨時性的數據。shell
onSaveInstanceState():瀏覽器
onRestoreInstanceState():bash
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
複製代碼
使用這個方法就能夠跳轉到 SecondActivity網絡
MainActivity.java:app
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);
複製代碼
這裏第二個參數是一個 requestCode,這個參數會在 onActivityResult 回調回來。ide
SecondActivity.java:ui
setResult(2);
finish();
複製代碼
當 SecondActivity finish 後會回調 MainActivity 中的 onActivityResult 方法:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.e("chan", "==================onActivityResult main= " + requestCode + " " + resultCode);
}
複製代碼
打印結果:
E/chan: ==================onActivityResult main= 1 2
複製代碼
固然你也能夠不調用 setResult() 方法,這時回調過來的 resultCode 就是 0。
顯示啓動分類:
Intent intent = new Intent(this, SecondActivity.class);
複製代碼
ComponentName componentName = new ComponentName(this, SecondActivity.class);
Intent intent = new Intent();
intent.setComponent(componentName);
複製代碼
Intent intent = new Intent();
intent.setClass(this, SecondActivity.class);
intent.setClassName(this, "com.example.administrator.myapplication.SecondActivity");
複製代碼
隱式啓動就是要在該 Activity 中設置 IntentFilter 屬性,只要啓用的 Intent 匹配 IntentFilter 的條件就能夠啓動相應的 Activity。
要理解隱式啓動就必需要理解 IntentFilter 是如何使用的
IntentFilter 有三個標籤分別是:
這三個標籤都有對應的匹配規則,下面會說到。這裏來講下使用 IntentFilter 要注意的地方
<category android:name="android.intent.category.DEFAULT"/>
複製代碼
不然就會出現以下錯誤:
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.chan1 }
複製代碼
action 的匹配規則就是隻要知足其中一個 action 就能夠啓動成功。
在 Manifest 定義一個 SecondActivity:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.chan" />
<action android:name="com.chan2" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="com.chan3" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
複製代碼
MainActivity:
Intent intent = new Intent();
intent.setAction("com.chan2");
startActivity(intent);
複製代碼
這樣就能夠啓動 SecondActivity,要注意的是 action 是區分大小寫的。
category 在代碼設置以下:
intent.addCategory("com.zede");
複製代碼
這句能夠添加也能夠不添加,由於代碼默認會爲咱們匹配 「android.intent. category.DEFAULT」。
data 主要是由 URI 和 mimeType 組成的。URI 的結構以下:
<scheme> :// <host> : <port> [<path>|<pathPrefix>|<pathPattern>]
複製代碼
這些值在 Manifest 文件中能夠定義,語法以下:
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string" />
複製代碼
如下用一個例子來講明:
Manifest:
<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="zede"
android:port="1010"
android:scheme="chan" />
</intent-filter>
</activity>
複製代碼
MainActivity:
Intent intent = new Intent();
intent.setData(Uri.parse("chan://zede:1010"));
startActivity(intent);
複製代碼
經過這個方法就能夠跳轉到 SecondActivity。
咱們也能夠建立一個 html 文件,來實現跳轉 SecondActivity。
test.html:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<a href="chan://zede:1010">跳轉至SecondActivity</a>
</body>
</html>
複製代碼
使用手機瀏覽器打開這個 html 文件,點擊這個超連接也能夠跳轉到 SecondActivity。
經過這個連接也能夠傳輸數據到 SecondActivity,代碼以下:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<a href="chan://zede:1010/mypath?user=admin&psd=123456">跳轉至SecondActivity</a>
</body>
</html>
複製代碼
在 SecondActivity 接收數據:
Intent intent = getIntent();
Uri uri = intent.getData();
Log.e("chan", "==================getScheme= " + intent.getScheme());
Log.e("chan", "==================getHost= " + uri.getHost());
Log.e("chan", "==================getPort= " + uri.getPort());
Log.e("chan", "==================getPath= " + uri.getPath());
Log.e("chan", "==================getQuery= " + uri.getQuery());
Set < String > names = uri.getQueryParameterNames();
Iterator < String > iterator = names.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
uri.getQueryParameter(key);
Log.e("chan", "==================getQueryParameter= " + uri.getQueryParameter(key));
}
複製代碼
打印結果:
07-19 10:47:54.969 19201-19201/com.example.administrator.myapplication E/chan: ==================getScheme= chan
07-19 10:47:54.970 19201-19201/com.example.administrator.myapplication E/chan: ==================getHost= zede
==================getPort= 1010
==================getPath= /mypath
==================getQuery= user=admin&psd=123456
==================getQueryParameter= admin
==================getQueryParameter= 123456
複製代碼
另外還須要注意另外一個屬性:android:mimeType,這個屬性就是說要傳遞什麼類型的數據,一般有 text/plain 或 image/jpeg。
能夠經過如下代碼來啓動 Activity:
intent.setType("text/plain");
複製代碼
不過須要注意的是,若是同時設置了 URI 和 mimeType 的話就必須使用以下代碼才能夠跳轉:
intent.setDataAndType(Uri.parse("chan://zede:1010"), "text/plain");
複製代碼
由於若是使用 setData() 或者 setType() 的話,分別會將相應 type 和 data 置爲 null。
啓動模式 | 做用 |
---|---|
standard | 每次啓動都會從新建立一個 Activity |
singleTop | 若是該棧頂上有所要啓動的 Activity,那麼就不會從新建立該 Activity,並會回調 onNewIntent() |
singleTask | 若是棧內已經有所要啓動的 Activity 就不會被建立,同時也會調用 onNewIntent() |
singleInstance | 建立該 Activity 系統會建立一個新的任務棧 |
這裏重點說下 singleTask。
singleTask 叫作棧內複用模式,這個啓動模式的啓動邏輯以下圖:
相信看了上面這個圖,你們也清楚 singleTask 的邏輯了,可是這個模式還有幾個須要注意的地方。
前面提到 A 想要的任務棧,那什麼是 A 想要的任務棧呢?這就提到一個屬性 taskAffinity,如下詳細介紹這個屬性。
標識一個 Activity 所須要的任務棧的名字。若是不設置這個屬性值,默認值是應用的包名。
若是啓動了設置了這兩個屬性的 Activity,這個 Activity 就會在 taskAffinity 設置的任務棧中,下面用代碼來驗證下:
建立 SecondActvitiy,在 Mnifest 文件設置 SecondActvitiy,代碼以下:
<activity android:name=".SecondActivity"
android:taskAffinity="com.chan"
android:launchMode="singleTask" />
複製代碼
如今使用 MainActivity 啓動 SecondActvitiy,這裏的代碼就不展現了,咱們直接看看結果,在終端輸入如下命令:
adb shell dumpsys activity activities | sed -En -e '/Running activities/,/Run #0/p'
複製代碼
這個命令能夠查看正在運行的 Activity,結果以下:
Running activities (most recent first):
TaskRecord{762a040 #63 A=com.chan U=0 StackId=1 sz=1}
Run #1: ActivityRecord{3881f68 u0 com.example.activitydemo/.SecondActivity t63}
TaskRecord{351eb79 #62 A=com.example.activitydemo U=0 StackId=1 sz=1}
複製代碼
從打印結果能夠看出, MainActivity 和 SecondActivity 運行在不一樣的任務棧中。
allowTaskReparenting 這個屬性直接解釋的話,可能不少人都會聽得懵逼,下面直接使用例子來解釋:
如今建立兩個應用,一個應用的包名爲:com.example.activitydemo,如下成爲應用 A。另外一個應用的包名爲:com.example.hellodemo,如下稱爲應用 B。
在應用 A 的 MainActivtiy 中添加以下代碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.start1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent();
intent.setClassName("com.example.hellodemo", "com.example.hellodemo.HelloActivity");
startActivity(intent);
}
});
}
複製代碼
應用 B 中建立 HelloActivity,並在 Manifest 文件中設置 HelloActivity 的 allowTaskReparenting 爲 true,代碼以下:
<activity android:name=".HelloActivity"
android:exported="true"
android:allowTaskReparenting="true" />
複製代碼
而後根據如下步驟操做:
完成第二步的時候,在終端看下任務棧的狀況:
Running activities (most recent first):
TaskRecord{5d54c1c #85 A=com.example.activitydemo U=0 StackId=1 sz=2}
Run #1: ActivityRecord{ff0b8e u0 com.example.hellodemo/.HelloActivity t85}
Run #0: ActivityRecord{95ee35c u0 com.example.activitydemo/.MainActivity t85}
複製代碼
能夠看出 HelloActivity 運行在應用 A 的任務棧中。
完成第四步後,再看下任務棧:
Running activities (most recent first):
TaskRecord{74c894d #86 A=com.example.hellodemo U=0 StackId=1 sz=2}
Run #1: ActivityRecord{ff0b8e u0 com.example.hellodemo/.HelloActivity t86}
TaskRecord{5d54c1c #85 A=com.example.activitydemo U=0 StackId=1 sz=1}
Run #0: ActivityRecord{95ee35c u0 com.example.activitydemo/.MainActivity t85}
複製代碼
從結果能夠看到,HelloActivity 從應用 A 的任務棧移動到應用 B 的任務棧。
如今再修改下 HelloActivity 的 taskAffinity 屬性,代碼以下:
<activity android:name=".HelloActivity"
android:exported="true"
android:allowTaskReparenting="true"
android:taskAffinity="com.chan"/>
複製代碼
從新根據以上步驟操做,操做完畢後看下任務棧信息:
Running activities (most recent first):
TaskRecord{50264fe #90 A=com.example.hellodemo U=0 StackId=1 sz=1}
Run #2: ActivityRecord{bc77713 u0 com.example.hellodemo/.MainActivity t90}
TaskRecord{41abf9e #89 A=com.example.activitydemo U=0 StackId=1 sz=2}
Run #1: ActivityRecord{2d0b7bb u0 com.example.hellodemo/.HelloActivity t89}
Run #0: ActivityRecord{8b57551 u0 com.example.activitydemo/.MainActivity t89}
複製代碼
能夠看出 HelloActivity 並無移動到應用 B 的主任務棧中,由於這並非 HelloActivity 想要的任務棧。
繼續修改 HelloActivity 配置屬性,增長 singleTask 屬性:
<activity android:name=".HelloActivity"
android:exported="true"
android:allowTaskReparenting="true"
android:taskAffinity="com.chan"
android:launchMode="singleTask"/>
複製代碼
繼續操做,任務棧結果以下:
Running activities (most recent first):
TaskRecord{775e709 #95 A=com.example.hellodemo U=0 StackId=1 sz=1}
Run #2: ActivityRecord{757bb47 u0 com.example.hellodemo/.MainActivity t95}
TaskRecord{aa75b2 #94 A=com.chan U=0 StackId=1 sz=1}
Run #1: ActivityRecord{76e2133 u0 com.example.hellodemo/.HelloActivity t94}
TaskRecord{21c8903 #93 A=com.example.activitydemo U=0 StackId=1 sz=1}
Run #0: ActivityRecord{be84df4 u0 com.example.activitydemo/.MainActivity t93}
複製代碼
能夠看出與沒有增長 singleTask 屬性的結果是同樣的,其實 allowTaskReparenting 這個屬性的最主要做用就是將這個 Activity 轉移到它所屬的任務棧中,例如一個短信應用收到一條帶網絡連接的短信,點擊連接會跳轉到瀏覽器中,這時候若是 allowTaskReparenting 設置爲 true 的話,打開瀏覽器應用就會直接顯示剛纔打開的網頁頁面,而打開短信應用後這個瀏覽器界面就會消失。
指定啓動模式的方式有兩種,一種是在 AndroidMenifest 文件設置 launchMode 屬性,另外一種就是在 Intent 當中設置標誌位。第二種方式的優先級會比第一種的要高,若是兩種都設置了會以第二種方式爲準。咱們來驗證一下:
在 MainActivity 設置以下代碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.start1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, SecondActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
}
複製代碼
SecondActivity 在 AndroidMenifest 設置以下:
<activity android:name=".SecondActivity"
android:taskAffinity="com.chan" />
複製代碼
這裏我並無設置 SecondActivity 爲 sigleTask,來驗證下啓動 SecondActivity 是否會開啓一個新的任務棧。
運行後,任務棧的結果爲:
Running activities (most recent first):
TaskRecord{148d7c5 #143 A=com.chan U=0 StackId=1 sz=1}
Run #2: ActivityRecord{de59b2d u0 com.example.activitydemo/.SecondActivity t143}
TaskRecord{520151a #142 A=com.example.activitydemo U=0 StackId=1 sz=1}
Run #1: ActivityRecord{d80bfc1 u0 com.example.activitydemo/.MainActivity t142}
複製代碼
從結果能夠看出,是開啓了一個新的任務棧的,也證實了第二種方式的優先級比較高
啓動 singleTask 的 Activity 的時候會回調 onNewIntent() 方法,可是並非全部狀況都這樣,總結以下圖:
如下使用代碼來驗證一下這四種狀況:
代碼以下:
MainActivity.java:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.start1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, SecondActivity.class));
}
});
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.e("chan", "MainActivity=======================onNewIntent");
}
}
複製代碼
SecondActivity.java:
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.start2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(SecondActivity.this, ThirdActivity.class));
}
});
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.e("chan", "SecondActivity=======================onNewIntent");
}
}
複製代碼
清單文件中將 SecondActivity 設置爲 singleTask,taskAffinity 屬性設置一個非該程序包名的值,代碼以下:
<activity android:name=".SecondActivity"
android:taskAffinity="com.onnewintent"
android:launchMode="singleTask" />
複製代碼
以上代碼的結果並無打印任何東西,證實這樣並不會回調 onNewIntent()。
這種狀況還要分兩種子狀況,一種就是 A 不在棧中,另外一種 A 不在棧中。
仍是用回上面的例子的代碼,添加一個 ThridActivity,ThridActivity 在清單文件描述以下:
<activity android:name=".ThirdActivity"
android:taskAffinity="com.onnewintent"
android:launchMode="singleTask" />
複製代碼
點擊 SecondActivity 跳轉按鈕後一樣也不會有任何打印,證實並不會回調 onNewIntent()。
這種狀況也會分兩種子狀況,一種就是 A 在棧頂,另外一種就是 A 不在棧頂。
一樣也是使用上面的例子,修改 ThirdActivity,代碼以下:
public class ThirdActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
findViewById(R.id.start3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(ThirdActivity.this, ThirdActivity.class));
}
});
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.e("chan", "ThirdActivity=======================onNewIntent");
}
}
複製代碼
到 ThirdActivity 時,ThirdActivity 已是在棧頂了,這時候點擊按鈕再次啓動 ThirdActivity,打印結果以下:
chan : ThirdActivity=======================onNewIntent
複製代碼
能夠發現這種狀況會回調 ThirdActivity 的 onNewIntent。
一樣改動 ThirdActivity,此次啓動的是 SecondActivity。具體代碼就不寫了,打印結果以下:
chan : SecondActivity=======================onNewIntent
複製代碼
能夠發現這種狀況會回調 SecondActivity 的 onNewIntent。
與啓動模式 singleTask 的做用同樣,必須設置 taskAffinity
與啓動模式 singleTop 的做用同樣
從名字就能夠看出這個標誌的做用就是若是這個 Activity 頂部有別的 Activity 的話,那麼就會它頂部的 Activity 所有出棧,若是這個 Activity 的啓動模式爲 standard 的話,就會將以前的 Activity 出棧,而且建立一個新的 Activity,若是不是的話就會調用 onNewIntent 方法。
不能在查看歷史 Activity 中查看到此 Activity