Activity的生命周期和启动

Activity的生命周期和启动方式与流程

从安卓入门到现在,从事安卓开发已经有三年有余的时间了,对安卓开发的经验在逐步积累,解决问题的思路更加清晰,方式也越来越广泛。安卓最重要的就是四大组件,分别是Activity、Service、Receiver和ContentProvider。其中我们用到的最广泛的就是Activity,基本上每天都要使用,所以本章文章我将对activity进行一次梳理。其实我也是在阅读了任玉刚的《Android开发艺术探索》才对Activity有了更深一点的了解。

  • activity的生命周期
  • activity的启动模式
  • activity的隐式启动和显示启动
  • activity的启动流程

 

  • activity的生命周期

Activity的生命周期和启动_第1张图片

上图为activity的正常生命周期过程,但是在具体的开发过程中activity会异常关闭或则销毁,那么这个时候需要对当前activity的状态进行存储,当再次恢复或者创建的时候可以根据存储的状态值进行界面复原。

activity异常销毁一般情况有两种情况:

1.资源相关的系统配置发生改变,那么activity会被销毁或者重建。

例如手机原本为竖屏,结果突然转为横屏之后,那么activity就需要重新加载资源,因为我们的应用有drawable-mdpi、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi、drawable-land等目录,原本为竖屏时加载的是drawable-mdpi、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi目录里面的资源,当它横屏时需要加载drawable-land里面的资源。此时activity就会销毁被重建。

针对上述情况,如果我们不想在系统配置发生改变时activity并销毁重新加载,则可以在AndroidManifest.xml文件注册某个Activity的里面加上android:configChanges="orientation|keyboardHidden"。这样一来在系统配置发生改变时如横竖屏切换不会销毁activity。当然系统配置的选项有很多,可以用"|"符号连接,下面我将会展示任玉刚老师的做的视图来说明有哪些。

Activity的生命周期和启动_第2张图片

  

2.资源不足导致优先级低的activity被杀死

优先级比较:前台正在与用户交互的activity>可见但是不位于前台,不能与用户交互的activity(例如当弹出对话框时,我们可以看见activity但是不能点击activity上面的控件,只能与dialog交互)>位于后台又不可见的activity。

当系统内存不足的时候就会先回收优先级比较低的activity,那些低优先级activity就会被销毁。

那么当碰到上述异常情况,activity被销毁被重新创建时,我们可以做什么呢?当activity被销毁时,会调用onSaveInstanceState()方法,在该方法里面可以用bundle来存储当前activity的数据。所以我们可以重写onSaveInstanceState()方法实现数据存储。具体实现如下

    @Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        outState.putInt("position",1);
        outState.putCharSequence("obj","1111111111111111");
        super.onSaveInstanceState(outState, outPersistentState);
    }
}

这样数据就会被存储在outState对象里面,当我们重建activity时在onCreate方法或者onRestoreInstanceState()方法中可以获取到bundle,从而提取出里面存取的数据,做一些恢复界面的操作。再次提醒一句onCreate方法里面的bundle对象可能为空,因为正常创建activity也会调用该方法,但是并没有数据,但是onRestoreInstanceState一旦被调用里面的bundle对象一定有值,不会为空,所以在onRestoreInstanceState()里面进行数据恢复比较保险。

Activity的生命周期和启动_第3张图片

  • activity的启动模式

   activity有四种启动模式分别为standard(标准模式)、singleTop(栈顶复用模式)、singleTask(栈内复用模式)、singleInstance(单例模式)。

standard模式:每次启动一个activity都会重新创加一个activity,并把它压入一个栈中,这样的activity不会复用。若有栈S,里面有ABC三个activity,此时启动B这个activity,则栈内为ABCB.

singleTop模式:如果栈内存在要启动的activity并且该activity在栈顶则不再重新创建,直接使用栈顶activity,否则重新创建一个activity实例。若有栈,栈内有ADBC几个activity,C位于栈顶。先要启动D这个Activity,则创建D这个Activity实例,压入栈中,此时栈为ADCBD。现在又需要启动一个D的Activity,则不再创建D,直接使用栈顶的D,此时栈的活动页为ADCD.同时会调用D这个Activity的onNewIntent()方法。

singleTask模式:如果栈内有要启动的Activity,则会把该activity上面所有的activity出栈,使该activity置于栈顶。并且调用该Activity的onNewIntent()方法,若没有该Activity,则重新创建一个栈,并且创建该activity的实例,压入该栈中。若有栈S1,里面有ABCD四个Activity,其中B为singleTask模式,现在若要启动Activity B,则将Activity C和D出栈,则此时栈中有Activity A B,并调用B的onNewIntent().若有栈S1,里面有ACD三个Activity,其中B为singleTask模式,现在若要启动Activity B,则将会重新创建一个栈S2,并创建Activity B压入S2栈中。

singleInstance模式:如果一个Activity拥有这个模式的话,那么这个Activity在启动的时候会为他专门创建一个栈,把该Activity放进去,也就是每个这样的Activity都有一个自己的栈,一旦该Activity创建实例就不会被销毁,无需再次创建,直到这个栈被销毁。若有栈S1,里面有CDE.现在启动了A,A为singleInstance模式,则会为A建立一个栈S2,下一次启动A无需再次创建直接使用S2里面的Activity A.现在再次启动了B,B为singleInstance模式,则会为B建立一个栈S3,下一次启动B无需再次创建直接使用S3里面的Activity B。

