首页 > 代码库 > 安卓开发者必知的Activity知识点

安卓开发者必知的Activity知识点

Activity 是 Android 开发里最常见也是最常用,占比重最大的一个点,这篇文章给大家总结一些有用的 Activity 学习笔记

生命周期

正常情况下的生命周期

. onCreate()

表示 Activity 正在被创建,这是生命周期的第一个方法,一般在这个方法里做一些初始化的工作。

. onRestart()

表示 Activity 正在重新启动,从可见状态下变为不可见状态,这个方法会被调用,比如用户按 home 键切换到桌面,再切换回来,这个方法就会被调用。

. onStart()

回调这个方法的时候,Activity 已经可见,但是还和用户无法交互,这个方法和 onStop() 方法对应

. onResume()

回调这个方法的时候,Activity 已经可以和用户交互,这个方法和 onPause() 方法对应。

. onPause()

表示 Activity 正在停止,这个方法调用完毕之后,新的 Activity 的 onResume() 方法才会被调用。

所以不建议在这个方法里做耗时的操作,这个方法和 onResume() 方法对应。

. onStop()

表示 Activity 即将停止,同样不建议在这个方法里进行耗时的操作,这个方法和 onStart() 方法对应。

但是需要在 onPause() 和 onStop() 中选择的话,选择 onStop 进行操作,这是因为 onStop() 调用的时候,新的 Activity 已经出现在前台并且可以与用户交互了。

. onDestory()

表示 Activity 即将被销毁,这个方法里可以做一些资源回收和释放的操作,如解除绑定 Service,反注册 BroadCaseReceiver 等。

非正常情况下的生命周期

. onSaveInstanceState()

在系统配置发生改变之后,或者内存吃紧,系统回收 Activity 的时候,Activity 会调用这个方法,以保存当前 Activity 的状态。

从这个方法追踪源码到 PhoneWindow#saveHierarchyState() 方法中,可以看到 Activity 的默认实现是保存界面当前的焦点,以及调用了 View#onSaveInstanceState() 方法 来保存界面上所有 View 的状态。

贴一段 PhoneWindow#saveHierarchyState() 的代码

public BundlesaveHierarchyState() {

 

    // ...

    // 当前焦点

    ViewfocusedView = mContentParent.findFocus();

    if (focusedView != null) {

        if (focusedView.getId() != View.NO_ID) {

            outState.putInt(FOCUSED_ID_TAG, focusedView.getId());

        } else {

            if (false) {

  Log.d(TAG, "couldn’t save which view has focus because the focused view "

                        + focusedView + " has no id.");

            }

        }

    }

 

    // view 的状态保存

    SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();

    savePanelState(panelStates);

    if (panelStates.size() > 0) {

        outState.putSparseParcelableArray(PANELS_TAG, panelStates);

    }

 

    // ActionBar 的状态保存

    if (mDecorContentParent != null) {

    SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();

        mDecorContentParent.saveToolbarHierarchyState(actionBarStates);

        outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);

    }

 

    return outState;

}

 

. onCreate(Bundle savedInstanceState)

Activity 重新创建的时候,也会调用这个方法,正常状态下,savedInstanceState 为空,重新创建的时候,则包含之前保存的数据。

Activity 的默认实现与 onSaveInstanceState() 方法相对应,对之前保存的焦点和调用了 View 的 onRestoreInstanceState() 方法,对 View 的状态进行了恢复。

贴一段 PhoneWindow#onRestoreInstanceState 的代码

public void restoreHierarchyState(BundlesavedInstanceState) {

    // ...

  // 恢复当前的焦点

    int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);

    if (focusedViewId != View.NO_ID) {

        ViewneedsFocus = mContentParent.findViewById(focusedViewId);

        if (needsFocus != null) {

            needsFocus.requestFocus();

        } else {

            Log.w(TAG,

                    "Previously focused view reported id " + focusedViewId

                            + " during save, but can’t be found during restore.");

        }

    }

 

    // 恢复 View 的状态

  SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);

    if (panelStates != null) {

        restorePanelState(panelStates);

    }

    // 恢复 ActionBar 的状态

    if (mDecorContentParent != null) {

        SparseArray<Parcelable> actionBarStates =

                savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);

        if (actionBarStates != null) {

            doPendingInvalidatePanelMenu();

            mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);

        } else {

            Log.w(TAG, "Missing saved instance states for action bar views! " +

                    "State will not be restored.");

  }

    }

}

 

