文章目录
前言
博主整天就是敲业务代码, 太没劲了. 敲一天是敲, 敲十年也是敲, 敲来敲去还是那个水平, 所以博主决定回归基础, 重修再造 🤣
本篇介绍内容并不详细, 算是划重点吧 😇
那么, 就从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图片预览