Android基础之Activity

Activity导图

主要内容:

Activity 基本使用

  1. 创建活动
  2. AndroidManifest.xml 的 application 标签内进行声明
  3. 载入布局 setContentView(R.layout.second_layout)

Activity 跳转

  1. 显式 Intent

    1
    2
    3
    //Intent(Context packageContext, Class<?> cls)
    Intent intent = new Intent(MainActivity.this, SecondActivity.class);
    startActivity(intent);
    这种指定上下文和目标活动类名的 Intent 称之为显式 Intent。

  2. 隐式 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
    3
    Intent 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
    8
    btn.setOnClickListener(new OnClickListener() {
    @Override
    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 间数据传递

  1. 向下一个活动传递数据

    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();
    MainActivity 中通过 Intent 的 putExtra() 方法将数据暂存在 Intent 中,然后在 SecondActivity 中通过getIntent() 获取到启动 SecondActivity 的 Intent,之后通过 getStringExtra() 方法获取到传递的数据。如果传递的是整型数据则使用 getIntExtra() 方法。

  2. 返回数据给上一个活动

    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() {
    @Override
    public void onClick(View v) {
    Intent intent = new Intent(MainActivity.this, SecondActivity.class);
    startActivityForResult(intent, 1);
    }
    });
    @Override
    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() {
    @Override
    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 的生命周期

  1. onCreate():活动第一次被创建的时候调用,一般在该方法中完成活动的初始化操作,比如加载布局、绑定事件等。
  2. onStart():在活动由不可见变为可见的时候调用。
  3. onResume():在活动准备好和用户进行交互的时候调用。此时活动一定位于栈顶,并且处于运行状态。
  4. onPause():在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗 CPU 的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的使用。
  5. onStop():在活动完全不可见的时候调用。如果启动的新活动是一个对话框式的活动,则 onPause() 会执行,而 onStop() 不会执行。
  6. onDestroy():在活动被销毁之前调用。
  7. onRestart():在活动由停止状态变为活动状态之前调用。 > 以上七种方法中除了 onRestart( ) 方法,其他都是两两相对的,从而可以分为三种生存期:onCreate( ) 和 onDestory( ) 为完整生存期、onStart( ) 和 onStop( ) 为可见生存期、onResume( ) 和 onPause( ) 为前台生存期。 > Activity 最多可能有四种状态,分别为:运行状态、暂停状态、停止状态、销毁状态。系统一般倾向于回收处于后两种状态的 Activity。

Activity 的数据保存

由于 Activity 处于停止状态的时候可能被系统回收,那么当用户返回被回收的 Activity 之时会面对一个空数据的 Activity。为了避免这种情况的发生,我们可以利用 onSaveInstanceState() 进行数据的保存工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 //保存
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("data", "outState");
Log.i(TAG, "onSaveInstanceState: ");
super.onSaveInstanceState(outState);
}
//恢复
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
if (savedInstanceState != null) {
Log.i(TAG, "onCreate: " + savedInstanceState.getString("data"));
}
}

主动销毁 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 来传递对象通常有两种实现方式:

  1. 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
    30
    public class Person implements Parcelable {

    ...

    @Override
    public int describeContents() {
    return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(name);
    dest.writeInt(age);
    }

    public static final Parcelable.Creator<Person> CREATOR = new Creator<Person>() {
    @Override
    public Person createFromParcel(Parcel source) {
    Person person = new Person();
    person.name = source.readString();
    person.age = source.readInt();
    return person;
    }

    @Override
    public Person[] newArray(int size) {
    return new Person[size];
    }
    };
    }

    Person 类实现了 Parcelable 接口用于序列化,提供了一个名为 CREATOR 的常量用于反序列化。createFromParcel() 和 writeToParcel() 的顺序需要一致。采用 Parcelable 方式进行序列化,获取对象的方法变为 getParcelableExtra()。

  2. 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
    31
    public 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() 来获取对象。

实用技巧

  1. 知晓当前是在哪一个活动

    1
    2
    3
    4
    5
    6
    7
    8
    public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d("onCreate: ", getClass().getSimpleName());
    }
    }
    > 创建一个 BaseActivity,然后让其他 Activity 继承 BaseAcitivty,那么当启动活动时就会 logcat 中打印出相应的信息。
  2. 随时随地退出程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public 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()
  3. 启动活动的最佳写法

    1
    2
    3
    4
    5
    6
    public 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);
    }
    > 当活动中需要用到一些参数之时,可以采用如上方式去启动活动。那么当其他人来启动活动的时候将会非常方便。