文章中绝大部分内容来源于付费专栏 重学安卓
1. 视图控制器
1. Activity角色
- 对系统而言,Activity本质上仍然是被管理的窗口,系统能管理窗口间的切换和通信.
- 对开发者而言,Activity本质上是视图控制器,控制View的排版,特殊情况下View的保存及恢复.
- Activity的存在是为了兼顾"多窗口管理"和"傻瓜式视图管理".
- Activity的要点在于 生命周期, Activity重建, 多窗口跳转, 视图加载及优化.
2. 进程优先级
- Android系统为不进程设置不同的优先级,以便在系统资源不足时杀死优先级较低的进程释放资源.
- 按照进程优先级从高到低,将进程分为5种
- 前景进程
- 1个APP有组件(如Activity)处于可交互状态,比如Activity进入onResume,则该APP属于前景进程
- 对Activity而言,进入onResume代表该APP属于前景进程.
- 可见进程
- 1个APP有组件(如Activity)处于可见但不可交互状态,比如Activity进入onStart,则该APP属于可见进程
- 比如PC上可以同时显示多个不同应用的窗口,但只有1个应用A当前可获取焦点,则A属于前景进程,其他显示的窗口所属应用属于可见进程
- 对Activity而言,进入onStart,onPause代表该APP属于可见进程
- 服务进程
- 背景进程
- 对Activity而言,进入onStop代表该APP属于背景进程
- 当APP属于背景进程,类似于PC上的窗口被最小化,随时可能被系统回收.
- 空白进程
- 系统为回收内存,将背景进程回收为空白进程.
- 被回收时,背景进程该保留的状态数据会保留,等再次变成前景进程,系统可以为APP恢复状态数据.
- 前景进程
- APP进程优先级,由其"活跃的或处于栈顶的组件"的状态(或生命周期)决定.
- 系统回收资源时,针对的是APP进程,而非APP进程中的某个组件/如Activity.
3. Activity的生命周期
- 为何在onCreate中执行setContentView和UI控件初始化,因为onCreate和onDestroy仅执行一次,其他生命周期函数会多次调用,为了避免反复的初始化.
- Activity A跳转到Activity B.生命周期函数调用顺序
- A.onPasuse -> B.onCreate -> B.onStart -> B.onResume -> A.onStop
- 不能在onPause中执行耗时逻辑,因为两个Activity生命周期是串行的,上一个Activity的onPause耗时,会导致下一个Activity过晚进入onResume,给用户的感觉就是反应迟钝.
- 短暂耗时逻辑可以在onStop中执行,但也不能过于耗时.因为onStop状态APP若属于背景进程,随时可能被系统回收,导致耗时逻辑无法执行完.
- 为了保证数据被及时保存,应该定时将数据从内存备份到磁盘,避免在onStop中未保存完APP就被系统回收.
- Activity处于不同生命周期,其APP进程优先级
- Activity处于onStart,onPause,APP属于可见进程.
- Activity处于onResume,APP属于前景进程
- Activity处于onStop,APP属于背景进程
- 前景进程的Activity也可能被回收
- 该情况是因为Activity所属的task被虚拟机回收
- 所以回收包括:
- 系统回收某进程
- 虚拟机回收某进程的某个task
- 上面2种回收机制针对的都不是某个Activity.
4. Activity的数据保存及恢复
- Activity重建产生的原因
- APP变成背景进程,进程被系统回收.再次打开应用,触发该Activity重建
- 系统配置发生变更(旋转屏幕,语言变更)
- Activity重建涉及的2个方法 : onSaveInstanceState(@NonNull Bundle outState) 及 onRestoreInstanceState(@NonNull Bundle savedInstanceState)
- onSaveInstanceState
- 从Android P开始, onSaveInstanceState 在 onStop之后执行
- 在Android P之前, onSaveInstanceState 在 onStop之前执行,但和onPause顺序不定
- onRestoreInstanceState
- 在 onStart 之后执行
- onSaveInstanceState
- Activity需要保存的数据有两类
- View的数据,原生View已实现了数据保存及恢复方法.第三方及开源控件不一定实现.
- Activity其他数据,需要在 onSaveInstanceState 及 onRestoreInstanceState 中手动保存恢复.
- Activity数据保存及恢复的步骤
- 用到的所有View都已经实现了数据保存及恢复的方法.目前原生的View均已实现.
- 布局中每个View都设置id
- 非View数据,要手动在 onSaveInstanceState 及 onRestoreInstanceState 中保存恢复.
- 将数据托管给Jetpack ViewModel,可直接实现数据的 暂存及恢复. 后续介绍.
5. Activity任务, 返回栈, 启动模式
-
任务和返回栈
- ActivityRecord
- 描述1个Activity的信息,对应着1个Activity界面
- TaskRecord/任务
- 1个栈结构,内部有多个ActivityRecord.
- 栈顶的ActivityRecord可能就是当前获得焦点的Activity
- ActivityStack/返回栈
- 1个栈结构,内部有多个TaskRecord
- 栈顶的TaskRecord'可能'就是当前获得焦点的任务
- ActivityStackSupervisior
- 管理多个ActivityStack.
- 同一时间只会有1个获得焦点的ActivityStack.
上述结构的图示:
- ActivityRecord
-
1个Activity所属的TaskRecord
-
Activity启动模式(TaskA:TA, ActivityA:AA)
- standard
- TA中的AA启动standard的AB. 会直接创建AB实例,放到TA的栈顶
- singleTop
- TA中的AA启动singleTop的AB.
- 若AB就在TA的栈顶,则不创建AB新实例,直接执行其onNewIntent
- 若AB不在TA的栈顶,则创建其实例放到TA的栈顶
- TA中的AA启动singleTop的AB.
- singleTask
- TA中的AA启动singleTask的AB.
- 若AB所属的T中已存在AB实例,则清空其上面的Activity,并执行其onNewIntent
- 若AB所属的T中不存在AB实例,则创建其实例,放到T的栈顶
- TA中的AA启动singleTask的AB.
- singleInstance
- 该Activity会独占1个T.
- 整个系统中只有1个该Activity单例,多个APP公用.
- standard
-
FLAG
- FLAG_ACTIVITY_NO_HISTORY
- 从该Activity跳转至其他Activity,则该Activity会从TaskRecord中被移除
- 例如该Activity启动AB,或者点击HOME,该Activity就会被销毁
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- 该Activity不会出现在'最近任务'列表中
- Flag和启动模式的区别
- 启动模式配置在manifest中,是固定的,而设置Flag是灵活的,可以多个flag随时组合.
- 例如 singleTask 是固定的、而 ClearTop 是可组合的:当你多数场景下不合适使用 SingleTask,但个别场景又需要它的这种清空栈中上方 Activity 的特性,那么此时你就选用 singleTop + clearTop FLAG 的组合,如此,在清单中你配置的是 singleTop,仅在你需要清空效果的场景下,在代码中动态附加 FLAG 来组合使用
- FLAG_ACTIVITY_NO_HISTORY
-
1个TaskRecord TT在哪个ActivityStack中创建,取决于该TaskRecord中的Activity A是被哪个Activity B所启动. 若B所属的TaskRecord T所属的ActivityStack是S. 则 TT 就会在 S 中创建.
-
allowTaskReparenting
- 用于activity的迁移,即可以从一个task迁移到另一个task
- 比如现在有两个应用A和B,A启动了B的一个ActivityC(其allowTaskReparenting的值为true),然后按Home键回到桌面,再单击B应用时,那么这个时候并不会启动B的主Activity,而是直接显示已被应用A启动的ActivityC,我们也可以认为ActivityC从A的任务栈转移到了B的任务栈中。
- 代码实例
APP1的manifest配置: <?xml version="1.0" encoding="utf-8"?> <manifest ***> <application *** //以下3个Activity都可以打开APP2中的一个设置了android:allowTaskReparenting="true"的Activity <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".JumpAllowTaskReparentingActivity"/> <activity android:name=".JumpToJumpAllowTaskReparentingActivity"/> </application> </manifest> APP2的manifest配置: <?xml version="1.0" encoding="utf-8"?> <manifest ***> <application *** <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> //该Activity就是可以从1个TaskRecord迁移到另一个TaskRecord的Activity <activity android:name=".AllowTaskReparentingActivity" android:exported="true" android:allowTaskReparenting="true" /> </application> </manifest> 日志: //通过APP1中的Activity(3个中任意1个)打开APP2中的AllowTaskReparentingActivity. D/AllowTaskReparentingActivity: AllowTaskReparentingActivity. onResume. AllowTaskReparentingActivity -- taskId:92 -- processId:27266 内存地址:com.example.app02.AllowTaskReparentingActivity@5bcbbc0 //点击HOME回到桌面 //点击APP2 D/AllowTaskReparentingActivity: AllowTaskReparentingActivity. onResume. AllowTaskReparentingActivity -- taskId:93 -- processId:27266 内存地址:com.example.app02.AllowTaskReparentingActivity@5bcbbc0 //点击回退 APP2从 AllowTaskReparentingActivity 回到了其启动页: MainActivity -
Activity在manifest中可以配置的几个属性 来源于知乎
- 系统默认行为
- 当一个APP被系统回收,长时间属于空白进程,会被系统的其他机制彻底销毁.其TaskRecord仅仅保留最底层的Activity,其他Activity全部清除.
- alwaysRetainTaskState
- 设置给TaskRecord中的根Activity才有效.
- TaskRecord中所有的Activity过了很久也不会被清除.
- clearTaskOnLaunch
- 设置给TaskRecord中的根Activity才有效.
- 只要用户离开了该任务,再次回到该任务,只有根Activity被保留,其余全部被清除
- excludeFromRecents
- 设置给TaskRecord中的根Activity才有效.
- 该根Activity所属的TaskRecord不会在Recent中显示.
- autoRemoveFromRecents
- 设置给TaskRecord中的根Activity才有效.
- 当该TaskRecord中的Activity都被移除(比如一直BACK),该TaskRecord自动从Recent中移除
- finishOnTaskLaunch
- 和clearTaskOnLaunch类似,但是其仅针对设置了该属性的Activity生效.再次回到该任务,对应Activity就被清除.
- 根Activity和其他Activity都可以设置.
- Google Activity在manifest中所有可配置的属性
- 系统默认行为
-
standard和singleTop的ActivityRecord在哪个TaskRecord,完全由启动它的Activity所在的TaskRecord决定.
- APP1中的A启动APP1中的B(standard或singleTop),B就在A所属的TaskRecord中
- APP1中的A启动APP2中的B(standard或singleTop),B依然在A所属的TaskRecord中
-
singleTask和singleInstance的ActivityRecord,一定在其所属的APP的TaskRecord中,无论是新建还是复用.
-
在Android 9及以后,每新建1个TaskRecord,就会同时新建一个ActivityStack. 在Android 9之前,哪怕是 SingleInstance,也不过是独享了一个 TaskRecord,而不是新建和独享一个 ActivityStack.
-
Recent中展示的最小单位是TaskRecord. 所以1个APP可以在RECENT中展示多个预览图.
-
几个问题
- 从SDK 20起,startActivityForResult且requestCode>=0,启动Activity A,若A是singleTask或singleInstance,则不会为A新建1个TaskRecord.
- 系统为何不直接使用ActivityStack管理ActivityRecord.加入TaskRecord的原因:为了页面的连续跳转 及 窗口多开.
- 1个TaskRecord相当于1个窗口,其持有的多个ActivityRecord就是多个页面,在窗口中可以实现前进,后退
- 多个TaskRecord同时存在,只有1个可以走到前台获得焦点.
5. Intent
- 显式Intent
- 通过setComponent等指定了目标组件的Intent
- 隐式Intent
- 非显示Intent即隐式Intent.通过一些条件过滤目标组件.
- 当存在多个符合条件的目标组件,会弹出弹窗将多个组件列表展示出来供用户选择.
6. Fragment
- 执行replace和add的差别
-
replace会执行prev的onDestroyView, add不会.
-
回退时,replace会执行prev的onCreateView+onViewCreated,以及curr的onDestroyView; add只会执行curr的onDestroyView.
private void loadFirstFragment() { OneTestFragment fragment = new OneTestFragment(); fragment.setListener(() -> loadSecondFragment()); getSupportFragmentManager().beginTransaction() .replace(R.id.fragment_container, fragment, OneTestFragment.class.getSimpleName()) //这里没有执行addToBackStack .commit(); } 2023-01-25 14:11:35.328 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onCreateView 2023-01-25 14:11:35.328 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onViewCreatedprivate void loadSecondFragment() { TwoTestFragment fragment = new TwoTestFragment(); fragment.setListener(() -> loadThirdFragment()); getSupportFragmentManager().beginTransaction() .replace(R.id.fragment_container, fragment, TwoTestFragment.class.getSimpleName()) .addToBackStack(null) .commit(); } 2023-01-25 14:11:40.617 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onDestroyView 2023-01-25 14:11:40.655 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: TwoTestFragment. onViewCreatedprivate void loadThirdFragment() { ThreeTestFragment fragment = new ThreeTestFragment(); getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment, ThreeTestFragment.class.getSimpleName()) .addToBackStack(null) .commit(); } 2023-01-25 14:11:46.440 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: ThreeTestFragment. onViewCreated//回退
2023-01-25 14:11:56.675 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: ThreeTestFragment. onDestroyView//回退
2023-01-25 14:12:03.444 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: TwoTestFragment. onDestroyView 2023-01-25 14:12:03.530 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onCreateView 2023-01-25 14:12:03.536 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onViewCreated//回退
2023-01-25 14:12:12.158 9805-9805/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onDestroyView
- Fragment返回栈实现了Fragment状态的自动保存.
- 2020 年起,fragment 就算 replace,返回后仍然能恢复之前的状态.
- 即使页面跳转时视图被销毁,也要让页面回退时状态被恢复.
- 比如farg1被frag2执行replace. frag1中输入框中的文字,列表滚动的位置都会被保留,再次回到frag1,上述状态都会恢复.
- FragmentManager.popBackStack
- 一直跳转到新Fragment
2023-01-25 14:32:50.139 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onCreateView 2023-01-25 14:32:50.139 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onViewCreated 2023-01-25 14:32:53.872 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onDestroyView 2023-01-25 14:32:53.923 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: TwoTestFragment. onViewCreated 2023-01-25 14:32:56.140 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: ThreeTestFragment. onViewCreated - 一直执行FragmentTransaction addToBackStack
//ThreeTestFragment: mActivity.getSupportFragmentManager().popBackStack() 2023-01-25 14:32:58.355 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: ThreeTestFragment. onDestroyView //TwoTestFragment: mActivity.getSupportFragmentManager().popBackStack() 2023-01-25 14:33:02.511 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: TwoTestFragment. onDestroyView 2023-01-25 14:33:02.550 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onCreateView 2023-01-25 14:33:02.553 11667-11667/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onViewCreated //OneTestFragment: mActivity.getSupportFragmentManager().popBackStack() 无变化, OneTestFragment 依然在显示.
-
修改 OneTestFragment . FragmentTransaction addToBackStack. 即可将该Fragment实例添加至返回栈.
private void loadFirstFragment() { OneTestFragment fragment = new OneTestFragment(); fragment.setListener(() -> loadSecondFragment()); getSupportFragmentManager().beginTransaction() .replace(R.id.fragment_container, fragment, OneTestFragment.class.getSimpleName()) //添加addToBackStack,即可将OneTestFragment实例添加至返回栈 .addToBackStack(null) .commit(); } //ThreeTestFragment: mActivity.getSupportFragmentManager().popBackStack() 2023-01-25 14:45:47.813 12107-12107/com.kunminx.relearn_android_2 D/TagFragment: ThreeTestFragment. onDestroyView //TwoTestFragment: mActivity.getSupportFragmentManager().popBackStack() 2023-01-25 14:45:50.823 12107-12107/com.kunminx.relearn_android_2 D/TagFragment: TwoTestFragment. onDestroyView 2023-01-25 14:45:50.860 12107-12107/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onCreateView 2023-01-25 14:45:50.863 12107-12107/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onViewCreated //OneTestFragment: mActivity.getSupportFragmentManager().popBackStack() //因为之前已经将OneTestFragment添加至返回栈,所以这里执行popBackStack,会触发其View的销毁. 2023-01-25 14:45:54.272 12107-12107/com.kunminx.relearn_android_2 D/TagFragment: OneTestFragment. onDestroyView -
setArugments 和 getArguments 用于向Fragment传递数据.
- 在执行Fragment替换之前执行setArguments传递数据, 在Fragment的onAttach通过getArgumments获取数据
class TestFragmentActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_test_fragment) } fun testSetArguments(view: View) { val fragment: Fragment1 = Fragment1() val bundle: Bundle = Bundle() bundle.putFloat("1", 1.00F) fragment.arguments = bundle supportFragmentManager.beginTransaction() .replace(R.id.container, fragment, fragment.javaClass.simpleName) .addToBackStack(null) .commit() } } class Fragment1 : Fragment() { override fun onAttach(context: Context) { super.onAttach(context) Log.i(TagConstants.TAG_FRAGMENT, "Fragment1. onAttach.") arguments?.apply { val content: Float = getFloat("1") Log.i( TagConstants.TAG_FRAGMENT, "Fragment1. onAttach. arguments exist. content:$content" ) } } } 2023-01-25 15:30:32.777 I/TagFragment: Fragment1. onAttach. 2023-01-25 15:30:32.777 I/TagFragment: Fragment1. onAttach. arguments exist. content:1.0 ***
- Activity重建对应的数据保存和恢复方法
- Activity重建前:
- Activity中每个UI控件都设置ID
- Activity中 onSaveInstanceState 保存Activity的成员变量
- Fragment中每个UI控件都设置ID
- Fragment中 onSaveInstanceState 保存Fragment的成员变量
- Activity重建后:
- Activity中 onRestoreInstanceState 获取之前保存的数据
- Fragment中 onActivityCreated 获取之前保存的数据
//正常进入界面 2023-01-25 16:41:35.243 I/TagActivity: TestFragmentActivity. onCreate. 2023-01-25 16:41:37.333 I/TagFragment: Fragment1. onAttach. 2023-01-25 16:41:37.333 I/TagFragment: Fragment1. onAttach. arguments exist. content:1.0 2023-01-25 16:41:37.333 I/TagFragment: Fragment1. onCreate. 2023-01-25 16:41:37.334 I/TagFragment: Fragment1. onCreateView. 2023-01-25 16:41:37.395 I/TagFragment: Fragment1. onViewCreated. 2023-01-25 16:41:37.400 I/TagFragment: Fragment1. onActivityCreated. //点击HOME //触发Activity和Fragment的onSaveInstanceState 2023-01-25 16:42:00.842 I/TagFragment: Fragment1. onSaveInstanceState. 2023-01-25 16:42:00.846 I/TagActivity: TestFragmentActivity. onSaveInstanceState. c1:冻久了 c2:阿鲁日本五六万 //调整字体大小后,重新进入该APP,触发Activity重建 //这里可见,之前通过setArguments传给Fragment的数据,Activity重建后,Fragment实例依然可以获取到 2023-01-25 16:42:30.532 I/TagFragment: Fragment1. onAttach. 2023-01-25 16:42:30.532 I/TagFragment: Fragment1. onAttach. arguments exist. content:1.0 2023-01-25 16:42:30.532 I/TagFragment: Fragment1. onCreate. 2023-01-25 16:42:30.626 I/TagActivity: TestFragmentActivity. onCreate. 2023-01-25 16:42:30.628 I/TagFragment: Fragment1. onCreateView. 2023-01-25 16:42:30.635 I/TagFragment: Fragment1. onViewCreated. //在Fragment的onActivityCreated中,可以获取之前保存的数据 2023-01-25 16:42:30.635 I/TagFragment: Fragment1. onActivityCreated. 2023-01-25 16:42:30.635 I/TagFragment: Fragment1. onActivityCreated. s1:string s1. 2023-01-25 16:42:30.641 I/TagActivity: TestFragmentActivity. onRestoreInstanceState. c1:冻久了 c2:阿鲁日本五六万 class TestFragmentActivity : AppCompatActivity() { private var c1: String? = null private var c2: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_test_fragment) Log.i(TagConstants.TAG_ACTIVITY, "TestFragmentActivity. onCreate.") } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) c1 = (findViewById<View>(R.id.et1) as EditText).text.toString() c2 = (findViewById<View>(R.id.et2) as EditText).text.toString() outState.putString("c1", c1) outState.putString("c2", c2) Log.i(TagConstants.TAG_ACTIVITY, "TestFragmentActivity. onSaveInstanceState. c1:$c1 c2:$c2") } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) c1 = savedInstanceState.getString("c1") c2 = savedInstanceState.getString("c2") Log.i( TagConstants.TAG_ACTIVITY, "TestFragmentActivity. onRestoreInstanceState. c1:$c1 c2:$c2" ) } fun testSetArguments(view: View) { val fragment: Fragment1 = Fragment1() val bundle: Bundle = Bundle() bundle.putFloat("1", 1.00F) fragment.arguments = bundle supportFragmentManager.beginTransaction() .replace(R.id.container, fragment, fragment.javaClass.simpleName).addToBackStack(null) .commit() } } class Fragment1 : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.i(TagConstants.TAG_FRAGMENT, "Fragment1. onCreate.") } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) Log.i(TagConstants.TAG_FRAGMENT, "Fragment1. onActivityCreated.") savedInstanceState?.apply { val str: String? = getString("s1") Log.i(TagConstants.TAG_FRAGMENT, "Fragment1. onActivityCreated. s1:$str") } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { Log.i(TagConstants.TAG_FRAGMENT, "Fragment1. onCreateView.") return inflater.inflate(R.layout.layout_frag1, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) Log.i(TagConstants.TAG_FRAGMENT, "Fragment1. onViewCreated.") } override fun onAttach(context: Context) { super.onAttach(context) Log.i(TagConstants.TAG_FRAGMENT, "Fragment1. onAttach.") arguments?.apply { val content: Float = getFloat("1") Log.i( TagConstants.TAG_FRAGMENT, "Fragment1. onAttach. arguments exist. content:$content" ) } } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) Log.i(TagConstants.TAG_FRAGMENT, "Fragment1. onSaveInstanceState.") outState.putString("s1", "string s1.") } }
- 避免Fragment重叠
- Fragment重叠是因为Activity触发了重建,重建会自动恢复之前添加的Fragment实例.若不加判断直接创建Fragment实例添加就会触发重叠.
- 正确做法是在FragmentActivity中判断是否能获取到之前加载过的Fragment实例,找不到再创建加载.
//进入页面 2023-01-25 17:05:45.313 I/TagActivity: TestFragmentActivity. onCreate. 2023-01-25 17:05:47.875 I/TagFragment: Fragment1. onAttach. *** 2023-01-25 17:05:47.960 I/TagFragment: Fragment1. onActivityCreated. //HOME 2023-01-25 17:06:02.156 I/TagFragment: Fragment1. onSaveInstanceState. 2023-01-25 17:06:02.160 I/TagActivity: TestFragmentActivity. onSaveInstanceState. c1:222 c2:333 //Activity重建,发现之前已经加载过Fragment实例 *** 2023-01-25 17:06:15.755 I/TagActivity: TestFragmentActivity. onCreate. 2023-01-25 17:06:15.755 I/TagActivity: TestFragmentActivity. onCreate. frag : Fragment1{9d9fde2} (fe3e0b11-ce9f-4b09-8288-4d61db5b6908 id=0x7f08007f tag=Fragment1) *** 2023-01-25 17:06:15.783 I/TagActivity: TestFragmentActivity. onRestoreInstanceState. c1:222 c2:333 class TestFragmentActivity : AppCompatActivity() { private var c1: String? = null private var c2: String? = null private var frag: Fragment1? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_test_fragment) Log.i(TagConstants.TAG_ACTIVITY, "TestFragmentActivity. onCreate.") savedInstanceState?.apply { frag = supportFragmentManager.findFragmentByTag(Fragment1::class.java.simpleName) as Fragment1 Log.i(TagConstants.TAG_ACTIVITY, "TestFragmentActivity. onCreate. frag : $frag") } } *** }
- 建议使用单Activity多Fragment原因
- Fragment可以被混淆,Activity不能被混淆
- 饿了么的开源工具可以混淆Activity和View. Mess
- Fragment专用于视图控制,占用资源少,切换速度比Activity快5-10倍
- Fragment重建及恢复比Activity更快
- 建议用DialogFragment替换Dialog
- 可以很方便的承载复杂的布局
- 可以跟随Activity自动完成重建及状态恢复,避免Activity重建后对话框消失
- 什么情况不适合单Activity多Fragment结构
- 每个 Activity 持有一个 Window,申请的资源有限,单Activity 架构的多 Fragment 在转场动画时,容易存在不可预期的绘制错误,如果 fragment 中采用 毛玻璃等重量级绘制任务,容易出问题.