Android进阶之Activity生命周期+Activity难点

1 概要

Activity是一种展示型组件,主要是向用户展示一个界面,并且可以接收用户的输入信息从而和用户进行交互。对用户来说,Activity就是Android应用的全部,因为其他三大组件对用户来说是不可感知的。

2 Activity的生命周期图(正常生命周期)

只有Activity之间的转换才会调用生命周期函数。
在这里插入图片描述

2.1 启动一个新的 Activity 后,onStart ——> onResume 都会执行,那什么时候会执行 onStart,什么时候接着执行 onResume 呢?

(1)Activity 启动后,我们会看到界面,然后可以点击界面上的按钮,这时候是不是分成了二大块:

  1. 看得到我们写 Activity 的界面
  2. 然后可以操作我们的界面

所以 (onStart - onStop) 和界面 是否可见 有关(onResume - onPause)和界面 是否位于前台、是否可以操作 有关

(2)onRestart 表示 Activity 重新启动,当前 Activity 从不可见重新变为可见状态时,onRestart() 会被回调。例如:用户按Home切换到桌面。

2.2 假设当前Activity为A,这时用户打开一个新的Activity B,那么B的onResume和A的onPause哪个先执行呢?

在这里插入图片描述
(1)简单理解,启动Activity的请求会由Instrumentation来处理,然后他通过Binder向AMS发请求,AMS内部维护着一个ActivityStack并负责栈内的Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期方法的调用。

(2)在ActivityStack中的resumeTopActivityLnnerLocked方法中,有这么这段代码,总结:在新 Activity 启动之前,栈顶的Activity 需要先调用 onPause 后,新的Activity才能启动

// we need to start pausing the current activity so the top one can be resumed
boolean dontWaitForPause = (next.info.flags& ActivityInfo.FLAG_RESUME_WHILE_PAUSING)!=0;
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, KeyStore.TrustedCertificateEntry,dontWaitForPause);
 if(mResumedActivity != null){
	pausing != startPausingLocked(userLeaving,false,true,dontWaitForPause);
	if(DEBUG_STATES){
		 Slog.d(TAG,"resumeTopActivityLocked:pausing" + mResumedActivity);
	}
}

(3)最终,在 ActvityStackSupervisor 中的 realStartActivityLocked 方法中,会调用如下代码:

app.thread.scheduleLaunchActivity(new Intent(r.intent),r.appToken,System.identityHashCode(r),r.info,new Configuration(mService.mConfiguration)
        ,r.compat,r.task.voiceInteractor,app.repProcState,r.icicle,r.persistentState,results,
        new Intents,!andResume,mService.isNextTransitionForward() ,profilerInfo);

在这个 app.thread 的类型是 IApplicationThread 的具体实现是 ActivityTread 中的 ApplicationThread 。所以,这段代码实际上调用了 ActivityThread 当中的 scheduleLaunchActivity 方法,最终会完成新 Activity 的 onCreate、onStrrt、onResume 的调用过程。因此可以得出结论是:旧 Activity 先 onPause,然后新的 Activity 再启动

(4)至于 ApplicationThread 的 scheduleLaunchActivity 方法为什么会完成新 Activity 的生命周期,scheduleLaunchActivty() 最终调用 handlerLaunchActivity() 方法,请看代码:

private void handlerLaunchActivity(ActivityClientRecord r, Intent customIntent){
        //if we are getting ready to gc after going to the background,well we are back active so skip it
        unscheduleGcIdler();
        mSomeActivitiesChanged =true;
        if(r.profilerInfo != null){
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling;
        }
        handlerConfigurationChanged(null,null);
        if(localLOGV)Slog.v(TAG,"Handling launch of"+r);
        
        // 在这里新Activity被创建出来,其onCreate和onStart被调用
        Activity a = PerformLaunchActivity(r,customIntent);

        if(a != null){
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.start;
            // 在这里新Activity的onResume会被调用
            handlerResumeActivity(r.token,false,r.isForward, !r.activity.mFinished && r.startsNotResumed);
        }
        //省略...
}

2.4 如果从 A 这个 Activity ,跳到了 B 这个 Activity,那二个 Activity 的 (onStart - onStop) 和(onResume - onPause)又分别如何执行。

A(onPause) -> B(onCreate)->B(onStart)> B(onResume) -> A(onStop)

