Android生命周期的那些事

这篇文章总结一下Android里面那些常用组件的生命周期,以及在项目中的一些简单应用。

Activity

Activity的生命周期

说起Activity的生命周期,很多人都会马上想起下面这张图:

Activity生命周期
Activity生命周期

(不错,这是一个已经烂大街的问题。不过我是总结给自己看的=。=)

我写了一个例子观察不同的动作对生命周期的影响:

  1. Activity首次启动:

    LifeCycle1Activity: onCreate LifeCycle1Activity: onStart LifeCycle1Activity: onResume

  2. 按下Home键:

    LifeCycle1Activity: onPause LifeCycle1Activity: onStop

  3. 再次打开app:

    LifeCycle1Activity: onRestart LifeCycle1Activity: onStart LifeCycle1Activity: onResume

  4. 跳转到其他Activity:

    LifeCycle1Activity: onPause LifeCycle2Activity: onCreate LifeCycle2Activity: onStart LifeCycle2Activity: onResume LifeCycle1Activity: onStop

  5. 从其他Activity返回:

    LifeCycle2Activity: onPause LifeCycle1Activity: onRestart LifeCycle1Activity: onStart LifeCycle1Activity: onResume LifeCycle2Activity: onStop LifeCycle2Activity: onDestroy

  6. Activity销毁:

    LifeCycle1Activity: onPause LifeCycle1Activity: onStop LifeCycle1Activity: onDestroy

  7. 切换横竖屏:

    LifeCycle1Activity: onPause LifeCycle1Activity: onStop LifeCycle1Activity: onDestroy LifeCycle1Activity: onCreate LifeCycle1Activity: onStart LifeCycle1Activity: onResume

和生命周期相关的动作基本就是上面这些了。现在对照上面那张图以及这些Log,可以总结出以下几点:

  1. 当一个Activity创建并出现在屏幕中时,一定要经历onStart()onResume()两个函数,只有做完onResume()这个动作,Activity才会可见;
  2. Activity退到后台时(按home键或切换Activity),一定要经过onPause()onStop()两个动作。经过onPause()这个动作,Activity就已经不可见了,这个时候其他组件(如其他Activity)会开始自己的创建绘制工作,等屏幕上面的其他组件绘制完成并覆盖原Activity后,原Activity才会调用onStop()方法;
  3. Activity从后台变为可见时,一定会经过onRestart()onStart()onResume()三个动作,onResume()之后Activity才变得可见;
  4. 切换横竖屏会导致Activity销毁并重建。

<br>

ActivityFragment生命周期比较

由于Fragment是要依附Activity才能存在的,所以把FragmentActivity放在一起对比,下面的View同理。

说实话,Fragment是我不怎么喜欢的组件(因为实在太复杂),但既然Google推荐,我们还是总结一下:

fragment生命周期
fragment生命周期

有了上面这张图,再结合Activity的生命周期,基本就可以梳理完Fragment的生命周期问题了。

这里主要总结一下同一个Activity内不同Fragment创建或替换时,生命周期的变化。

  1. 同一个Activity内同时创建两个Fragment:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class LifeCycle2Activity extends FragmentActivity {

    MyFragment1 myFragment1 = new MyFragment1();
    MyFragment2 myFragment2 = new MyFragment2();

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

    Log.i(TAG, "onCreate");

    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction ft = fragmentManager.beginTransaction();

    ft.add(R.id.container_fragment1, myFragment1);
    ft.add(R.id.container_fragment2, myFragment2);
    ft.commit();

    }
    ....
    }

    这里只摘了重要代码片段,打印出的Log如下:

    LifeCycle2Activity: onCreate MyFragment1: onAttach(Activity) MyFragment1: onAttach(Context) MyFragment1: onCreate MyFragment2: onAttach(Activity) MyFragment2: onAttach(Context) MyFragment2: onCreate MyFragment1: onCreateView MyFragment1: onActivityCreated MyFragment2: onCreateView MyFragment2: onActivityCreated LifeCycle2Activity: onStart LifeCycle2Activity: onResume

    从中可以看出,不同Fragment的创建是依次进行的,但它们都遵循上面那张图的规则。

  2. Fragment之间的替换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public class LifeCycle2Activity extends FragmentActivity {

    private final String TAG = getClass().getName();

    MyFragment1 myFragment1 = new MyFragment1();
    MyFragment2 myFragment2 = new MyFragment2();

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

    Log.i(TAG, "onCreate");

    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction ft = fragmentManager.beginTransaction();
    ft.add(R.id.container_fragment1, myFragment1);
    ft.commit();

    findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    getSupportFragmentManager().beginTransaction().
    replace(R.id.container_fragment1, myFragment2)
    .addToBackStack(null)
    .commit();
    }
    });
    }
    .....
    }

    上面的例子中,Activity先加载MyFragment1,然后在点击按钮时,用MyFragment2替换MyFragment1。打印出的Log如下:

    MyFragment2: onAttach(Activity) MyFragment2: onAttach(Context) MyFragment2: onCreate MyFragment1: onDestroyView MyFragment2: onCreateView MyFragment2: onActivityCreated

    可以发现,原来的Fragment的视图会被销毁,新的Fragment则会重建。但上面的生命周期流程图显示,只有在Activity进入Destroy状态时,Fragment才会执行onDestroyView()动作,Log里面MyFragment1却执行了onDestroyView()方法,有点费解。

    如果Activity中包含较多的Fragment,比较好的方式是将它们缓存起来,防止频繁地创建和回收资源造成内存“抖动”现象。Google之所以提倡使用Fragment,是因为Fragment占用的资源比Activity小,这样,如果可以把视图都嵌入到多个Fragment中,而共用同一个Activity,可以更加合理地使用内存和CPU资源。

