Android Activity 探坑分析

01.Activity异常生命周期

  • 虽然前面也说了这个异常的生命周期,但是还是有些疑问?
  • 异常条件会调用什么方法
    • 当非人为终止Activity时,比如系统配置发生改变时导致Activity被杀死并重新创建、资源内存不足导致低优先级的Activity被杀死,会调用 onSavaInstanceState() 来保存状态。该方法调用在onStop之前,但和onPause没有时序关系。
    • 有人会问,onSaveInstanceState()与onPause()的区别,onSaveInstanceState()适用于对临时性状态的保存,而onPause()适用于对数据的持久化保存。
    • 当异常崩溃后App又重启了,这个时候会走onRestoreInstanceState()方法,可以在该方法中取出onSaveInstanceState()保存的状态数据。
  • 什么时候会引起异常生命周期
    • 资源相关的系统配置发生改变或者资源不足:例如屏幕旋转,当前Activity会销毁,并且在onStop之前回调onSaveInstanceState保存数据,在重新创建Activity的时候在onStart之后回调onRestoreInstanceState。其中Bundle数据会传到onCreate(不一定有数据)和onRestoreInstanceState(一定有数据)。
    • 还有中情况是异常导致app崩溃,然后并没有完全杀死进程,重启回到activity页面,也会引起异常生命周期。

02.后台Activity被系统回收

  • 后台的Activity被系统回收怎么办?

    • Activity中提供了一个 onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用,可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。onSaveInstanceState()方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用putString()方法保存字符串,使用putInt()方法保存整型数据。每个保存方法需要传入两个参数,第一个参数是键,用于后面从 Bundle中取值,第二个参数是真正要保存的内容。
  • 说一下onSaveInstanceState()和onRestoreInstanceState()方法特点?

    • Activity的 onSaveInstanceState()和onRestoreInstanceState()并不是生命周期方法,它们不同于onCreate()、onPause()等生命周期方法,它们并不一定会被触发。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      //保存数据
      @Override
      protected void onSaveInstanceState(Bundle outBundle) {
      super.onSaveInstanceState(outBundle);
      outBundle.putBoolean("Change", mChange);
      }

      //取出数据
      @Override
      protected void onRestoreInstanceState(Bundle savedInstanceState) {
      super.onRestoreInstanceState(savedInstanceState);
      mChange = savedInstanceState.getBoolean("Change");
      }

      //或者在onCreate方法取数据也可以
      //onCreate()方法其实也有一个Bundle类型的参数。这个参数在一般情况下都是null,
      //但是当活动被系统回收之前有通过 onSaveInstanceState()方法来保存数据的话,这个参就会带有之前所保存的全部数据
      protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      if (savedInstanceState != null) {
      String data = savedInstanceState.getString("data");
      }
      }
  • 什么时候会触发走这两个方法?

    • 当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。除非该activity是被用户主动销毁的,通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
  • onSaveInstanceState()被执行的场景有哪些?

    • 系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activityA是否会被销毁,因此系统都会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则当用户按下HOME键时
      • 长按HOME键,选择运行其他的程序时
      • 锁屏时
      • 从activity A中启动一个新的activity时
      • 屏幕方向切换时

03.如何判断activity优先级

  • 除了在栈顶的activity,其他的activity都有可能在内存不足的时候被系统回收,一个activity越处于栈底,被回收的可能性越大.如果有多个后台进程,在选择杀死的目标时,采用最近最少使用算法(LRU)。

04.App切换到后台分析

  • app切换到后台,当前activity会走onDestory方法吗?
    • 不会走onDestory方法,会先后走onPause和onStop方法。
  • 一般在onstop方法里做什么?
    • 比如。写轮播图的时候,会在onstop方法里写上暂停轮播图无限轮播,在onStart方法中会开启自动无限轮播。
    • 再比如,写视频播放器的时候,当app切换到后台,则需要停止视频播放,也是可以在onstop中处理的。
  • 什么情况会导致app会被杀死,这时候会走onDestory吗?
    • 系统资源不足,会导致app意外被杀死。应用只有在进程存活的情况下才会按照正常的生命周期进行执行,如果进程突然被kill掉,相当于System.exit(0); 进程被杀死,根本不会走(activity,fragment)生命周期。只有在进程不被kill掉,正常情况下才会执行ondestory()方法。
  • activity被回收如何恢复
    • 当系统内存不足时, activity会被回收,我们其实可以覆写onSaveInstanceState()方法。onSaveInstanceState()方法接受一个Bundle类型的参数, 开发者可以将状态数据存储到这个Bundle对象中,这样即使activity被系统摧毁,当用户重新启动这个activity而调用它的onCreate()方法时,上述的Bundle对象会作为实参传递给onCreate()方法,开发者可以从Bundle对象中取出保存的数据, 然后利用这些数据将activity恢复到被摧毁之前的状态。