如果从一个 Activity 跳转到另外一个 Activity 之前,要做一些操作的话,最好是放在 onStop 中,因为如果放在 onPause 中的话,会影响新的 Activity 启动速度

2.5 半遮挡情况

(1)特殊情况,在启动一个半遮挡/透明主题/透明的Dialog样式的Activity,当前 Activity 不会调用 onStop()

(2)注意:弹出当前 Activity 的 Dialog 对话框属于它的组件,是不会引起生命周期变化的

2.6 全遮挡情况

(1)用户正常打开新的 Activity 时,原 Activity 的回调是:onPause()–>onStop()
在这里插入图片描述
(2)用户从新 Activity 再次返回原 Activity 时,原 Activity 的回调是:onRestart()–>onStart()–>onResume()
在这里插入图片描述

2.7 按主菜单键情况

(1)按主菜单键返回桌面时,回调是:onPause()–>onStop()

(2)再点击 App 返回退出时的Activity,回调是:onRestart()–>onStart()–>onResume()

3 异常情况下的生命周期分析

3.1 屏幕旋转等系统配置发生改变,导致 Activity 被杀死并重新创建

(1)异常生命周期如图
在这里插入图片描述

(2)保存和默认恢复 View 是如何实现的?

当 Activity 在异常情况下需要重新创建时,系统会默认保存当前的 Activity 视图架构,并且恢复这些数据。比如:EditText 中的数据、ListView滚动的位置,这些View相关的状态系统都会默认恢复。

系统的工作流程是这样的:Activity 被意外终止时,Activity 会调用 onSaveInstanceState 去保存数据,然后委托 Window 去保存数据,接着 Window 再委托它的顶级容器 DecorView(ViewGroup) 去保存数据,最后顶层容器再去通知它的子 View 来保存数据,这样整个数据保存过程就完成了,详细看 Activity 的 onSaveInstanceState 源码。同理,恢复数据也是类似的流程。

(3)onSaveInstanceState保存,onRestoreInstanceState 恢复数据的案例?

public class MainActivity extends AppCompatActivity {
    public static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState != null) {
            String test = savedInstanceState.getString("extra_test");
            Log.i(TAG, test);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
        String content = "test";
        Log.d(TAG, "onSaveInstanceState extra_test:" + content);
        outState.putString("extra_test", content);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String test = savedInstanceState.getString("extra_test");
        Log.d(TAG, test);
    }
}
MainActivity: onConfigurationChanged, newOrientation:1
MainActivity: onPause
MainActivity: onSaveInstanceState extra_test:test
MainActivity: onStop
MainActivity: onDestroy

MainActivity: [onCreate]restore extra_test:test
MainActivity: onStart
MainActivity: [onRestoreInstanceState]restore extra_test:test
MainActivity: onResume

(4)建议在 onCreate(Bundle savedInstanceState) 中恢复数据

常状态保存和恢复,是在 onSaveInstanceState 方法中保存 Activity 状态、数据;建议在onCreate(Bundle savedInstanceState) 中恢复,因为 onRestoreInstanceState 方法不一定执行。

(5)onSaveInstanceState 保存数据有限,不可作为持久化使用?

Activity的onSaveInstanceState()方法不是Activity生命周期方法,也不保证
一定会被调用。它是用来在Activity被意外销毁时保存UI状态的,只能用于保存临时性数据,例如UI控件的属性等,不能跟数据的持久化存储混为一谈。持久化存储应该在Activity的onPause()/onStop()中实行,在onCreate(Bundle savedInstanceState)中恢复

3.1.1 监听系统配置变化,并且不让 Activity 重新创建

(1)onConfigurationChanged(Configuration newConfig)方法的作用?(newConfig:新的设备配置信息)

只有在配置文件 AndroidManifest 中设置了 configChanges 属性对应的设备配置,当系统的配置信息发生改变时,系统会调用此方法。如果发生设备配置与在配置文件中设置的不一致,则Activity会被销毁重建。

(2)如何修改配置,不让 Activity 重新创建?

当屏幕方向发生改变时,Activity 默认会被销毁重建。如果AndroidManifest 文件中处理屏幕方向配置信息如下: configChanges = orientation,则 Activity 不会被销毁重建,而是调用 onConfigurationChanged 方法
在这里插入图片描述
如果 configChanges 只设置了 orientation,当其他设备配置信息改变时,Activity 依然会销毁重建,且不会调用 onConfigurationChanged 方法。例如,在上面的配置的情况下,如果语言改变了,Activity 就会销毁重建,且不会调用 onConfigurationChanged 方法。

