Activity深入浅出全解

1、activity的生命周期

先看一下官方的图

1.1 正常情况下的生命周期

  • onCreate():表示activity刚开始被创建,在这个方法中可以做一些初始化的工作,例如调用setContentView去加载布局资源、初始化Activity所需要的数据等。
  • onStart():表示Activity正在被启动,这时activity已经处于可见状态,但是还没有处于前台,无法和用户进行交互,其实已经显示出来,我们看不到而已。
  • onResume():表示activity已经可见了,位于前台并开始活动。onStart()和onResume()的区别就是,前者位于后台,后者位于前台。
  • onPause():表示Activity正在停止,此时可以做一些存储数据、停止动画的操作,但是不能太耗时,因为新的Activity的显示,onPause()必须先执行完,才能执行新activity的onResume
  • onStop():表示activity即将停止,可以做一些重量级的操作,但是同样不能太耗时。
  • onDestroy():表示Activity即将被销毁,可以做资源回收和释放的工作。

(1) 针对一个特定的Activity,第一次启动

           onCreate  --->    onStart  --  >  onResume  

 (2)用户打开新的activity或者切换到桌面的时候:

         onPasuse   ---> onStop    当然有一种情况,如果新的activity是透明主题,就不会调用onStop

 (3)用户再次回到原activity

          onRestart   --- >   onStart  ---> onResume

 (4)用户按下back建回退的时候:

         onPause  --->  onStop  ----> onDestroy      

(5)  从activity的整个生命周期来看,onCreate()和onDestroy()是成对出现的。代表activity的创建和销毁,只会调用一次。onStart和onStop,表示是否可见,也是成对出现,可能被调用多次;onResume和onPause,表示是否位于前台,是成对出现,可能被调用多次。

1.2 异常情况下的生命周期

    Activity除了受到用户操作所导致正常生命周期方法调度,还有一些异常的情况,比如当资源相关的系统配置发生改变以及系统资源不足时,activity就会被杀死。

资源相关的系统配置发生了改变导致Activity被杀死并且重新创建

         资源的资源加载机制,系统会根据当前的设备情况去加载合适的Resources资源,例如横竖屏下拿到资源不一样。如果屏幕发生了旋转,由于系统资源发生了变化,默认情况下,Activity会重建。

       当系统配置发生变化后,activity被销毁,执行onPause--onStop--onDestroy,由于Activity是在异常情况下被销毁的,系统会调用onSaveInstanceState来保存当前的状态,这个方法调用在onStop之前,和onPause没有时序关系。正常生命周期下,不会调用该方法。

       当activity被重新创建之后,系统会调用onRestoreInstanceState方法,并将onSaveInstanceState方法保存的Bundle对象作为参数传递给onRestoreInstanceState和onCreate方法。因而,我们可以通过onRestoreInstanceState和onCreate来判断Activity是否被重建了,如果被重建,我们可以取出当前的数据并恢复,时序上,onRestoreInstanceState在onStart之后。

        系统配置发生了改变,可不可以不创建activity,那就给Activity指定configChanges属性,添加orientation | keyboardHidden | screenSize属性值 。configChanges属性的属性值如下:

资源内存不足导致低优先级的Activity被杀死

先看一下Activity优先级的高低

(1)前台Activity,正在和用户交互的activity,优先级最高。

(2)可见但非前台Activity,例如activity中弹出一个对话框,导致activity可见但是不能进行交互

(3)后台activity,已经被暂停的activity,执行了onstop,优先级最低。    

        当系统空间不足的时候,系统优先级的高低去杀死目标activity所在的进程,并且在后续通过onSaveInstanceState和onRestoreInstanceState来保存和恢复数据。如果一个进程没有四大组件,那么这个进程会快就会被杀死,因为一些后台任务不能脱离四大组件单独工作,比较好的方法就是将后台工作放在Service中从而保证进程有一定的优先级,这样就不会被轻易杀死。

