Android基础——四大组件之Activity

说到Android开发,大部分同学接触到的第一个东西就是Activity,网上有海量的关于Activity的资料,我会在本文中尽量的总结Activity的相关知识。

- Activity是什么?


众所周知,Activity是Android四大组件之一,另外三个组件分别是Service(服务),Content Provider(内容提供者),BroadcastReceiver(广播接收器)。

它们4个在Android当中各司其职,而本文的主角Activity的作用——通常是一个用户可以见到、并且能进行交互操作的单独屏幕(窗口)。下面引用Google官方对Activity的解释:

An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View).

有道词典翻译:
Activity是一个单一的、集中,用户可以做的事情。几乎所有的活动与用户交互,所以Activity类负责创建一个窗口,你可以将你的UI setContentView(视图)。

别人的翻译:
Activity是独立平等的,用来处理用户操作。几乎所有的Activity都是用来和用户交互的,所以Activity类会创建了一个窗口,开发者可以通过setContentView(View)的接口把UI放到给窗口上。

更深入一些的解释(以下为复制粘贴并进行微量修改,原文位置会贴在最后):

Android中的activity全都归属于task管理 。task 是多个 activity 的集合,这些 activity 按照启动顺序排队存入一个栈(即“back stack”)。android默认会为每个App维持一个task来存放该App的所有activity,task的默认name为该app的packagename。

当然我们也可以在AndroidMainfest.xml中申明activity的taskAffinity属性来自定义task,但不建议使用,如果其他app也申明相同的task,它就有可能启动到你的activity,带来各种安全问题(比如拿到你的Intent)。

系统通过堆栈来管理activity,当一个新的activity开始时,它被放置在堆栈的顶部和成为运行活动,以前的activity始终保持低于它在堆栈,而不会再次到达前台,直到新的活动退出。

- Activity的生命周期


一个Activity的生命周期就像一个人的一生,从最开始的出生(starts/create)……工作(running)……休息(stop)……直到最后的死亡(shut down)……或许这样的比喻不恰当,下面看一张Google官方关于Activity生命周期的图:

Activity的生命周期

从图中可以看出一个Activity从出生到死亡至少需要经历6个阶段:
onCreate、onStart、onResume、onPause、onStop、onDestory
那么,Activity是在什么时候进行哪一个阶段呢?我们来写一个简单的Demo(例子)看看,先让我们看看如何使用Activity。

- Activity生命周期的触发时机


测试环境为Android 5.0系统版本,某不知名小厂手机进行的真机测试。为了方便测试,我们建立两个Activity,分别命名为FirstActivity和SecondActivity(下面以A1和A2简称),并重写两个Activity类的所有生命周期方法,在方法中打印Log日志。

A1中的代码:

public class FirstActivity extends Activity {

    private static final String TAG = "FirstActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_first);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });

        Log.e(TAG, "onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG,"onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e(TAG,"onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e(TAG,"onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.e(TAG,"onStop");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.e(TAG,"onRestart");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG,"onDestroy");
    }
}

A2中的代码:

public class SecondActivity extends Activity {

    private static final String TAG = "SecondActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        Log.e(TAG, "onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e(TAG, "onResume");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.e(TAG, "onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.e(TAG, "onStop");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.e(TAG, "onRestart");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy");
    }

}

别忘了在manifest.xml文件中声明Activity:

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".FirstActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".SecondActivity" />

    </application>

这样准备工作就做好了,然后根据不同的操作,看看log日志(生命周期)的变化:

1.启动APP到显示界面的操作的日志:

不进行其他操作

结论:Activity从启动到展示出界面,Activity的生命周期经历了onCreate->onStart->onResume,然后就开始running了。

2.由竖屏切换到横屏(或反之):

横竖屏切换

结论:在进行屏幕切换时,Activity的生命周期经历了onPause->onStop->onDestroy->onCreate->onStart->onResume。什么意思呢?意思就是在横竖屏切换时实际上是Activity销毁了再重新加载了一次!

3.按返回键(back)或执行finish()方法:

back or finish

结论:Actvity的生命周期执行了onPause->onStop->onDestroy,意味着该Activity被销毁了。

4.按home键,并再次进入APP(和锁屏,再解锁的情况):

home

结论:按下home键返回主界面的时候(锁屏时),Actvity的生命周期执行了onPause->onStop,再次进入APP的时候(解锁后),执行了onRestart->onStart->onResume。从看到的生命周期执行情况看来,这个Activity并没有被销毁。

