Android基础之Activity
Activity导图
主要内容:
Activity 基本使用
- 创建活动
- AndroidManifest.xml 的 application 标签内进行声明
- 载入布局 setContentView(R.layout.second_layout)
Activity 跳转
显式 Intent
这种指定上下文和目标活动类名的 Intent 称之为显式 Intent。1
2
3//Intent(Context packageContext, Class<?> cls)
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);隐式 Intent
1
2
3
4
5
6
7
8<activity android:name=".SecondActivity">
<intent-filter>
<action android:name="com.rookieyang.demo.ACTION_START" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.rookieyang.demo.MY_CATEGORY" />
</intent-filter>
</activity>1
2
3Intent implieIntent = new Intent("com.rookieyang.demo.ACTION_START");
implieIntent.addCategory("com.rookieyang.demo.MY_CATEGORY");
startActivity(implieIntent);隐式 Intent 通过指定一系列更为抽象的 action 和 category 等信息,然后交由系统去分析这个 Intent,并帮我们找出合适的活动去启动。只有活动中的 <action> 和 <category> 标签完全匹配 Intent 中的 action 和 category 时,活动才能响应 Intent。
在调用 startActivity() 方法时会自动将 android.intent.category.DEFAULT 加入到 Intent 中,所以在给 Activity 指定 category 的时候不能够去掉默认的 android.intent.category.DEFAULT,去掉之后通过隐式 Intent 将无法匹配到该 Activity。
隐式 Intent 除了可以启动自己程序内的活动,还可以启动其他程序的活动:
1
2
3
4
5
6
7
8btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
}
});隐式 Intent 指定了一个系统内置的 action,通过 setData() 设置 Intent 操作的数据用以调用系统浏览器,其中 setData() 对应 <intent-filter> 标签内的 <data> 标签,用于指定当前活动能够响应什么类型的数据。<data> 标签中主要可以配置以下内容:
- android:scheme:用于指定数据的协议部分,如上例的 http 部分。
- android:host:用于指定数据的主机名部分,如上例的 www.baidu.com 部分。
- android:port:用于指定数据的端口部分,一般紧随主机名之后。
- android:path:用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。
- android:mimeType:用于指定可以处理的数据类型,允许使用通配符方式进行指定。
Activity 间数据传递
向下一个活动传递数据
MainActivity 中通过 Intent 的 putExtra() 方法将数据暂存在 Intent 中,然后在 SecondActivity 中通过getIntent() 获取到启动 SecondActivity 的 Intent,之后通过 getStringExtra() 方法获取到传递的数据。如果传递的是整型数据则使用 getIntExtra() 方法。1
2
3
4
5
6
7//MainActivity
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("data", "data");
startActivity(intent);
//SecondActivity
Intent intent = getIntent();
Toast.makeText(this, intent.getStringExtra("data"), Toast.LENGTH_LONG).show();返回数据给上一个活动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30//MainActivity
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivityForResult(intent, 1);
}
});
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1:
if (resultCode == RESULT_OK) {
Log.i("onActivityResult", data.getStringExtra("data"));
}
break;
default:
}
}
//SecondActivity
btn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("data", "data");
setResult(RESULT_OK, intent);
finish();
}
});- startActivityForResult(Intent intent, int requestCode):期望在活动销毁的时候能够返回一个结果给上一个活动。
- setResult(int resultCode, Intent data):专门用于向上一个活动返回数据,第一个参数用于向上一个活动返回处理结果,一般只使用 RESULT_OK 和 RESULT_CANCELED。第二个参数则是把带有数据的 Intent 传递回去,然后调用 finish() 方法来销毁当前活动。
- onActivityResult(int requestCode, int resultCode, Intent data):第一个参数是我们启动活动传入的请求码,第二个参数是返回数据传入的返回结果,第三个参数是返回的携带数据的 Intent,由于一个活动可能调用 startActivityForResult() 启动多个活动,所以首先需要检查 requestCode 来判断数据来源,然后在通过 resultCode 来判断处理结果是否成功。
Activity 的生命周期
- onCreate():活动第一次被创建的时候调用,一般在该方法中完成活动的初始化操作,比如加载布局、绑定事件等。
- onStart():在活动由不可见变为可见的时候调用。
- onResume():在活动准备好和用户进行交互的时候调用。此时活动一定位于栈顶,并且处于运行状态。
- onPause():在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗 CPU 的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
- onStop():在活动完全不可见的时候调用。如果启动的新活动是一个对话框式的活动,则
onPause()
会执行,而 onStop() 不会执行。 - onDestroy():在活动被销毁之前调用。
- onRestart():在活动由停止状态变为活动状态之前调用。 > 以上七种方法中除了 onRestart( ) 方法,其他都是两两相对的,从而可以分为三种生存期:onCreate( ) 和 onDestory( ) 为
完整生存期
、onStart( ) 和 onStop( ) 为可见生存期
、onResume( ) 和 onPause( ) 为前台生存期
。 > Activity 最多可能有四种状态,分别为:运行状态、暂停状态、停止状态、销毁状态。系统一般倾向于回收处于后两种状态的 Activity。
Activity 的数据保存
由于 Activity 处于停止状态的时候可能被系统回收,那么当用户返回被回收的 Activity 之时会面对一个空数据的 Activity。为了避免这种情况的发生,我们可以利用 onSaveInstanceState() 进行数据的保存工作:
1 | //保存 |
主动销毁 Acitivity 不会触发 onSaveInstanceState(),Bundle 也可以和 Intent 结合起来传递数据。
Activity 启动模式
Activity 有下列四种启动模式:
启动模式 | 作用 |
---|---|
standard | 每次启动活动都会创建一个活动的实例 |
singleTop | 在启动活动的时候如果发现返回栈的栈顶已经是该活动,则会直接使用它,不会再创建新的活动实例 |
singleTask | 在启动活动的时候如果发现返回栈中已经存在该活动,则直接使用它,并把该活动之上的所有活动统统出栈 |
singleInstance | 在启动活动的时候,启动一个单独的返回栈来管理该活动 |
- singleInstance 的意义:假设程序中有一个活动允许其他程序调用,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,就需要使用 singleInstance 模式,因为在这种模式下会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动,都共用同一个返回栈。
Intent 的 Flags 属性
Flags 的作用有很多,有的 Flags 为可以设定 Activity 的启动模式,还有的 Flags 可以影响 Activity 的运行状态,比如 FLAG_ACTIVITY_SINGLE_TOP 和 FLAG_ACTIVITY_CLEAR_TOP。 常用的 Flags 如下:
Flags | 作用 |
---|---|
FLAG_ACTIVITY_NEW_TASK | When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in. |
FLAG_ACTIVITY_CLEAR_TOP | 设置该标志之后的 Activity,当启动 Activity 时,如果该 Activity 已经存在任务栈中,则不在创建新的实例,并且位于该 Activity 之上的 Activity都需要出栈 一般配合 FLAG_ACTIVITY_NEW_TASK 进行使用 |
FLAG_ACTIVITY_SINGLE_TOP | 设置 Activity 的启动模式为 singleTop |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 具有该标志的 Activity 不会出现在历史 Activity 的列表中 |
FLAG_ACTIVITY_LAUNCH_ADJACENT | 此标志仅用于分屏多窗口模式,新的活动将显示在上一个 Activity 的附近 需要与 FLAG_ACTIVITY_NEW_TASK 结合使用。 另外,如果要创建现有活动的新实例,则应同时设置 FLAG_ACTIVITY_MULTIPLE_TASK |
FLAG_ACTIVITY_NEW_DOCUMENT | 该标志用于在一个以目标 Activity 为栈顶的新任务栈中打开文档 |
- FLAG_ACTIVITY_NEW_TASK:设置此状态,记住以下原则,首先会查找是否存在和被启动的 Activity 具有相同的亲和性的任务栈(即 taskAffinity,注意同一个应用程序中的 activity 的亲和性在没有修改的情况下是一样的),如果有,则直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的 activity 顺序不变,如果没有,则新建一个栈来存放被启动的 activity。
- FLAG_ACTIVITY_LAUNCH_ADJACENT:测试发现在分屏模式下在启动目标 Activity 时,如果没有设置 FLAG_ACTIVITY_MULTIPLE_TASK ,那么当目标 Activity 已经存在于任务栈中,将不会进入分屏模式。
- FLAG_ACTIVITY_NEW_DOCUMENT: 当单独使用时,它相当于指定
documentLaunchMode =“intoExisting”
。当与 FLAG_ACTIVITY_MULTIPLE_TASK 一起使用时,它等同于指定documentLaunchMode =“always”
。
序列化
目的
Intent 的 putExtra() 方法中所支持的数据类型是有限的,当需要传递一些自定义对象的时候,就需要用到序列化了。
方法
使用 Intent 来传递对象通常有两种实现方式:
Parcelable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30public class Person implements Parcelable {
...
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public static final Parcelable.Creator<Person> CREATOR = new Creator<Person>() {
public Person createFromParcel(Parcel source) {
Person person = new Person();
person.name = source.readString();
person.age = source.readInt();
return person;
}
public Person[] newArray(int size) {
return new Person[size];
}
};
}Person 类实现了 Parcelable 接口用于序列化,提供了一个名为 CREATOR 的常量用于反序列化。createFromParcel() 和 writeToParcel() 的顺序需要一致。采用 Parcelable 方式进行序列化,获取对象的方法变为 getParcelableExtra()。
Serializable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31public class Person implements Serializable {
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
//MainActivity 中
Person person = new Person();
person.setAge(13);
person.setName("data");
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("data_person", person);
startActivity(intent);
//SecondActivity 中
Person person = (Person) getIntent().getParcelableExtra("data_person");
Log.i("SecondActivity", "onCreate: " + person.getName());首先创建了一个 Person 类去实现 Serializable 接口,这样所有的 Person 对象就都是可序列化的了,然后在 MainActivity 中创建了一个 Person 对象,然后将对象传入 SecondActivity 中去,在 SecondActivity 中用 getParcelableExtra() 来获取对象。
实用技巧
知晓当前是在哪一个活动
> 创建一个 BaseActivity,然后让其他 Activity 继承 BaseAcitivty,那么当启动活动时就会 logcat 中打印出相应的信息。1
2
3
4
5
6
7
8public class BaseActivity extends AppCompatActivity {
protected void onCreate( { Bundle savedInstanceState)
super.onCreate(savedInstanceState);
Log.d("onCreate: ", getClass().getSimpleName());
}
}随时随地退出程序
> 创建一个活动的管理类,在活动的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class ActivityCollector {
private static List<Activity> activities = new ArrayList<>();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}onCreate()
中调用addActivity()
方法保存创建的活动,在onDestroy()
中调用removeActivity()
方法移除马上要销毁的活动。如果需要随时随地退出程序则只需要调用finishAll()
。启动活动的最佳写法
> 当活动中需要用到一些参数之时,可以采用如上方式去启动活动。那么当其他人来启动活动的时候将会非常方便。1
2
3
4
5
6public static void actionStart(Context context, String param1, String param2) {
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra("param1", param1);
intent.putExtra("param2", param2);
context.startActivity(intent);
}