Android开发学习 -- Day4 探究Activity(三)

一、Activity的生命周期

掌握Activity的生命周期对于各任何Android开发者来说都非常重要。当深入理解Activity的生命周期之后,就可以写出更加连贯流畅的程序。

1、返回栈

通过前面的学习,我们发现Android中的Activity是可以层叠的。每新启动一个Activity,就会覆盖原来的Activity,然后点击back键会销毁最上面的,下面的一个Activity就会重新显示出来。

Android是使用任务(Task)来管理Activity的,一个任务就是一组存放在栈里的Activity集合,这个栈也被称作返回栈(Back Stack)。栈是一种先进后出的数据结构,在默认情况下,每当我们启动一个新的Activity,它会在返回栈中入栈,并处于栈顶。在我们按下back键销毁一个Activity时,处于栈顶的Activity会出栈,前一个入栈的Activity重新回到栈顶。系统总是将栈顶的Activity展示给用户。

2、Activity状态

每个活动在其生命周期中最多可能会有4种状态。

1). 运行状态

当一个活动位于返回栈的栈顶时, 这时活动就处于运行状态。系统最不愿意回收的就是处于运行状态的活动, 因为这会带来非常差的用户体验。

2). 暂停状态

当一个活动不再处于栈顶位置, 但仍然可见时, 这时活动就进人了暂停状态。你可能会觉得既然活动已经不在栈顶了, 还怎么会可见呢? 这是因为并不是每一个活动都会占满整个屏幕的,比如对话框形式的活动只会占用屏幕中间的部分区域, 你很快就会在后面看到这种活动。 处于暂停状态的活动仍然是完全存活着的, 系统也不愿意去回收这种活动〈因为它还是可见的, 回收可见的东西都会在用户体验方面有不好的影响 ), 只有在内存极低的情况下, 系统才会去考虑回收这种活动。

3). 停止状态

当一个活动不再处于栈顶位置, 并且完全不可见的时候, 就进入了停止状态。 系统仍然会为这种活动保存相应的状态和成员变量, 但是这并不是完全可靠的, 当其他地方需要内存时, 处于停止状态的活动有可能会被系统回收。

4).销毁状态

当一个活动从返回栈中移除后就变成了销毁状态。 系统会最倾向于回收处于这种状态的活动, 从而保证手机的内存充足。

3、Activity的生存期

Activity 类中定义了 7个回调方法, 覆盖了活动生命周期的每一个环节, 下面就来一一介绍这7个方法。

口 onCreate() 这个方法你已经看到过很多次了, 每个活动中我们都重写了这个方法, 它会在活动第一次被创建的时候调用。 你应该在这个方法中完成活动的初始化操作, 比如说加载布局、 绑定事件等。

口 onStart() 这个方法在活动由不可见变为可见的时候调用。

口 onResume() 这个方法在活动准备好和用户进行交互的时候调用。此时的活动定位于返回栈的栈顶 并且处于运行状态。

口 onPause() 这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将那些消耗 CPU 的资源释放掉, 以及保存一些关键数据, 但这个方法的执行速度一定要快, 不然会影响到新的栈顶活动的使用。

口 onStop() 这个方法在活动完全不可见的时候调用。 它和 onPause()方法的主耍区别在于, 如果启动的新活动是一个对话框式的活动, 那么 onPause()方法会得到执行, 而onStop()方法并不会执行。

口 onDestory() 这个方法在活动被销毁之前调用, 之后活动的状态将变为销毁状态。

口 onRestart() 这个方法在活动曰停止状态变为运行状态之前调用, 也就是活动被重新启动了。

以上7个方法中除了onCreate(),其他都是两两相对的。在Android的官网上,提供一张非常清晰的示意图。

Activity生命周期

为了体验Activity的生命周期,我们可以创建一个工程来,修改代码:

public class ExampleActivity extends Activity {

    public static final String TAG = "ExampleActivity";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
        // The activity is about to be destroyed.
    }
}

重新运行应用,这时候我们就可以观察logcat中的打印日志来了解Activity的生命周期。以下是当 Activity A 启动 Activity B 时一系列操作的发生顺序:

1、Activity A 的 onPause() 方法执行。
2、Activity B 的 onCreate()、onStart() 和 onResume() 方法依次执行。(Activity B 现在具有用户焦点。)
3、然后,如果 Activity A 在屏幕上不再可见,则其 onStop() 方法执行。

我们可以利用这种可预测的生命周期回调顺序管理从一个 Activity 到另一个 Activity 的信息转变。 例如,如果必须在第一个 Activity 停止时向数据库写入数据,以便下一个 Activity 能够读取该数据,则应在 onPause() 而不是 onStop() 执行期间向数据库写入数据。

二、Activity的启动模式

Activity的启动模式一共有四种,我们应该根据特定的需求为每个Activity指定恰当的启动模式。这4种模式分别为standard、singleTop、singleTask、singleInstance,可以在AndroidManifest中通过给<Activity>标签指定android:launchMode属性来选择启动模式。

1、standard

2、singleTop

3、singleTask

4、singleInstance

前面3个非常容易理解,第四个有一些特殊,需要多花一点功夫来理解。singleInstance模式的Activity会启动一个新的返回栈来管理这个Activity。想象以下场景, 假设我们的程序中有一个活动是允许其他程序调用的, 如果我们想实现其他程序和我们的程序可以共享这个活动的实例, 应该如何实现呢?使用前面3种启动模式肯定是做不到的, 因为每个应用程序都会有自己的返回栈, 同一个活动在不同的返回栈中人栈时必然是创建了新的实例。而使用 singleInstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动, 不管是哪个应用程序来访问这个活动, 都共用的同一个返回栈, 也就解决了共享活动实例的问题。

