Activity: 一、生命周期,启动模式,FLAG,Context


前言

博主整天就是敲业务代码, 太没劲了. 敲一天是敲, 敲十年也是敲, 敲来敲去还是那个水平, 所以博主决定回归基础, 重修再造 🤣
本篇介绍内容并不详细, 算是划重点吧 😇

在这里插入图片描述


那么, 就从Activity开始吧!

一、Activity生命周期

说道生命周期, 那就国际惯例了, 上图 (网上偷的…)

在这里插入图片描述
Activity的四个状态:

状态表象
running栈顶, 可见
poused可见, 但已失去焦点, 无法交互
stopped不可见
killed已销毁

二、Activity启动模式:

Activity有四种启动模式,standard、singleTop、singleTask、singleInstance,默认启动模式是standard

1.standard: (默认)

默认的启动模式, 每次 startActivity 的时候, 都会创建一个 Activity 实例, 压入栈顶;

2.singleTop: (栈顶复用)

当该模式的 Activity1 已经在栈顶时, 此时再次 start Activity1; 则复用实例, 不再新建;
此时生命周期: onPause -> onNewIntent -> onResume
其他情况, 则跟 standard(默认模式) 没有区别;

3.singleTask: (栈内复用)

简单理解模式:

  • start Activity 时, 如果栈内已存在此 Activity 的实例, 则复用; 复用时 会走onNewIntent()
  • 会清除该栈中 此Activity 上面的所有 Activity (出栈, destroy)

实际的启动流程:

  • 先查找合适的任务栈, 是具有相同亲和性的任务栈 (taskAffinity); 没有的话,要新建Task
  • 再看该栈中是否已经有该 Activity 的实例? 没有的话,新建实例,塞入栈顶即可
  • 如果有! 把它上面的 Activity 清空, 然后走生命周期: onStart..onNewIntent()..onResume

注意:

  • taskAffinity(亲和性): 默认同APP包名, 可以在清单文件中指定 android:taskAffinity="aaa"
  • singleTask 和 singleInstance 默认自带Flag: FLAG_ACTIVITY_NEW_TASK
  • 当启动页和目标页的任务栈一致时, 流程跟上面 '简单理解模式'一致
  • 否则, 当 singleTask 的活动回归前台时, 它的任务栈会整体回归前台。它上面的 act 被清空, 但下面的不会. 所以此时回退, 会先在此任务栈中回退, 任务栈退到底的时候, 才会回到 启动 singleTask 的页面;

4.singleInstance: (独立任务栈实例)

这种模式的 Activity 启动时, 会 单独启动一个任务栈. 该任务栈只存放这一个 Activity 实例;
再次启动时, 也是走 onNewIntent();

扩展:

  • 普通Activity启动: onPause -> onCreate -> onStart -> onResume -> onStop; 所以 onPause 尽量少做事情, 因为它会拖慢新页面的启动速度
  • 配置变化(例如屏幕旋转)导致页面重建时: onPause->onStop->onSaveInstanceState->onDestroy->onCreate->onRestoreInstanceState->onStart->onResume

三、Intent.addFlags

说起Flag, 那可就多了. Intent源码里, @interface Flags 总共配了四十多个. 这里只挑几个常用的讲一下, ps: 博主懒,不愿测 😂


FLAG_ACTIVITY_NEW_TASK

1.首先, 查找亲和任务栈; 没有的话就新建栈; 有的话就把栈移到前台;  
2.其次, 就是栈内操作了. 得保证 栈顶是被启动的这个页面; 至于是新建, 还是复用, 
 是否清除一些活动等, 还得看其他配合的属性;

发现没, 第一条的行为 跟 singleTask 模式一致; 并且 singleTask 默认就会带上 FLAG_ACTIVITY_NEW_TASK 所以博主大胆猜测, 第一条的行为就是 FLAG_ACTIVITY_NEW_TASK 独自带来的效果

注意点:

  • 当 taskAffinity 一致时, 并不会新建任务栈; 所以在 普通Activity中startActivity时, 单独带上 FLAG_ACTIVITY_NEW_TASK 并没有任何意义
  • 在 Service, Broadcast, Application 中启动 Activity 时, 必须带上 FLAG_ACTIVITY_NEW_TASK; 以保证, 任务栈不存在时, 可以执行 task 的新建. 当然不带就会抛异常.