2、Activity的启动模式

       首先我们需要明确一个概念:Activity的任务栈:首先要了解TaskAffinity参数,这个参数翻译为任务相关性,标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需要的任务栈的名字为应用的包名。当然我们也可以为每个任务栈都单独的指定TaskAffinity属性,这个属性不能和包名一致,否者就相当于没有指定。 TaskAffinity属性主要和singletask启动模式或者和allowTaskReparenting属性配对使用,在其他情况下,没有意义。此外,任务栈可以分为前台任务栈和后台任务栈,处于后台任务栈的Activity处于暂停状态,用户可以通过切换将后台任务再次调到前台任务。

        当TaskAffinity和singleTask启动模式配对使用的时候,它是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。

         当TaskAffinity和allowTaskReparenting结合的时候,这种情况较为抽象,当应用A启动了应用B的Activity,如果这个Activity的allowTaskReparenting属性为true的时候,那么当B被启动后,此Activity会直接从应用A的任务栈转移到应用B的任务栈。例如:A启动B的Activity C,然后Home键返回桌面,然后点击B的图标,这时并不是启动了B的主Activity,而是重新显示了已经被A启动的C,也就是C从A的任务栈转移到B的任务栈。原因是因为A启动了C,C只能处于A的任务栈中,但是C是属于B的,正常情况下,C的TaskAffinity值肯定和A的任务栈不同(包名都不一样),所以当B被启动了之后,B会创建自己的任务栈,这是系统就会发现C想要的任务栈存在了,就会把C移动到B的任务栈中。

 2.1 activity的四种启动模式

(1)Standard模式:标准模式。这是系统默认的模式,每次启动activity都会创建一个新的实例,不管这个实例是否已经存在。一个任务栈可以有多个实例,每个实例也可以属于不同的任务栈。在标准模式下,谁启动当前的activity,那么当前的activity就会在谁的任务栈中。A启动B(标准模式),那么A就会在B的任务栈中。 有一种情况,当我们使用Applicationcontext去启动standard模式的Activity就会报错。原因是因为, Applicationcontext不是Activity类型的,没有任务栈,那肯定就会存在问题,解决这个问题就是需要为当前被启动的Activity指定FLAG_ACTIVITY_NEW_TASK 标记,这样启动时候就会为它创建一个任务栈,其中这个时候待启动的Activity实际上是以singleTask模式启动的。

(2)singleTop:栈顶复用模式,这种模式下,如果Activity位于任务栈的栈顶,那么启动该Activity不会被重建,同时它的onNewIntent方法会被调用,通过该方法可以取出当前请求的信息。这个Activity的onCreate、onStart不会被调用,因为它并没有发生改变。但是如果待启动的activity不处于栈顶,就还是需要重新创建一个新的activity的。例如:栈ABCD,A处于栈底,启动D,并且D的启动模式是singleTop,那么D启动完了之后,栈内还是ABCD。如果启动B(singleTOP),完成之后,栈ABCDB。

(3)singleTask:栈内复用,这种模式下,只要Activity在一个栈中存在,那么多次启动此Activity都不会重建实例,只会调用onNewIntent。例如:Activity A启动,系统首先寻找是否有A想要的任务栈,没有,就创建一个,然后创建一个A的实例,并将其放到刚创建的任务栈中;如果存在A需要任务栈,就看任务栈中是否有A实例存在,存在就把A拿到栈顶,调用onNewIntent,不存在就创建新的A实例,压到A的栈顶。

(4)singleInstance:单实例模式,这是一种加强的singletask模式,在该模式下,被启动的Activity只能单独的存在一个任务栈中。例如:Activity A(singleInsatance)启动后,系统会为它创建一个新的任务栈,然后将A单独的放在这个任务栈中,由于是栈内复用,后续在启动A,都不会创建新的Activity,处于这个单独的任务栈被销毁了。

2.2 Activity中的Flags(标志位)

      Activity 中的Flags(标志位)有很多,我们主要介绍常用的几个

FALG_ACTIVITY_NEW_TASK 

    这个标志位指定Activity的启动模式为“singleTask”,效果和在menifest一致。

FLAG_ACTIVITY_SINGLE_TOP

    这个标记位的作用是为Activity启动模式指定“singleTop”,效果和在menifest一致。

FLAG_ACTIVITY_CLEAR_TOP

     这个标记代表,具有此标志位的Activity,当它启动的时候,在同一个任务栈中,位于它上面的Activity都要出栈。这个模式一般和FALG_ACTIVITY_NEW_TASK 配合使用,在这种情况下,被启动的Activity如果已经存在,那么系统就会调用它的onNewInsatnce。如果被启动的Activity采用的是standard模式,那么它连同它之上的Activity都要出栈,然后系统新建一个Activity放入栈顶。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

      具有这个标志位的Activity不会出现在历史Activity的列表中,当某些情况下不希望用户通过列表回到当前的Activity时,可以设置这个标记。等于在manifest中设置Android:excludeFromRecents="true"。