除了上述可以为Activity设定启动模式,也是可以用在Java代码中用flag模式设定他的启动模式。并且用flag方式设定的启动模式优先级高于在Androidmanifest.xml中设定的。下面我们介绍一下flag如何设立启动模式。我们使用flag的时候常用有以下四种

 Intent intent=new Intent(ExampleViewActivity.this,ExampleViewActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//第一种
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);//第二种
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);//第三种
        intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);//第四种
        startActivity(intent);

FLAG_ACTIVITY_NEW_TASK    它的功能和singleTask一样的

FLAG_ACTIVITY_SINGLE_TOP 它的功能和singleTop一样的

FLAG_ACTIVITY_CLEAR_TOP  它一般是和FLAG_ACTIVITY_NEW_TASK使用,如何被启动的Activity A采用的是standard模式,那么如果栈中存在Activity A,则Activity A和他之上的所有Activity都要出栈,重新创建Activity A实例运行。

FLAG_ACTIVITY_NEW_TASK    它的功能是被启动的Activity不会出现在历史列表中,这样当我们通过历史列表返回的时候不会返回到该Activity。这个属性和在AndroidManifest.xml写android:excludeFromRecents="true".

  • activity的隐式启动和显示启动

     android的启动方式有两种,一个隐式启动,一个是显示启动。显示启动比较简单,需要明确的写明启动对象的组件信息包括包名和类名。一般来说一个intent的启动不能同时存在隐式启动和显示启动,如果同时存在则以显示为主。显示比较简单,就不多说了。下面就说说隐式启动。

     
            

            
            
            

            
            
            

            
            
            
                

                
                

                
                
                
                
            
        






   Intent intent=new Intent();
        intent.setAction("com.example.action.a_1");
        intent.addCategory("com.example.category.a_1");
        intent.setDataAndType(Uri.parse("file://abc"),"text/plain");
        startActivity(intent);

      隐式启动的时候没有明确指定启动组件的包名和类名,但是intent可以设置action,category,和data,其中action是必须要设置的,另外两个是可选的,这三个都可以设置多个值。intent会与IntentFilter对应然后进行过滤,一个activity可以有多个IntentFilter,但是intent主要和其中的一个IntentFilter完全匹配,那么他们就匹配成功了。

    action是一个字符串,我们可以用系统自带的action,也可以自定义一些action,action区分大小写。intent中的action一定要在intentfilter中找到一条一样的,这条action才算匹配成功。也就是说intentFilter的action数量一定大于等于intent定义的action数量。注意intent中一定含有一条action。

   category是一个字符串,  我们可以用系统自带的category,也可以自定义一些category。intent中的category一定要在intentfilter中找到一条一样的,这条category才算匹配成功。也就是说intentFilter的category数量一定大于等于intent定义的category数量。注意intent中可以不定义category是,系统会默认一条给intent设置“android.intent.category.DEFAULT”,所以隐式启动的activity,在注册的intentFilter里面一定要加上一条“android.intent.category.DEFAULT”。

   data由两部分组成,mimeType和URL。mimeType指的是媒体类型 ,image/jpeg,audio/mpeg4-generic和video/*,可以表示图片,文本,和音频等不同格式。而URL包含的数据就比较多了。

URL的结构:://:/[||]

列举的URL例子
1、http://www.processon.com:80/diagraming/5b9f37b9e4b0fe81b63962a
2、content://com.example/project:200/folder/image/data
3、file://card/download/image/product

scheme,host是必须要写的,port是在前两者都写的基础上才有意义。而Path,pathPattern和pathPrefix是平级的,前两者代表的意义都是一样的,写其中一个就行。pathPattern可以包含*,*为通配符,可以代表任意字符。pathPrefix表示为前缀
 

  • activity的启动流程

    activity的启动流程比较复杂,涉及到binder,binder的主要作用是可以连接两个进程,使两个进程进行跨进程通信。binder机制比较复杂,我也是浅浅的了解了一点,在这里我简单的描述一下activity的启动流程,后续我会继续跟踪并深层一点的学习binder并进行解说。

   当我们点击桌面应用的时候,会通过AMS发送开启创建activity的请求,然后ams会检查应用进程是否存在,如果不存在的话,ams会发出请求创建一个app进程。当app进程创建之后,会通过 ActivityManagerNative.getDefault();获取ams在客户端的对应的binder的代理,然后通过binder向ams发送创建activity的请求,ams启动activity并通过appThread在服务端的binder的代理,像客户端发送请求,客户端接受请求,通过Handler向主线程发送消息启动activity。调用onCreate();这里说的比较简单,在后面我会做详尽的叙述。

本篇文章主要是对任玉刚老师的《Android开发艺术探索》第一章的总结和感悟,这本书个人认为写的非常好,可以仔细学习。

若有不足之处,请谅解!

可以给个赞哦!!

   

 

你可能感兴趣的:(技术,Android)