<br>

ActivityView生命周期比较

笔者之前比较关注自定义View的一些知识,但比较忽略View生命周期相关的东西,直到前不久的项目中需要获得View的宽高后才遇到了问题,所以这一块有必要总结一下。

我简单地自定义了一个View,并在一些关键的回调函数里面打了Log:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MyView extends View {

public static final String TAG = "MyView";

public MyView(Context context) {
super(context);
Log.i(TAG, "call Constructor");
}

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Log.i(TAG, "call onMeasure");
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Log.i(TAG, "call onLayout");
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.i(TAG, "call onDraw");
}
}

将这个View放到Activity的Layout文件中,启动Activity时会打出如下Log:

MyView: call Constructor LifeCycle1Activity: onCreate LifeCycle1Activity: onStart LifeCycle1Activity: onResume MyView: call onMeasure MyView: call onMeasure MyView: call onMeasure MyView: call onMeasure MyView: call onLayout MyView: call onMeasure MyView: call onMeasure MyView: call onLayout MyView: call onDraw

结果明确地告诉我们,View只有在Activity执行完onResume()动作后才会开始测量绘制工作,在这之前它仅仅是实例化了自己。相信很多人刚开始接触Android时,都曾经尝试过在onCreate()函数里去获取View的宽高,结果总是得到0。现在看来那是再自然不过的事情了,因为View只有测量绘制后才会有宽高。

另外,View只会测量并布局一次,之后如果Activity从后台重新返回前台,View只会执行onDraw()方法,下面是Activity切换回来后打印的Log:

LifeCycle1Activity: onStop LifeCycle2Activity: onPause LifeCycle1Activity: onRestart LifeCycle1Activity: onStart LifeCycle1Activity: onResume MyView: call onDraw LifeCycle2Activity: onStop

显然,Activity执行onResume()后界面需要重绘,所以需要执行onDraw()函数,但View的宽高及摆放位置是一样的,所以onMeasure()onLayout()便不必再执行。

现在问题来了,如果onResume()之后才开始测量绘制,我们要如何得到View的宽高呢?Google为每一个View提供了一个ViewTreeObserver类来帮我们监听View的一些动作,可以通过注册OnGlobalLayoutListener监听器来获得View的宽高等信息,见下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_life_cycle1);
myView = (MyView) findViewById(R.id.myview);
Log.i(TAG, "onCreate");
Log.i(TAG, "onCreate width===>" + myView.getWidth() +
" height===>" + myView.getHeight());
myView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Ensure you call it only once :
if(android.os.Build.VERSION.SDK_INT >=
android.os.Build.VERSION_CODES.JELLY_BEAN) {
myView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
else {
myView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}

Log.i(TAG, "onGlobalLayout width===>" + myView.getWidth() +
" height===>" + myView.getHeight());
}
});
}

onCreate函数中,我在myView上注册了OnGlobalLayoutListener监听器,结果打印的Log如下:

MyView: call Constructor LifeCycle1Activity: onCreate LifeCycle1Activity: onCreate width===>0 height===>0 LifeCycle1Activity: onStart LifeCycle1Activity: onResume MyView: call onMeasure MyView: call onMeasure MyView: call onMeasure MyView: call onMeasure MyView: call onLayout LifeCycle1Activity: onGlobalLayout width===>350 height===>350 MyView: call onMeasure MyView: call onMeasure MyView: call onLayout MyView: call onDraw

不难看出,这个监听器会在测量得到宽高后被调用,并返回正确的宽高。关于View生命周期更多的东西,可以参考后面提供的链接。

<br>

参考

Android自定义view生命周期

getWidth() and getHeight() of View returns 0