这里有一些特殊,大致分为以下三种情况讨论:

①用户返回到该Activity就调用onResume()方法重新running。

②用户回到桌面或是打开其他Activity,就会调用onStop进入停止状态(保留所有的状态和成员信息,但是用户看不见)。

③系统内存不足,拥有更高限权的应用需要内存,那么该Activity的进程就可能会被系统回收。(回收onRause和onStop状态的Activity进程)要想重新打开就必须重新创建一遍。

5.由A1跳转到A2:

跳转

结论:A1的生命周期——onPause->onStop,这意味着跳转时A1并没有被摧毁;A2的生命周期——onCreate->onStart->onResume。

6.A2执行finish()方法:

finish

结论:A1的生命周期——onRestart->onStart->onResume,这证实了上面说的跳转时A1并没有被摧毁;A2的生命周期——onPause->onStop->onDestroy。

关于以上几种常见的情况,已有很多前辈进行过总结,我这边直接贴过来了:

  • Activity的完整生存期会在 onCreate() 调用和 onDestroy() 调用之间发生。

  • Activity的可见生存期会在 onStart() 调用和 onStop() 调用之间发生。系统会在Activity的整个生存期内多次调用 onStart() 和onStop(), 因为Activity可能会在显示和隐藏之间不断地来回切换。

  • Activity的前后台切换会在 onResume() 调用和 onPause() 之间发生。 因为这个状态可能会经常发生转换,为了避免切换迟缓引起的用户等待,这两个方法中的代码应该相当地轻量化。 

说到这里可以简单的提一下,在Activity当中关于保存状态(数据)的方法,直接贴代码解释更清晰一些:

    private static final String TAG = "FirstActivity";
    private String data;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        //判断是否有保存的数据
        if (savedInstanceState != null) {
            data = (String) savedInstanceState.get("key");
        }
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
        Log.e(TAG, "onCreate");
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        //保存可能被回收的数据
        Bundle bundle = new Bundle();
        bundle.putString("key", data);
        outState.putAll(bundle);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        //判断是否有保存的数据
        if (savedInstanceState != null) {
            data = (String) savedInstanceState.get("key");
        }
        super.onRestoreInstanceState(savedInstanceState);
    }

关于上面的代码出现的onSaveInstanceState和onRestoreInstanceState方法,大神是这么解释的:

  • onSaveInstanceState方法,是在Activity可能被回收之前调用,用来保存自己的状态和信息,以便回收后重建时恢复数据(在onCreate()或onRestoreInstanceState()中恢复)。旋转屏幕重建Activity时会调用该方法,但其他情况在onRause()和onStop()状态的Activity不一定会调用 ,也就是说,系统灵活的来决定调不调用该方法,但是如果要调用就一定发生在onStop方法之前,但并不保证发生在onPause的前面还是后面

  • onRestoreInstanceState方法,在onStart 和 onPostCreate之间调用,在onCreate中也可以状态恢复,但有时候需要所有布局初始化完成后再恢复状态。刚刚说到的onPostCreate方法一般不实现,当程序的代码开始运行时,它调用系统做最后的初始化工作。

- Activity的启动模式


或许细心的同学会注意到,我们在manifest.xml文件声明activity的时候,有许多个属性可以设置。但是由于属性太多,这里就不一一做解释了。
我们主要讲讲启动模式这个属性,启动模式属性在< activity />节点中为名为launchMode,这个属性的默认值为standard。下面我们先解释一下这个属性的4个值的意思:

为了更形象的说明(或者说是我自己的理解),下面我用一个名为a的A对象(代表Activity)和一个名为isTop的boolean方法(代表是否在栈顶)来演示

  • standard:刚刚我们也说了,如果没有手动设置启动模式的话,Activity的默认启动模式就是它。当通过这种模式来启动Activity时, Android总会为目标Activity创建一个新的实例,并将该Activity添加到当前Task栈中。这种方式不会启动新的Task,只是将新的Activity添加到原有的Task中。

    我的理解:
    //每次都new一个
    retrun new A();
    
  • singleTop:该模式和standard模式基本一致,但有一点不同——当将要被启动的Activity已经位于Task栈顶时,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。

    我的理解:
    //如果不为空,并且在栈顶,那么直接复用
    if(a != null && a.isTop()){
            retrun a;
    }else{
    //如果为空,或者不在栈顶,就new一个
            retrun new A();
    }
    
  • singleTask:以这种模式启动时,Activity在同一个Task内只有一个实例。如果将要启动的Activity不存在,那么系统将会创建该实例,并将其加入Task栈顶;如果将要启动的Activity已存在,且存在栈顶,直接复用Task栈顶的Activity。如果Activity存在但是没有位于栈顶,那么此时系统会把位于该Activity上面的所有其他Activity全部移出Task,从而使得该目标Activity位于栈顶。

    我的理解:
    //如果为空就new,并且放到栈顶
    if(a==null){
        a=new A();
        retrun a;
    }else{
        //如果不为空,并且在栈顶,那么直接用
        if(a.isTop()){
            retrun a;
        }else{
        //不为空,但不在栈顶
    
            ……//把该activity之前的其余activity移除
    
            retrun a;
        }
    }
    
  • singleInstance:无论从哪个Task中启动目标Activity,只会创建一个目标Activity实例且会用一个全新的Task栈来装载该Activity实例(全局单例)。如果将要启动的Activity不存在,那么系统将会先创建一个全新的Task,再创建目标Activity实例并将该Activity实例放入此全新的Task中。如果将要启动的Activity已存在,那么无论它位于哪个应用程序,哪个Task中;系统都会把该Activity所在的Task转到前台,从而使该Activity显示出来。

    不好意思,这个我不知道该如何用“我的理解”来进行逻辑判断模式的表达!
    

如何使用?两种方式:

1.直接在manifest.xml的activity标签后方添加属性,如:

 <activity 
            android:launchMode="singleTask"
            android:name=".SecondActivity" />

2.在Intent的时候设置flags,如:

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);

这里说几个Intent.XXX的启动模式:

  • FLAG_ACTIVITY_NEW_TASK:与manifest.xml中的singleTask模式相同,在新的 task 中启动 activity。如果要启动的 activity 已经运行于某 task 中,则那个 task 将调入前台。

  • FLAG_ACTIVITY_SINGLE_TOP:与manifest.xml中的SingleTop模式相同,如果要启动的 activity位于back stack顶,系统不会重新创建目标Activity实例,而是直接复用Task栈顶的Activity。

  • FLAG_ACTIVITY_CLEAR_TOP:此种模式在launchMode中没有对应的属性值。如果要启动的 Activity已经在当前task中运行,则不再启动一个新的实例,且所有在其上面的Activity将被销毁。

总结:在manifest.xml与Intent的启动模式使用方式不同,但使用的场景都可以自行选择。建议除非必须达到业务要求,否则不要轻易的改变Activity和task默认的工作方式

- 关于Activity的几个类


使用过Eclipse和AndroidStudio的同学会发现,使用两个不同的编译器新建项目时默认创建的MainActivity是继承不同的Activity类的——
Eclipse中的MainActivity继承的是Activity。
AndroidStudioEclipse中的MainActivity继承的是AppCompatActivity。

说到这个,我们可以把关于Activity的几个类都列出来一一解释说明:

  1. Activity——Activity的基类,所有有关Activity的类都继承于它。

  2. FragmentActivity——由于在Android3.0版本中更新了Fragment这一大控件,它是为了向下兼容Fragment而推出的类,出自于support v4包,并最终继承于Activity类,在使用了Fragment并需要兼容低版本的情况下可以使用它。

  3. ActionBarActivity(deprecated)——是为了兼容ActionBar而出现的类,现在已经过时。曾经继承自FragmentActivity,后来被AppCompatActivity替换掉(现在继承AppCompatActivity),出自于support v7包。

  4. AppCompatActivity——继承自FragmentActivity,是为了替换掉ActionBarActivity而出现的类,因此同样出自support v7包。由于5.0版本更新了大量内容,其中推出了ToolBar来替换ActionBar,因此ActionBarActivity也被替换掉了,现在已经不推荐使用ActionBar。

总结:由上面的说明可以看出,在不需要兼容低版本的Fragment、ActionBar、ToolBar时直接使用Activity就行;在需要兼容时,使用FragmentActivity;在大部分情况下,请直接使用AppCompatActivity毕竟AS作为Google亲儿子,默认继承的AppCompatActivity是有道理的!


全文总结:Activity是我们Android开发最经常使用的组件,也可以称之为一个App最重要的核心之一,如何熟练的运用好Activity是作为Android开发很关键的一点。最后贴出本文的主要参考原文地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值