(3)configChanges 的取值表
在这里插入图片描述

如果 targetSdkVersion 的值小于 13,则只要配置:

android:configChanges="orientation"

如果 targetSdkVersion 的值大于等于 13,则如下配置才会回调 onConfigurationChanged 方法,因此适配全面需要以下的配置:

// orientation横竖屏切换,screenSize屏幕大小变化
android:configChanges="orientation|screenSize"

(4)扩展:接入一个外设键盘时,如何规避Activity 的销毁和创建了两次

当用户接入一个外设键盘时,其中一次的销毁重建是因为外设键盘的插入和拔出。当设置android:configChanges="keyboardHidden|keyboard"之后,就不会销毁重建,而是调用 onConfigurationChanged 方法。另一次是,当接入外设键盘时,除了键盘类型的改变,触摸屏也发生了变化。因为使用外设键盘,触摸屏不能使用了。

所以,要规避 Activity 的销毁和创建了两次,且回调 onConfigurationChanged 方法, configChanges 属性配置如下:
在这里插入图片描述
(5)学习链接

关于onConfigurationChanged方法及常见问题解决

3.2 资源内存不足导致低优先级的 Activity 被杀死

(1)内存不足时候杀死优先级低的 Activity,这时候的数据存储和恢复过程和我们上面讲的也是一样的

(2)那 Activity 的具体的优先级怎么样的呢:

1、前台Activity:正在和用户交互的Activity,优先级最高
2、可见但非前台Activity:比如对话框,导致Activity可见但是位于后台无法和用户直接交互
3、后台Activity:已经被暂停的Activity,比如执行了onStop,优先级最低

(3)我们可以看到后台 Activity 很容易被杀死,所以一些后台工作更适合放到 Service 中去,这样保证优先级,不会轻易被系统杀死

4 setResult 方法难点

4.1 setResult 方法的调用时机

(1)调用时机分析

// 1、重写onBackPressed()方法,捕获BACK事件,捕获到之后先setResult
@Override
public void onBackPressed() {
	Log.i(TAG, "onBackPressed");
	setResult(Const.LIVE_OK);
	super.onBackPressed();
 }
// 2、按点击事件中显式的调用finish()
setResult(RESULT_OK);
finish();
// ActivityB 回退到 ActivityA 过程中,执行过程是

B---onBackPressed
B---setResult 
B---finish
B---onPause (前)
A---onActivityResult  // A 的 onActivityResult 需要在 B 的 onPause 之后,A 的 onRestart 之前调用
A---onRestart
A---onStart
A---onResume
B---onStop
B---onDestroy

结论1:Activity A 的 onActivityResult 需要在 B 的 onPause 之后,A 的 onRestart 之前调用,所以 B 的 setResult 方法应该在 B 的 onPause 之前调用.

结论2:Activity A 回调结果是在被 finish 之后,也就是说 Activity B 调用 setResult 方法必须在 finish 之前。当然在 onCreate 就调用 setResult 肯定是在 finish 之前的,但是又不满足业务需要。

(2)学习链接
setResult()的调用时机

4.2 startActivityForResult 启动 singleTask 的 Activity,则 onActivitResult 方法立即回调且 resultCode 为 RESULT_CANCEL

4.2.1 问题分析与

(1)ActivityA–>XSTChatActivity,launchMode=“singleTask”(singleInstance),则会在 LaunchFlags 中加入FLAG_ACTIVITY_NEW_TASK 标志,启动后 A 的 onActivitResult 立即回调 resultCode 为 RESULT_CANCEL;原因–链接

(2)ActivityA–>XSTChatActivity,setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),A 的 onActivityResult 不会立即回调,但是在 setResult(RESULT_OK) + finish() 返回后,A 的 onActivitResult() 回调 resultCode 为 RESULT_CANCEL。

4.2.2 解决方案

(1)所以:在XSTChatActivity返回时,设置 setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP) 重新启动A,接着在 A 的 onNewIntent() 获取内容。

(2)其他解决方案:类似 EventBus 的观察者模式修改。

4.2.3 学习链接

[Android]startActivityForResult启动singleTask的Activity,则onActivitResult()立即回调且resultCode为RESULT_CANCEL

Android - startActivityForResult immediately triggering onActivityResult

Android中activity的回调和onActivityResult的使用

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值