在讲解生命周期的方法以前,先放上这张官方的图: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