实际举个例子
Activity1和Activity3都是standard模式,Activity2是singleInstance模式。
我们从Activity1中启动Activity2,然后在Activity2中启动Activity3。请先想象一下目前为止返回栈中的样子,因为Activity2采用了singleInstance模式,它新启动一个返回栈来管理自己,如果我们添加了taskID的log,就能发现Activity2的taskID是不同于Activity1和Activity3的。接着我们实验一下,在Activity3中按下back键后,居然直接从Activity3返回到了Activity1,再次按下back键又会返回Activity2,再按back键才会退出。这里面原理很简单,由于Activity1和Activity3是放在同一个返回栈中的,所以当在Activity3按下back键,Activity3就会出栈,Activity1回到栈顶,因此也就出现了直接从Activity3返回到了Activity1的情况。在Activity1中按下back键,这时返回栈已经空了,于是就显示另一个返回栈的栈顶活动,即Activity2。

借用郭神的图

三、Activity的实践

1、记录当前的Activity

业务很复杂,代码又不是自己写的时候,我们想要修改一点界面的东西会很难找当前是哪一个Activity。这时候我们可以使用一些技巧来解决这个问题。

在之前的项目基础上进行修改,创建一个BaseActivity。这里的BaseActivity和普通的Activity不一样,因为我们不需要他在Manifest中注册。所以创建一个java类,然后继承AppCompatActivity就可以。

public class BaseActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("BaseAvtivity", getClass().getSimpleName());
    }
}

我们在onCreate()方法中获取了当前实例的类名,并通过log打印出来。修改其他的Activity的继承结构,让他们不再继承AppCompatActivity,而是继承自BaseActivity。由于BaseActivity又是继承自AppCompatActivity,所以项目中其他的Activity的现有功能并不受影响,它们仍然全完继承了Activity中的所有特性。

重新运行应用,这是运行到哪个Activity,该Activity的类名就会被打印出来。

2、随时随地退出应用

以前面的项目为例,当我们在Activity3的时候,想要退出应用需要连续按3下back键才能退出应用,这样显得很繁琐,我们需要一个随地随地能退出应用的方案。

这里,我们可以用一个专门的集合类对所有的Activity进行管理就可以了。

新建一个ActivityCollector作为轰动管理器:

public class ActivityCollector {
    public static List<Activity> activities = new ArrayList<>();

    public static void addActivity(Activity activity){
        activities.add(activity);
    }

    public static void removeActivity(Activity activity){
        activities.remove(activity);
    }

    public static void finishAll(){
        for (Activity activity : activities){
            if (!activity.isFinishing()){
                activity.finish();
            }
        }
    }
}

在Activity管理器中,我们通过一个List暂存Activities,然后提供一个addActivity
()方法用于想List中添加Activity;提供一个removeActivity()方法用于从List中移除Activity;最后提供一个finishAll()方法用于将List中的所有Activity全部销毁掉。

接下来我们修改BaseActivity中的代码:

public class BaseActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("BaseAvtivity", getClass().getSimpleName());
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

在BaseActivity的onCreate()方法中调用了ActivityCollector的addActivity()方法,表示将当前的Activity添加到活动管理器中。然后在onDestroy()中调用ActivityCollector的removeActivity()方法,表示将一个马上要销毁的活动从活动管理器中移除。

现在,不管想要从哪个地方退出,只要调用ActivityCollector的finishAll()方法就可以了。

3、启动Activity的另一种写法

之前我们启动Activity,都是通过构建Intent,然后调用startActivity()或者startActivityForResult()来启动Activity。如果有数据要传递,也可以借助Intent完成。但是当别人需要启动我们开发的Activity时,并不清楚启动咱们的Activity需要传递哪些数据,所以要么自己看代码,要么过来问,而显然这两种方式都不太现实。其实我们只要换一种写法就可以轻松解决上面的窘境。

public class NewsContentActivity extends BaseActivity {

    public static void actionStart(Context context, String newsTitile, String newsContent) {
        Intent intent = new Intent(context, NewsContentActivity.class);
        intent.putExtra("news Title", newsTitile);
        intent.putExtra("news Content", newsContent);
        context.startActivity(intent);
    }

我们在NewsContentActivity中添加了一个actionStart()方法,这个方法中完成了Intent的构建,另外所有NewsContentActivity中需要的数据都是通过actionStart()方法的参数传递过来的,然后把他们存储到Intent中,最后调用startActivity()方法启动NewsContentActivity。

这样写的好处就是一目了然,NewsContentActivity所需要的数据在方法actionStart()中全部体现了出来。这样即使不了解内部的逻辑,不需要看代码,别人也能清晰的知道启动NewsContentActivity需要哪些数据。另外还简化了启动Activity的代码,现在只需要一行代码就可以启动NewsContentActivity了。

btn1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        NewsContentActivity.actionStart(MainActivity.this, "news Title", "news Conten");
    }
});

写代码需要养成良好的习惯。。。Activity本身的初探就先告一段落,不管是理论型还是实践型我们都已经涉及了。从Activity的基本用法,到启动Activity和传递数据的方式,再到Activity的生命周期,以及Activity的启动模式。最后我们还学习了一些实战的小技巧,感觉很满足。

关注获取更多内容
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • Day1: 在代码中通过R.string.hello_world可以获得该字符串的引用; 在XML中通过@stri...
    冰凝雪国阅读 1,403评论 0 5
  • 【快来开启AR捉猫的虚拟奇幻之旅~】,复制这条信息¥5q3903iYZ34¥后打开手机淘宝
    zolipi阅读 255评论 0 0
  • 面容矍铄思路广,满面笑容诉腰伤。 虽是本生粤籍人,国语对话却流畅。 皮包骨头双膝残,神情干练不紧张。 寿星术中巧配...
    徐一村阅读 282评论 1 5