2.3  给Activity指定启动模式

给Activity指定启动模式有两种方法:

(1)通过AndroidMenifest为Activity指定启动模式:

 <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

(2)通过Intent中设置标志位来为Activity指定启动模式

var intent = Intent()
        intent.setClass(this,MainActivity::class.java)
        intent.putExtra("time",System.currentTimeMillis())
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        b.setOnClickListener {
            startActivity(intent)
        }

3 IntentFilter的匹配规则

          Activity的启动方式分为:显示调用和隐示调用。显示调用需要明确指定被启动对象的组件信息,包括类名、包名;隐示调用则不需要。原则上一个Activity不应该既有显示又有隐示,如果有以显示为主。

         隐示调用需要Intent能够匹配目标组件的IntentFilter中设置的过滤信息,否则不能启动activity。IntentFilter中的过滤信息有action、category、data。

<activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.MAIN1" />

                <category android:name="android.intent.category.LAUNCHER" />
                <category android:name="android.intent.category.LAUNCHER1" />

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

         如上述代码所示,为了匹配过滤列表,需要同时匹配过滤列表中action、category、data信息,否则匹配失败。一个过滤列表中的action、category、data可以有多个,所有的action、category、data分别构成不同的类别,同一类别的信息共同约束当前类别的匹配过程、只有一个intent同时匹配action类别、category类别、data类别才算完全匹配,只有完全匹配才能成功启动activity。此外,一个activity中可以有多个IntentFilter,一个intent只要能匹配其中一个,就可以成功启动activity。

(1)action的匹配规则

         action是一个字符串,系统预定义了一些action,同时我们也可以自定义action。action的匹配规则是必须能够和过滤规则中的action的字符串完全一致(区别大小写)。一个过滤规则中有多个action,只需要和其中一个匹配成功即可。

(2)category的匹配规则

         category是一个字符串,系统预定义了一些category。同时我们也可以自定义category。cate的匹配规则是:如果intent中出现category,不管有几个,每一个都必须在intentFilter中已经定义。换句话说,intent中可以没有category,如果有,那么有的必须已经在intenFilter 中定义了。其中可以没有的原因是系统会在启动activity的时候,为其添加一条默认的。

(3)data的匹配规则

    先看一下data的语法。

 <data android:scheme="http"
                      android:host="www.baidu.com"
                      android:port="80"
                      android:pathPattern="/search"
                      android:pathPrefix="/info"
                      android:mimeType="image/jpeg"/>

   data的结构分为两部分:mineType和URI,mimeType指的媒体类型,比如image/jpeg、audio/mpeg4-genric和vido/*,可以表示图片、文本、视频等不同的媒体格式,而URI的结构如下:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]

例如:

http://www.baidu.com:80/search/info

其中:

scheme:URI的模式,比如http、Content、file等。如果没有指定,那么整个URI无效。

host:URI的主机名,例如www.baidu.com,如果没有指定,整个URI也无效。

Port:端口号,例如80 ,只有指定了scheme和host才有效。

path、pathPrefix、pathPattern:表示路径的信息的参数。path表示完整的路径信息,pathPattern页表示完整的路径信息,可以包含通配符“*”,“*”表示0个或者多个字符。

data的匹配规则和action类似,要求intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。

<intent-filter>
                <data android:mimeType="image/*"/>
                ..
            </intent-filter>

这种规则指定了媒体类型为所有类型的图片,那么intent中的mimeType属性必须为“image/*”才能匹配,这种情况下,虽然没指定URI,但是却有默认值,URI的默认值为Content和file。因此intent中的URL的schema部分必须为content或者file才能匹配。

intent.setDataAndType(Uri.parse("file://abc"),"image/png")

 又比如:

 <intent-filter>
                <data android:mimeType="image/mpeg"
                    android:scheme="http"/>
                <data android:mimeType="video/mpeg"
                    android:scheme="http"/>
            </intent-filter>

这种定义了两种data规则,并且每个data都指定了完整的属性值,既有URI也有mimeType,为了匹配上述规则

intent.setDataAndType(Uri.parse("http://abc"),"video/png")

或者

intent.setDataAndType(Uri.parse("http://abc"),"audio/png")

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值