. onRestoreInstanceState()

在 Activity 重新创建的时候,会回调这个方法,将之前 onSaveInstanceState()方法保存的数据进行恢复,需要恢复数据,这个方法和 onCreate() 选用一个方法即可。

不同之处是如果 onSaveInstanceState() 方法如果被调用,那么代表 Activity 一定被重新创建了,参数中的 savedInstanceState 一定不为空,而在 onCreate() 则需要做出判断。

引起 Activity 重新创建的系统配置

android.content.res.Configuration 中的许多成员变量,都属于系统配置的 item, 比如屏幕方向,系统语言等。如果不想因为某项内容改变而引起 Activity 重新创建的话,可以在清单文件里指定。

<!-- 不希望 Activity 在屏幕方向改变的时候重新创建 --><activity

    android:name=".MainActivity"

    android:configChanges="orientation" />

 

启动模式

概览

一些总结的点

. 

singleTask只是让系统判定为“可以单task启动”,而不是直接开启新的task启动,需要加上taskAffinity属性,值不同于当前的taskAffinity(一般默认的taskAffinity的value为包名

taskAffinity属性设置为相同,可以让不同应用的Activity跑在同一个task中。

按下多任务键,就能看到task的数量。

同一个task中,按下返回键起弹栈的作用。

为一个activity的taskAffinity设置一个空字符串,表明这个activity不属于任何task

示例

下图是同一个Application中,存在两个task,按下多任务键的情况

IntentFilter

Intent 分为两种,显式 intent 和 隐式的 intent,显式 intent 指定了包名和类名,而隐式 intent 没有。

对于需要接受隐式 intent 的组件,我们需要指定过滤的规则,用 IntentFilter 对象来包裹过滤的信息。

IntentFilter 的过滤信息有 action, category, data

action 的匹配规则

一个过滤规则中可以含有多个 action,只要 intent 中的 action 的任意一个 和过滤规则中的 action 的任意一个 匹配, 即为匹配成功。

category 的匹配规则

一个过滤规则中可以含有多个 category,要求 intent 中的所有 category 和 过滤规则中的 部分/所有 category 匹配, 即为匹配成功。

data 的匹配规则

与 action 类似,如果在过滤规则中定义了 action,那么 Intent 中必须也要有匹配的 data。

data 包含有以下的数据

. mimeType 媒体类型

. URI

. Scheme URI 的模式,这里我理解为协议

. Host 主机名

. Port 端口号

. Path 完整的路径

. PathPattern 使用正则表达式匹配路径

. PathPrefix 路径前缀

<intent-filter>

    <dataandroid:mimeType="image/*" />

    <!-- moreattrs --></intent-filter>

 

为了匹配这个规则,我们这样子写

intent.serDataAndType(Uri.parse("file:url"), "image/png");

 

另外, setData() 方法和 setType() 方法是互斥的,设置了 uri 会把 mimeType 清除,反之亦然,要设置完整的 uri 和 mimeType,调用 setDataAndType(Uri data, String type) 方法.

确保有组件可以收到隐式 intent 避免出错

以 activity 为例, PackageManager 提供了以下方法, 这两个方法都返回 能够接收到这个隐式 intent 的 activity 集合或者信息。

其他的组件也有类似的方法。

. queryIntentActivities

/*

* @see #MATCH_DEFAULT_ONLY

*/public abstract List<ResolveInfo> queryIntentActivities(Intentintent, int flags);

 

. resolveActivity

/**

* @see #GET_RESOLVED_FILTER

*/public abstract ResolveInforesolveActivity(Intentintent, int flags);

 

我们看常量 MATCH_DEFAULT_ONLY 的定义:

/**

* Resolution and querying flag: if set, only filters that support the

* {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for

* matching.  This is a synonym for including the CATEGORY_DEFAULT in your

* supplied Intent.

*/public static final int MATCH_DEFAULT_ONLY  = 0x00010000;

 

也就是说,需要保证不出错,建议将 flags 参数设置为 MATCH_DEFAULT_ONLY , 以排除无法接收隐式 intent 的 activity (也就是 category 中不包含 android.intent.category.DEFAULT 的 activity ),这样子,如果这个方法能返回不为空的数据,则 startActivity() 方法一定会调用成功。

 

 

文章来自:技术学习小组

安卓开发者必知的Activity知识点