美文网首页android基础Android知识Android
安卓四大组件之Activity 以及Activity管理器

安卓四大组件之Activity 以及Activity管理器

作者: 一条废鱼 | 来源:发表于2017-02-09 22:10 被阅读204次

    1.Activity生命周期

    androidheros_activitylife1.png

    先上经典的Activity的生命周期图。

    • onCreate:主要执行初始化工作,比如用setContentView加载布局界面
    • onStart:Activity由不可见变为可见,但是不在前台,不可以与用户进行交互(可见不可交互)
    • onResume:使Activity位于返回栈的顶端,并出现在前台,可与用户进行交互(可见可交互)
    • onRestart:Activity的重启,由不可见变为可见
    • onPause:表明Activity正在停止,正常情况下onStop会紧接着调用。
    • onStop:Activity由部分不可见变为完全不可见,可做一些重量级的回收工作,同样不能很耗时。
    • onDestory:销毁Activity并释放资源。

    Ps:一个经典面试问题两个Activity从A到B,再由B到A,依次经历了哪些生命周期?
    其实这里可以分为两种情况:
    1.当A完全不可见的状态下
    A跳转到B: A onPause > B onCreate > B onStart > B onResume > A onStop
    B跳转到A: B onPause > A onRestart> A onStart > A onResume > B onStop
    2.当A处于部分可见的状态下:
    A跳转到B: A onPause > B onCreate > B onStart > B onResume
    B跳转到A: B onPause > A onStart > A onResume > B onStop
    两者的区别在于onStop 和 onRestart的方法调用,onStop 只有在Activity完全不可见的时候才会调用。Activity由不可见变为可见,onRestart才会调用。
    此外需要注意的是:

    • 当用户打开新的Activity或切换回到桌面的时候,回调为onPause > onStop。若Activity为透明主题时,则不会回调onStop。
    • 当系统内存不足的时候,处于onStop 状态下的Activity会被系统回收。但是系统在回收之前会回调onSaveInstanceState()方法来保存应用的数据Bundle.当该Activity重新创建的时候,保存的Bundle数据就会传递到onRestoreSaveInstanceState方法和onCreate方法中,这就是onCreate方法中Bundle savedInstanceState参数的来源(onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原)。
    onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的。

    onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据。
    onRestoreInstanceState被调用的前提是,activity“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity,这种情况下activity一般不会因为内存的原因被系统销毁,故activity的onRestoreInstanceState方法不会被执行

    Activity被意外杀死

    1. 资源相关的系统配置发生改变导致Activity被杀死并重建

    资源相关的系统配置发生改变,举个栗子。当前Activity处于竖屏状态的时候突然转成横屏,系统配置发生了改变,Activity就会销毁并且重建,其onPause, onStop, onDestory均会被调用。因为是在异常情况下终止的,所以系统会调用onSaveInstanceState来保存当前Activity状态。这个方法是在onStop之前,与onPause没有固定的时序关系。当Activity重建的时候系统会把onSaveInstanceState所保存的Bundle作为对象传递给onRestoreInstanceState和onCreate方法。

    注:

    • View的源码中每个View都有onSaveInstanceState和onRestoreInstanceState这两个方法。
    • 接收位置可以是onRestoreInstanceState和onCreate方法,区别是:onRestoreInstanceState如果被调用,参数Bundle一定是有值的,在onCreate中需要判断参数是否为null。
    • onSaveInstanceState只有在Activity即将销毁并有机会重新显示时才会调用,正常销毁的Activity生命周期中不会调用,比如:旋转屏幕,按Home键,启动新Activity等。
    2. 内存不足导致低优先级的Acitvity被杀死

    Activity优先级

    1. 前台Activity——正在和用户交互的Activity,优先级最高
    2. 可见但非前台Activity——Activity中弹出的对话框导致Activity可见但无法交互
    3. 后台Activity——已经被暂停的Activity,优先级最低系统内存不足是,会按照以上顺序杀死Activity,并通过onSaveInstanceState和onRestoreInstanceState这两个方法来存储和恢复数据。

    不让Activity重新创建的方法

    系统配置有很多内容,当某项改变时,我们不想让Activity重新创建可以在AndroidMainfest中给Activity指定configChanges属性。比如

    android:configChanges="orientation"

    configChanges属性非常多,具体可参考官方文档
    常用的有locale, orientation和keyboardHidden这三个。

    2.Activity四种启动模式以及Stack管理器代码

    应用内的Activity是被任务栈Task来管理的,一个Task中的Activity可以来自不同的应用,同一个应用的Activity也可能不在同一个Task中。默认情况下,任务栈依据栈的后进先出原则管理Activity,但是Activity可以设置一些“特权”打破默认的规则,主要是通过在AndroidManifest文件中的属性android:launchMode或者通过Intent的flag来设置。
    standard:默认的启动模式,该模式下会生成一个新的Activity,同时将该Activity实例压入到栈中(不管该Activity是否已经存在在Task栈中,都是采用new操作)。例如: 栈中顺序是A B C D ,此时D通过Intent跳转到A,那么栈中结构就变成 A B C D A,点击返回按钮的 显示顺序是 D C B A,依次摧毁。

    singleTop:在singleTop模式下,如果当前Activity D位于栈顶,此时通过Intent跳转到它本身的Activity(即D),那么不会重新创建一个新的D实例(走onNewIntent()),所以栈中的结构依旧为A B C D,如果跳转到B,那么由于B不处于栈顶,所以会新建一个B实例并压入到栈中,结构就变成了A B C D B。应用实例:三条推送,点进去都是一个activity。

    singleTask:在singleTask模式下,Task栈中只能有一个对应Activity的实例。例如:现在栈的结构为A B C D,此时D通过Intent跳转到B(走onNewIntent()),则栈的结构变成了:A B。其中的C和D被栈弹出销毁了,也就是说位于B之上的实例都被销毁了。如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用通常情况下我们处理请求数据的onCreate方法,而是调用onNewIntent方法。通常应用于首页,首页肯定得在栈底部,也只能在栈底部。

    singleInstance:singleInstance模式下会将打开的Activity压入一个新建的任务栈中。例如:Task栈1中结构为:A B C,C通过Intent跳转到了D(D的启动模式为singleInstance),那么则会新建一个Task 栈2,栈1中结构依旧为A B C,栈2中结构为D,此时屏幕中显示D,之后D通过Intent跳转到D,栈2中不会压入新的D,所以2个栈中的情况没发生改变。如果D跳转到了C,那么就会根据C对应的启动模式在栈1中进行对应的操作,C如果为standard,那么D跳转到C,栈1的结构为A B C C,此时点击返回按钮,还是在C,栈1的结构变为A B C,而不会回到D。

    Intent Flag启动模式

    (1) Intent.FLAG_ACTIVITY_NEW_TASK:使用一个新的task来启动Activity,一般用在service中启动Activity的场景,因为service中并不存在Activity栈。
    (2) Intent.FLAG_ACTIVITY_SINGLE_TOP:类似andoid:launchMode="singleTop"
    (3) Intent.FLAG_ACTIVITY_CLEAR_TOP:类似andoid:launchMode="singleTask"
    (4) Intent.FLAG_ACTIVITY_NO_HISTORY:使用这种模式启动Activity,当该Activity启动其他Activity后,该Activity就消失了,不会保留在task栈中。例如A B,在B中以这种模式启动C,C再启动D,则当前的task栈变成A B D。

    清空任务栈

    (1) clearTaskOnLaunch:每次返回该Activity时,都将该Activity之上的所有Activity都清除。通过这个属性可以让task每次在初始化的时候都只有这一个Activity。
    (2) finishOnTaskLaunch:clearTaskOnLaunch作用在别的Activity身上,而finishOnTaskLaunch作用在自己身上。通过这个属性,当离开这个Activity所在的task,那么当用户再返回时,该Activity就会被finish掉。
    (3) alwaysRetainTaskState:如果将Activity的这个属性设置为true,那么该Activity所在的task将不接受任何清理命令,一直保持当前task状态,相当于给了task一道”免死金牌”。

    附上一个Activity管理器

    // Activity栈
    private Stack<Activity> activityStack;
    // 单例模式
    private volatile static AppManager instance;
    
    private AppManager() {
        
    }
    
    /**
     * 单例
     */
    public static AppManager getAppManager() {
        if (instance == null) {
            synchronized (AppManager.class) {
                if (instance == null) {
                    instance = new AppManager();
                }
            }
        }
        return instance;
    }
    
    /**
     * 添加Activity到堆栈
     */
    public void addActivity(Activity activity) {
        if (activityStack == null) {
            activityStack = new Stack<>();
        }
        activityStack.push(activity);
    }
    
    /**
     * 获取当前Activity(堆栈中最后一个压入的)
     */
    public Activity currentActivity() {
        return activityStack.lastElement();
    }
    
    /**
     * 结束当前Activity(堆栈中最后一个压入的)
     */
    public void finishActivity() {
        Activity activity = activityStack.lastElement();
        finishActivity(activity);
    }
    
    /**
     * 结束指定的Activity
     */
    public void finishActivity(Activity activity) {
        if (!activityStack.isEmpty()) {
            if (activity != null) {
                activity.finish();
                activityStack.remove(activity);
            }
        }
    }
    
    /**
     * 结束指定类名的Activity
     */
    public void finishActivity(Class<?> cls) {
        for (Activity activity : activityStack) {
            if (activity.getClass().equals(cls)) {
                finishActivity(activity);
            }
        }
    }
    
    /**
     * 结束所有Activity
     */
    private void finishAllActivity() {
        for (int i = 0; i < activityStack.size(); i++) {
            if (null != activityStack.get(i)) {
                activityStack.get(i).finish();
            }
        }
        activityStack.clear();
    }
    
    /**
     * 退出应用程序
     */
    public  void appExit(Context context) {
        try {
            finishAllActivity();
    
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.e(e.getMessage());
        }
    }
    
    public void printActivityStack(){
        LogUtil.e(activityStack.toString());
    }
    

    3.IntentFilter的匹配规则

    (1)IntentFilter中的过滤信息有action、category、data,为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。一个过滤列表中的action、category、data可以有多个,所有的action、category、data分别构成不同类别,同一类别的信息共同约束当前类别的匹配过程。只有一个Intent同时匹配action类别、category类别和data类别才算完全匹配,只有完全匹配才能成功启动目标Activity。此外,一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intenf-filter即可成功启动对应的Activity。

    <intent-filter>
        <action android:name="com.charpter_1.c" />
        <action android:name="com.charpter_1.d" />
    
        <category android:name="com.category.c" />
        <category android:name="com.category.d" />
        <category android:name="android.intent.category.DEFAULT" />
    
        <data android:mimeType="text/plain" />
    </intent-filter>
    

    (2)action匹配规则
    只要Intent中的action能够和过滤规则中的任何一个action相同即可匹配成功,action匹配区分大小写。

    (3)category匹配规则
    Intent中如果有category那么所有的category都必须和过滤规则中的其中一个category相同,如果没有category的话那么就是默认的category,即android.intent.category.DEFAULT,所以为了Activity能够接收隐式调用,配置多个category的时候必须加上默认的category。

    (4)data匹配规则
    data的结构很复杂,语法大致如下:

    <data 
    android:scheme="string"
    android:host="string"
    android:port="string"
    android:path="string"
    android:pathPattern="string"
    android:pathPrefix="string"
    android:mimeType="string" />
    

    主要由mimeType和URI组成,其中mimeType代表媒体类型,而URI的结构也复杂,大致如下:
    ://:/[]|[]|[pathPattern]
    例如content://com.example.project:200/folder/subfolder/etc
    scheme、host、port分别表示URI的模式、主机名和端口号,其中如果scheme或者host未指定那么URI就无效。
    path、pathPattern、pathPrefix都是表示路径信息,其中path表示完整的路径信息,pathPrefix表示路径的前缀信息;pathPattern表示完整的路径,但是它里面包含了通配符(*)。

    data匹配规则:Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。

    URI有默认的scheme!
    如果过滤规则中的mimeType指定为image/或者text/等这种类型的话,那么即使过滤规则中没有指定URI,URI有默认的scheme是content和file!如果过滤规则中指定了scheme的话那就不是默认的scheme了。

    //URI有默认值
    <intent-filter>
        <data android:mimeType="image/*"/>
        ...
    </intent-filter>
    //URI默认值被覆盖
    <intent-filter>
        <data android:mimeType="image/*" android:scheme="http" .../>
        ...
    </intent-filter>
    

    如果要为Intent指定完整的data,必须要调用setDataAndType方法!
    不能先调用setData然后调用setType,因为这两个方法会彼此清除对方的值。
    intent.setDataAndType(Uri.parse("file://abc"), "image/png");
    data的下面两种写法作用是一样的:

    <intent-filter>
        <data android:scheme="file" android:host="www.github.com"/>
    </intent-filter>
    
    <intent-filter>
        <data android:scheme="file"/>
        <data android:host="www.github.com"/>
    </intent-filter>
    

    如何判断是否有Activity能够匹配我们的隐式Intent?
    (1)PackageManager的resolveActivity方法或者Intent的resolveActivity方法:如果找不到就会返回null
    (2)PackageManager的queryIntentActivities方法:它返回所有成功匹配的Activity信息
    针对Service和BroadcastReceiver等组件,PackageManager同样提供了类似的方法去获取成功匹配的组件信息,例如queryIntentServices、queryBroadcastReceivers等方法

    有一类action和category比较重要,它们在一起用来标明这是一个入口Activity,并且会出现在系统的应用列表中。

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

    相关文章

      网友评论

      本文标题:安卓四大组件之Activity 以及Activity管理器

      本文链接:https://www.haomeiwen.com/subject/nqziittx.html