1.Activity生命周期
onCreate():是 activity 的创建时候调用,做一些初始化布局之类的操作
onStart():即将显示页面的时候调用。用户无法操作,也是初始化操作,
onresume():当前 Activity 处于栈顶,获取焦点可以和用户进行交互,处于运行状态
onPause():暂停状态。可能被其他的 activity
覆盖,仍然可见,但是失去焦点不能和用户进行交互
onstop():完全不可见的时候被调用。处于停止状态。内存不足这个 Activity
可能会被杀死,进行资源回收。
ondestroy():activity 被销毁的时候调用,进行资源释放。
onRestart():从不可见的时候变成可见。
成对出现:
onCreate 和 onDestroy:根据 activity 创建和销毁
onStart 和 onstop:根据 activity 是否可见,
onResume 和 onpause:根据 activity 是否显示在前台
(1)从A->B->A
A 到 B
A(oncreate() -> onStart()->onResume()) ——> A(onpause()) ——> B(oncreate() -> onStart()->
onResume())——> A(onstop())
返回 B
B (onpause()) ——> A(onRestart() -> onStart()->onResume()) ——> B(onstop()->onDestroy())
总结:页面可见性发生改变时候,当前页面先调用 onpause();
(2)横竖屏切换时activity的生命周期
1. AndroidManifest没有设置configChange属性的时候
启动:
oncreate -> onstart -> onResume
切换横竖屏->onPause -> onSaveInstaceState ->onstop->onDestrory
重新启动可见->oncreate -> onstart -> onReStoreInstanceState-> onResume
总结:
没有设置configChanges属性,Android6.0,7.0,8.0系统手机表现都是一样的,当前的界面调用onSavelnstanceState走一遍流程,然后重启调用onRestorelnstanceState再走一遍完整流程,最终destory.
2.AndroidManifest设置了configChanges,android:configChanges="orientation”竖屏,
Android 6.0
启动:
onCreate --> onStart --> onResume
切换横屏:
onPause -->onSavelnstanceState -->onStop -->onDestroy
-->onCreate-->onStart-->onRestorelnstanceState-->onResume
Android 7.0
onConfigurationChanged-->onPause -->onSavelnstanceState -->onStop -->onDestroy
-->onCreate-->onStart -->onRestorelnstanceState-->onResume
Android 8.0
onConfigurationChanged
总结: 设置了configChanges属性为orientation之后
Android 6.0 同没有设置configChanges情况相同,完整的走完了两个生命周期,调用了onSavelnstanceState和onRestorelnstanceState方法;
Android 7.0 则会先回调onConfigurationChanged方法,剩下的流程跟Android 6.0 保持一致;
Android 8.0 系统更是简单只是回调了onConfigurationChanged方法,并没有走Activity的生命周期方法。
2.Activity 的四个启动模式
FLAG_ACTIVITY_SINGLE_TOP:对应 singleTop 启动模式。
FLAG_ACTIVITY_NEW_TASK:对应 singleTask 模式。
FLAG_ACTIVITY_CLEAR_TOP:同一任务栈的处于此Activity之上的都会出栈,和singleTask一起出现,触发OnNewIntent();
标准模式
就是不管 activity 存不存在都 new 一个 Activity 出来放在当前任务栈的栈顶,
比如 ABCD 四个 Activity,D 处于栈顶,D 要通过 Intent 跳转到 A,则任务栈中就是 ABCDA。
比较常见的场景是:社交应用中,点击查看用户 A 信息->查看用户 A 粉丝->在粉丝中挑选查看用户 B 信息->查看用户
A 粉丝… 这种情况下一般我们需要保留用户操作 Activity 栈的页面所有执行顺序。
Singletop
如果要启动的 Activity 处于栈顶,则调用 onNewIntent()
比如 ABCD 四个 Activity,D 处于栈顶,D 要通过 Intent 要跳转到它本身的 D ,调用 onNewIntent()
比如 ABCD 四个 Activity,D 处于栈顶,D 要通过 Intent 要跳转到 B,新建一个 B 压入栈顶,任务栈中就是 ABCDB
举例:三条推送点进去是一个 Activity,SingTop App
用户收到几条好友请求的推送消息,需要用户点击推送通知进入到请求者个人信息页,将信息页设置为SingleTop
模式就可以增强复用性。
SingTask
根据 TaskAffinity 寻找对应的任务栈。
如果任务栈不存在,那就新建任务栈,新建 activity
实例。
如果任务栈存在:任务栈中不存在该 Activity 实例,就新建一个 Activity
压入栈
任务栈中存在该实例,将该实例顶部的实例出栈,并将自己置于栈顶
比如 ABCD,要从 D 通过 Intent 跳转到 B(走 onNewIntent),则弹出 CD 销毁,变成 AB
举例,首页肯定在栈底。
使用举例:浏览器首页,用户从多个应用启动浏览器首页,主页面仅仅启动一次,其余都走onNewIntent,并且清空主页面上的其他页面。
onNewIntent 的调用时机:singleTop、singleTask、singleInstance 模式下都会调用 onNewIntent()。
目的:复用Activity;
onCreate() 和 onNewIntent() 不会被同时调用。
调用 onNewIntent()生命周期如下:onNewIntent()->onRestart()->onStart()->onResume()。
onNewIntent 方法, 如下所示:
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
注意:没有在 onNewIntent()里面设置 setIntent()方法,将最新的 intent 设置给这个 activity 实例。那么在
onNewIntent()里面的 getIntent()得到的 intent 都是旧数据。
onNewIntent(Intent intent)方法就是提供给 singleTask 模式这种特定实现的有效保持 intent 上下文的方法;
单例模式
一个实例单独占一个任务栈,全局唯一性,如果使用时已经存在就将该任务栈调度到前台。
任务栈是 APP 管理 Activity 的一种容器 ,一般一个应用程序一个任务栈,任务栈管理该应用的 activity 进出栈
taskAffinity 属性能给 Activity 指定 task,但必须使用 FLAG_ACTIVITY_NEW_TASK 标记
默认的 taskAffinity 常用于独立栈:
用例:比如闹钟的提醒页面,点击之后进入闹钟详情,再返回原app不影响原来的app
3.Activity/Dialog/PopupWindow/Toast与WindowState:
* Activity/Dialog/PopupWindow/Toast在WMS都有对应的WindowState,
* 只是Activity/Dialog/PopupWindow的WindowState属于同一个AppWindowToken,也就是Activity的token,
* 而Toast的WindowState属于自己独有的WindowToken。
4. Android 程序中 Context 分成两种。一种是 Activity Context,另一种是 Application Context。
凡是跟 UI 相关的,都应该使⽤Activity 做为 Context 来处理
通过 Application Context 来启动 Activity 的话。就需要 FLAG_ACTIVITY_NEW_TASK 属性,不管这个 Activity
是属于其他程序还是自己这个程序的。
使用:
Intent intent = new Intent(new Intent(this, TestActivity.class));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); MyApplication.getContext().startActivity(intent);
如果我们用ApplicationContext 去启动一个 LaunchMode 为 standard 的 Activity 的时候会报错
android.util.AndroidRuntimeException: Calling startActivity from outside of an Activity
context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
因为⾮Activity类型的 Context 并没有所谓的任务栈,所以待启动的 Activity 就找不到栈了。
为待启动的 Activity 指定 FLAG_ACTIVITY_NEW_TASK 标记位,这样启动的时候就为它创建一个新的任务栈,而此时Activity 是以 singleTask 模式启动的。
在 Application 和 Service 中去 layout.inflate 也是合法的,但是会使用系统默认的主题样式,如果你自定义了某些样式可能不会被使用。所以这种方式也不推荐使用。
对于 startActivity 操作
①当为 Activity Context 则可直接使用
②当为其他 Context, 则必须带上 FLAG_ACTIVITY_NEW_TASK flags 才能使用,因为⾮ Activity context 启动Activity 没有 Activity 栈,则无法启动,因此需要加开启新的栈;
getApplication和getApplicationContext区别?
1.对于Activity/Service来说,getApplication()和getApplicationContext()的返回值完全相同; 除非厂商修改过接口;
2.BroadcastReceiver在onReceive的过程,能使用getBaseContext.getApplicationContext获取所在Application,而无法使用getApplication;
3.ContentProvider能使用getContext().getApplicationContext获取所在应用程序。绝大多数情况下没有问题,但是有可能会出现空指针的问题,
情况如下:
当同一个进程有多个apk的情况下,对于第二个apk是由provider方式拉起的,前面介绍过provider创建过程并不会初始化所在应用程序,此时执行返回的结果便是空。所以对于这种情况要做好判空。
5.activit相关问题
(1)onDestory()一定会执行吗?
正常情况下的返回 onDestory 一定会执行的, 后台强杀可能会发生:
当前仅有一个 Activity,这时候强杀,会执行,
当前很多 activity 实例,从 A 到 B 到 C,后台强杀只会 A 的 onDestroy,BC 都不会执行了。
(2)onStop()一定会执行吗?
如果要启动的是个透明的窗口,或者是对话框的样式,就不会执行。onstop 用于停止更新UI。
(3)怎么写一个Activity 的统一管理类:
⑴定义一个 ActivityManager 实现 Application.ActivityLifecycleCallbacks;
⑵ List<WeakReference> mActivityStack;
public class ActivityManager implements Application.ActivityLifecycleCallbacks {
public static final String TAG = "ActivityManager";
private static ActivityManager instance = new ActivityManager();
public static ActivityManager getActivityManager() {
return instance;
}
// 当前所有的activity
private static final List<WeakReference<Activity>> mActivityStack = new ArrayList<>();
// 获取所有的activity
public List<WeakReference<Activity>> getActivityStack() {
return mActivityStack;
}
public Activity getCurrentActivity() {
return mCurrentActivity.get();
}
/**
* 当前的activity,弱引用持有防止内存泄漏
*/
@SuppressLint("StaticFieldLeak")
private WeakReference<Activity> mCurrentActivity;
@Override
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
mCurrentActivity = new WeakReference<>(activity);
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
}
private void addActivity(@NonNull Activity activity) {
for (WeakReference<Activity> activityWeakReference : mActivityStack) {
if (activityWeakReference.get() == activity) {
return;
}
mActivityStack.add(new WeakReference<>(activity));
}
}
private void removeActivity(@NonNull Activity activity) {
Iterator<WeakReference<Activity>> iterator = mActivityStack.listIterator();
while (iterator.hasNext()) {
WeakReference<Activity> activityWeakReference = iterator.next();
if (activityWeakReference.get() == activity || activityWeakReference == null) {
iterator.remove();
}
}
}
}
6.Intent 可传递的数据类型有 3 种
1.java 的 8 种基本数据类型(boolean byte char short int long float double)、String 以及他们的数组形式;
2.Bundle 类,Bundle 是一个以键值对的形式存储可传输数据的容器;
3.实现了 Serializable 和 Parcelable 接口的对象,这些对象实现了序列化。
Intent 传输数据的大小有限制吗?如何解决?
答:Intent 中的 Bundle 是使用 Binder 机制进行数据传送的,数据会写到内核空间,
Binder 的缓冲区是有大小限制的,有些 ROM 是 1M, 有些 ROM 是 2M;
解决办法:
1.尽量减少传输数据量。
2.Intent 通过绑定一个 Bundle 来传输,这个可以超过 1M,不过也不能过大。
3.通过内存共享,使用静态变量或者使用 EventBus 等类似的通信工具。
4.通过文件共享。