05.桌面launcher启动App

  • App启动过程有哪些?
    • app启动的过程有两种情况,第一种是从桌面launcher上点击相应的应用图标,第二种是在activity中通过调用startActivity来启动一个新的activity。
  • 从桌面launcher上点击应用图标会干啥,调用startActivty()又会做什么?
    • 创建一个新的项目,默认的根activity都是MainActivity,而所有的activity都是保存在堆栈中的,启动一个新的activity就会放在上一个activity上面,而我们从桌面点击应用图标的时候,由于launcher本身也是一个应用,当我们点击图标的时候,系统就会调用startActivitySately(),一般情况下,我们所启动的activity的相关信息都会保存在intent中,比如action,category等等。
    • 我们在安装这个应用的时候,系统也会启动一个PackaManagerService的管理服务,这个管理服务会对AndroidManifest.xml文件进行解析,从而得到应用程序中的相关信息,比如service,activity,Broadcast等等,然后获得相关组件的信息。
    • 当我们点击应用图标的时候,就会调用startActivitySately()方法,而这个方法内部则是调用startActivty(),而startActivity()方法最终还是会调用startActivityForResult()这个方法。而在startActivityForResult()这个方法。因为startActivityForResult()方法是有返回结果的,所以系统就直接给一个-1,就表示不需要结果返回了。
    • 而startActivityForResult()这个方法实际是通过Instrumentation类中的execStartActivity()方法来启动activity,Instrumentation这个类主要作用就是监控程序和系统之间的交互。而在这个execStartActivity()方法中会获取ActivityManagerService的代理对象,通过这个代理对象进行启动activity。启动会就会调用一个checkStartActivityResult()方法,如果说没有在配置清单中配置有这个组件,就会在这个方法中抛出异常了。
    • 当然最后是调用的是Application.scheduleLaunchActivity()进行启动activity,而这个方法中通过获取得到一个ActivityClientRecord对象,而这个ActivityClientRecord通过handler来进行消息的发送,系统内部会将每一个activity组件使用ActivityClientRecord对象来进行描述,而ActivityClientRecord对象中保存有一个LoaderApk对象,通过这个对象调用handleLaunchActivity来启动activity组件,而页面的生命周期方法也就是在这个方法中进行调用。

06.Activity传递参数数据

  • 两个Activity之间怎么传递数据?

    • 基本数据类型可以通过Intent传递数据

    • 把数据封装至intent对象中

      1
      2
      3
      Intent intent = new Intent(content, MeActivity.class);
      intent.putExtra("goods_id", goods_id);
      content.startActivity(intent);
    • 把数据封装至bundle对象中

    • 把bundle对象封装至intent对象中

      1
      2
      3
      4
      Bundle bundle = new Bundle();
      bundle.putString("malename", "李志");
      intent.putExtras(bundle);
      startActivity(intent);
  • intent和bundle有什么区别?

    • Intent传递数据和Bundle传递数据是一回事,Intent传递时内部还是调用了Bundle。

      1
      2
      3
      4
      5
      6
      7
      public @NonNull Intent putExtra(String name, String value) {
      if (mExtras == null) {
      mExtras = new Bundle();
      }
      mExtras.putString(name, value);
      return this;
      }
  • 为什么有了intent还要设计bundle?

    • 两者比较

      • Bundle只是一个信息的载体,内部其实就是维护了一个Map<String,Object>。
      • Intent负责Activity之间的交互,内部是持有一个Bundle的。
    • bundle使用场景

      • Fragment之间传递数据;比如,某个Fragment中点击按钮弹出一个DialogFragment。最便捷的方式就是通过Fragment.setArguments(args)传递参数。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      public static void showFragmentDialog(String title, String content, boolean is_open, AppCompatActivity activity) {
      ServiceDialogFragment mainDialogFragment = new ServiceDialogFragment();
      Bundle bundle = new Bundle();
      bundle.putString("title", title);
      bundle.putString("content", content);
      bundle.putBoolean("is_open",is_open);
      mainDialogFragment.setArguments(bundle);
      mainDialogFragment.show(activity.getSupportFragmentManager());
      }

      @Override
      public void onCreate(Bundle savedInstanceState) {
      setLocal(Local.CENTER);
      super.onCreate(savedInstanceState);
      Bundle bundle = getArguments();
      if (bundle != null) {
      title = bundle.getString("title");
      content = bundle.getString("content");
      is_open = bundle.getBoolean("is_open");
      }
      }

07.Activity任务战Task

  • 同一程序不同的Activity是否可以放在不同的Task任务栈中?
    • 可以的。比如:启动模式里有个Singleinstance,可以运行在另外的单独的任务栈里面。用这个模式启动的activity,在内存中只有一份,这样就不会重复的开启。
    • 也可以在激活一个新的activity时候,给intent设置flag,Intent的flag添加FLAG_ACTIVITY_NEW_TASK,这个被激活的activity就会在新的task栈里面