股票场内基金交易,没时间盯盘?
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() 的代码
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 32 33 |
public Bundle saveHierarchyState() { // ... // 当前焦点 View focusedView = 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 的代码
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 32 33 34 |
public void restoreHierarchyState(Bundle savedInstanceState) { // ... // 恢复当前的焦点 int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID); if (focusedViewId != View.NO_ID) { View needsFocus = 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 重新创建的话,可以在清单文件里指定。
1 2 3 4 5 |
<!-- 不希望 Activity 在屏幕方向改变的时候重新创建 --> <activity android:name=".MainActivity" android:configChanges="orientation" /> |
启动模式
概览
一些总结的点
-
singleTask 只是让系统判定为可以单 task 启动,而不是直接开启新的 task 启动,需要加上 taskAffinity 属性,值不同于当前的 taskAffinity(一般默认的 taskAffinity 的值为包名)
-
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 路径前缀
如
1 2 3 4 5 |
<intent-filter> <data android:mimeType="image/*" /> <!-- more attrs --> </intent-filter> |
为了匹配这个规则,我们这样子写
1 2 |
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
1 2 3 4 5 |
/* * @see #MATCH_DEFAULT_ONLY */ public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags); |
- resolveActivity
1 2 3 4 5 |
/** * @see #GET_RESOLVED_FILTER */ public abstract ResolveInfo resolveActivity(Intent intent, int flags); |
我们看常量 MATCHDEFAULTONLY 的定义:
1 2 3 4 5 6 7 8 |
/** * 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() 方法一定会调用成功。
想获得去掉 5 元限制的证券账户吗?

如果您想去掉最低交易佣金 5 元限制,使用微信扫描左边小程序二维码,访问微信小程序「优财助手」,点击底部菜单「福利」,阅读文章「通过优财开证券账户无最低交易佣金 5 元限制」,按照文章步骤操作即可获得免 5 元证券账户,股票基金交易手续费率万 2.5。
请注意,一定要按照文章描述严格操作,如错误开户是无法获得免 5 元证券账户的。
“也就是说,需要保证不出错,建议将 flags 参数设置为
MATCH_DEFAULT_ONLY
, 以排除无法接收隐式 intent 的 activity ;”
建议不要把 MATCH_DEFAULT_ONLY 放在单独一行,阅读起来会有障碍,如果想表示强调可以加黑或者斜体