FLAG_ACTIVITY_SINGLE_TOP

与 singleTop 启动模式一致;  是不是一回事还得去读源码; 

FLAG_ACTIVITY_CLEAR_TOP

以下为复用实例的情况, 新建的话就好办了. 创建实例并塞入栈顶即可.
1.销毁目标Activity以上(栈中)的所有Activity
2.如果是默认启动模式, 或者有 FLAG_ACTIVITY_SINGLE_TOP -> 目标Activity 会被销毁重建;
3.否则, 目标Activity 不销毁, 复用它.onNewIntent()

FLAG_ACTIVITY_CLEAR_TASK

简单理解: 清空Task, 新建实例,放进去. 该实例会在栈的底部, 作为根 Activity 
  • 本flag只能与 FLAG_ACTIVITY_NEW_TASK 配合使用
  • 配合使用? 那不就好办了. 那就先查找任务栈(亲和性) 😆
  • 没有的话, 新建任务栈, 新建实例. 由于是新任务栈, 第一个放进去的 Activity 肯定是栈底啊..
  • Task已存在? 那就清空它, 再新建实例. 复用? 一律新建, 不存在复用!

FLAG_ACTIVITY_REORDER_TO_FRONT

如果Task中已有目标实例, 则将它移动至栈顶复用;onNewIntent()
不会让其他任何 Activity 出栈;  单纯的复用.
如果已有 FLAG_ACTIVITY_CLEAR_TOP,本flag会被忽略

FLAG_ACTIVITY_FORWARD_RESULT

Activity 响应result;  startActivityForResult() + setResult()

假设: Activity  A -> B -> C
A: startActivityForResult(intent, 1)	// 不设置 Flag
B: startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT))	// 需要 Flag
C: setResult(RESULT_OK, ...)	// 设置 result 即可

注意: startActivityForResult(intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT))
这种写法会抛异常: FORWARD_RESULT_FLAG used while also requesting a result

Flag 太多了, 不同组合可能会有不同效果. 网上搜到的结论里, 有好多表述并不准确; 所以必须得亲自测试; 所以, 这个事.. 没有捷径!!


四、Activity, Application, Context:

怎么它们都是Context? 为什么有时候要用 ApplicationContext 防止内存泄漏 (比如 Toast)

1.Context

俗称上下文, 它是一个抽象类, 它描述的是一个应用程序环境的信息; 通过它,我们可以获取应用程序的资源和类,也包括一些应用级别操作;

里面方法众多; 例如: startActivity(), bindService(), sendBroadcast(), registerReceiver(), getSystemService(), getSharedPreferences()等, 还有 getText, getString, getDrawable 等;

2.ContextWrapper

我们看一下这个类源码:

public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;
	public ContextWrapper(Context base) { mBase = base; }
    ...
    @Override
    public void startActivity(Intent intent) { mBase.startActivity(intent); }
    @Override
	public void sendBroadcast(Intent intent) { mBase.sendBroadcast(intent); }
	@Override
    public Context getApplicationContext() {
        return mBase.getApplicationContext();
    }
    ...

这里用了 装饰模式; 而 Activity,Service,Application 最终都继承自 ContextWrapper;

// Application 
public class Application extends ContextWrapper implements ComponentCallbacks2 

// Service 
public abstract class Service extends ContextWrapper implements ComponentCallbacks2,

// Activity
public class Activity extends ContextThemeWrapper
public class ContextThemeWrapper extends ContextWrapper

Android 为每个 Activity, Service 都创建一个 Context实例, 就是 ContextWrapper 类中的 mBase; 而应用一般只有一个 Application 实例, 可以通过 getApplicationContext() 获取;

因此, Toast.makeText(this, “”, …).show() 时, Toast 会强引用 Activity实例; 此时如果页面关闭, Toast持续期间 Activity不会被回收. 造成内存泄漏. 因此Toast 应使用 getApplicationContext(); 并且任何与页面无关的操作, 都尽量使用 ApplicationContext

一般, 与 UI相关的操作, 使用 Activity, 而与系统资源相关的操作, 应使用 ApplicationContext


总结

没有总结

上一篇: Kotlin Coroutine(协程): 四、+ Retrofit
下一篇: Activity: 二、转场动画, RecycleView+ViewPager图片预览

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值