Android 关于Activity易疏漏知识点总结

一、前言

这篇总结并不是着重的深层次的去分析四大组件,而是对于日常开发中我们常用的或者经常被我们忽略的知识点做个总结,本文会从Activity、Service、BroadCastReceiver、ContentProvider逐一介绍,由于考虑到文章的篇幅问题,所以我会通过几篇小节将四大组件的知识点串联一下。

二、本篇脉络

本篇主要是对于Activity的相关知识点做个总结,都是一些老生常谈的问题,主要从生命周期、启动模式、隐式启动几个方面说起,下面我们就来看一下知识点大纲:

  • Activity的生命周期过程
  • 横竖屏切换与生命周期
  • 弹出对话框与生命周期
  • Activity透明主题与生命周期
  • Activity启动模式
  • IntentFilter的匹配规则

Activity是Android组件中最基本也是最常见常用的四大组件之一,Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务。Activity中的所有操作都与用户密切相关,是一个负责与用户交互的组件,在一个Android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应,这段定义出自百度百科

(1)Activity的生命周期过程

说到Activity就不得不提下其生命周期过程,不过估计每个学安卓的都知道,在其生命周期内,activity在运行、暂停、停止三种状态间进行转换,而我们所说的onCreate()等方法就是activity在状态切换时由系统调用的,在这里我们需要注意的是,onCreate()方法调用时期对用户来说是不可见的,其调用是在创建activity实例后,但在此实例出现在屏幕之前。

onCreate()>>onStart()>>onResume()>>onPause()>>onStop()>>onDestroy()

还有一点就是当ActivityA跳转到ActivityB的时候,两者的生命周期过程我们也应该清楚

onCreateA()>>onStart()A>>onResumeA()>>-点击跳转->>onPauseA()>>onCreateB()>>onStart()B>>onResumeB()>>onStopA()

当我们正常将程序切换至后台时的生命周期

//在后台过程中,假设停留时间短且内存充足,没有被系统回收
onPause()>>onStop()

当我们重新回到前台时的生命周期过程

//在后台过程中,假设停留时间短且内存充足,没有被系统回收
onRestart()>>onStart()>>onResume()
(2)横竖屏切换与生命周期

在我们的实际开发中,经常会需要考虑用户横竖屏切换之后是否会对程序产生影响,下面是横竖屏切换后生命周期的过程

onPause()>>onSaveInstanceState()>>onStop()>>onDestroy()>>onCreate()>>onStart()>>onResume()

在上面过程中我们可以发现,当用户横竖屏切换时activity实例会被销毁,随后新的activity实例又会被新建,正是基于这种原因,activity切换前我们所定义好的数据在这个过程中就会被重新初始化掉,解决办法也很简单,相信大家都会,那就是覆盖onSaveInstanceState(…)方法保存需要的数据,随后在onCreate(…)方法中取回这些数据,下面来简单看下代码:

public class ActivitySummary extends AppCompatActivity {
    private static final String TYPE_COUNT = "typeCount";
    private int type;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_summary);
        getBundleData(savedInstanceState);
        addClickListener();
    }

    @Override protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(TYPE_COUNT, type);
    }

    private void getBundleData(Bundle savedInstanceState) {
        if(savedInstanceState != null){
            type = savedInstanceState.getInt(TYPE_COUNT);
        }
    }

    private void addClickListener() {
        findViewById(R.id.tv_hw).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                type ++;
            }
        });
    }
}

其实横竖屏切换的烦恼还不止这个,横竖屏切换后的界面显示效果也是一个让人蛋疼的问题,不过这个问题Android已经给我们提供了较完美的解决方案,那就是新建备选资源,下面我们来看一个小例子,这是我们默认(竖屏)的代码:

public class ActivitySummary2 extends AppCompatActivity{

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_summary);
        addClickListener();
    }

    private void addClickListener() {
        findViewById(R.id.tv_hw).setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View view) {
                Log.i("Tag123","click执行");
            }
        });
    }
}

很简单的代码结构,这是我们的activity_summary.xml布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_hw"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="我在中间位置"
        android:textSize="18sp"
        android:textColor="#787878"
         />
</RelativeLayout>

如果对于横竖屏切换没有特殊要求,那么到这里就可以结束了,效果如下

这里写图片描述

如果你对于横屏布局有着特殊需求,那么就需要新建一个备选资源,首先调整项目到project结构,右键res新建一个文件夹并命名为layout-land,随后在layout-land目录下新建一个相同名字的xml布局,一个技巧就是可以直接将activity_summary.xml布局从layout目录下复制到layout-land目录之下,随后再根据需求更改xml布局,本例中src目录结构如下:

这里写图片描述

这是layout-land目录下的activity_summary.xml的布局,注意横屏和竖屏的两个布局文件名字必须一致,这样才能保证它们能以同一个资源ID被引用

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_hw"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="我来顶部哈"
        android:textSize="18sp"
        android:textColor="#787878"
         />
</RelativeLayout>

我们再次运行,查看下运行效果,同时我们点击文字,横竖屏都会打印“click执行”字样

这里写图片描述

当然如果想要关闭自动横竖屏切换也是可以的,第一种方法就是在清单中在相应的activity中指定特定方向,其中portrait为竖向,landscape为横向

    <activity android:name=".ActivitySummary2"
            android:screenOrientation="portrait"
            />

第二种方法就是可以在代码中动态添加

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏
//        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//横屏
        setContentView(R.layout.activity_summary);
    }

如果你不想关闭横竖屏切换功能,但又不想Activity在屏幕旋转的时候重新创建,那么我们可以给Activity指定configChanges属性

android:configChanges="orientation|keyboardHidden|screenSize"

其中orientation表示屏幕方向发生了改变;keyboardHidden表示键盘的可访问性发生了改变,比如用户调出了键盘;screenSize表示当编译时minSdkVersion和targetSdkVersion不低于或不全低于13时,当屏幕的尺寸信息发生了改变时,会导致Activity重启。

(3)弹出对话框与生命周期

在这里我们主要是看下,当存在对话框的情况下,Activity的生命周期过程,所以简单实现一个AlterDialog就行啦,实现代码也很简单

public class ActivitySummary3 extends AppCompatActivity {

    @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_summary3);
        addClickListener();
    }

    private void addClickListener() {
        findViewById(R.id.tv_hw).setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View view) {
                createDialog().show();
            }
        });
    }

    private Dialog createDialog() {
        View view = LayoutInflater.from(this).inflate(R.layout.dialog_show_helloworld, null);
        return new AlertDialog.Builder(this)
                .setView(view)
                .setTitle("Hello World Dialog").setPositiveButton(android.R.string.ok, null)
                .create();
    }
}

好了,我们在当前ActivitySummary3界面点击弹出对话框按钮,来看下其生命周期

//噢,其实生命周期并没有变化,有点意外啊,哈哈,我还以为Activity会失去焦点onPause()呢,不过已经习惯了,经常被打脸[笑哭]
//当取消对话框的时候,Activity生命周期的几个方法同样没有调用
//当存在对话框的Activity按Home键回到桌面随后再启动App后(未销毁),其生命周期与没有对话框时一样

AlterDialog的出现与否对于Activity的生命周期来说并没有影响,我随后又拿系统的权限对话框测试了一下,情况又有所不同

//当系统的提示框出现时,Activity失去焦点调用onPause()方法
//当系统的提示框消失时,Activity获得焦点调用onResume()方法
//所以说系统框出现的时候,对于Activity的生命周期还是有影响的,这和AlterDialog不同
(4)Activity透明主题与生命周期

当ActivityA跳转至ActivityB的时候,如果ActivityB设置为透明主题,那么跳转成功后虽然此时处于B界面,但我们仍能看到B界面之下的ActivityA界面布局,所以这就对于ActivityA的生命周期产生了影响,接下来我们来看一个具体的例子:

首先我们需要将ActivitySummary7的主题设置为透明背景,半透明效果也一样,因为现在的重点不是如何设置主题,所以主题的设置代码我就简单介绍了,在styles.xml中代码如下:

    <style name="myTransparent" parent="@android:style/Theme.Translucent.NoTitleBar">
        <item name="android:windowBackground">@color/transparent</item>
        <item name="android:windowAnimationStyle">@null</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowContentOverlay">@null</item>
    </style>

在我们的colors.xml中设置透明颜色

    <color name="transparent">#00000000</color>

随后在我们的清单中,给ActivitySummary7设置主题

    <activity android:name=".ActivitySummary7"
            android:theme="@style/myTransparent"
            >

好了,我们的重点来了,我们先来看下,从启动ActivitySummary4 到 跳转至ActivitySummary7的过程中 两个Activity所经历的生命周期过程

//我们启动ActivitySummary4
onCreate4()>>onStart4()>>onResume4()
//随后我们从ActivitySummary4跳转至透明主题的ActivitySummary7,我们能发现端倪,其并没有执行onStop()方法
onPause4()>>onCreate7()>> onStart7()>> onResume7()
//随后我们按返回键,返回到ActivitySummary4
onPause7()>>onResume4()>>onStop7()>>onDestroy7()

从上面我们可以发现,由于ActivitySummary7的主题透明导致跳转后ActivitySummary4的界面仍然可见,所以造成跳转后的ActivitySummary4并不会执行onStop()方法,按返回键的时候ActivitySummary4同样也就不会执行onRestart()和onStart()方法了。正是由于这种情况,也就造成了一个很好玩的现象:

//当我们在ActivitySummary7中,黑屏,两个Activity的stop方法先后执行
 onPause7()>>onStop7()>> onStop4()
//随后回到正常屏幕显示,是不是很有意思
onRestart7()>> onStart7()>>onRestart4()>>onStart4()>>onResume7()

值得注意的是,不仅透明主题如此,Theme.AppCompat.Light.Dialog等窗口主题同样如此,其实我们想想其中缘由也就明白了,当窗口Activity出现后,我们依然能够看到上一个Activity。

(5)Activity启动模式

启动模式在多个Activity的跳转过程中有着其重要的作用,它包括standard、singleTop、singleTask、singleInstance四种,接下来我们就来简单看下这几个启动模式。

1.standard标准模式

standard启动模式是我们默认的Activity启动模式,所以我们的清单中android:launchMode=”standard”可写可不写,不是必须的

    <activity android:name=".ActivitySummary3"
            android:launchMode="standard"
            />

在这种模式下,当我们每启动一个Activity,系统都会创建一个相应的activity实例,并把它们按照”先进后出”的原则放入任务栈中,当我们按back键之后,系统就会按照”后进先出”(也就是先进后出)的原则从任务栈中不断移除掉相应activity,直到任务栈为空,随后系统回收该任务栈。同时我们还需要注意,每个Activity都会被放入其相应任务栈之中,不会游离其外,刚进入App后的第一个Activity同样也不例外,不仅如此,在这种模式下,谁启动了一个Activity,那么那个Activity就运行在启动它的那个Activity所在栈中,下面我们来看一个小例子

//这是App启动后的当前前台界面
zmj.componentssummary I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@4271a668,taskId:245
//从ActivitySummary4跳转至ActivitySummary5
zmj.componentssummary I/Tag123: ActivitySummary5 内存地址:zmj.componentssummary.ActivitySummary5@427961c8,taskId:245
//从ActivitySummary5跳转至ActivitySummary6
zmj.componentssummary I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427c26b8,taskId:245
//从ActivitySummary6我直接跳转到自身ActivitySummary6,我们发现即使栈中存在ActivitySummary6实例,系统还是会创建一个新的实例
zmj.componentssummary I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427ed020,taskId:245
//back返回一次,销毁ActivitySummary6@427ed020
zmj.componentssummary I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427c26b8,taskId:245
//back返回一次,销毁ActivitySummary6@427c26b8
zmj.componentssummary I/Tag123: ActivitySummary5 内存地址:zmj.componentssummary.ActivitySummary5@427961c8,taskId:245
//back返回一次,销毁ActivitySummary5@427961c8
zmj.componentssummary I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@4271a668,taskId:245
//back再返回一次,本程序中直接退到了桌面,因为这里ActivitySummary4是第一个界面
//随后重新打开App,我们会发现taskId改变了
zmj.componentssummary I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@428dde98,taskId:248

ActivitySummary4、ActivitySummary5、ActivitySummary6几个类的代码我就不贴了,非常简单,只是在onResume()方法中打印了我们需要的信息

Log.i("Tag123",getClass().getSimpleName()+" 内存地址:"+this+",taskId:"+getTaskId());

从上面我们不难看出,每当我们启动了一个Activity时,系统都会创建一个新的对象,即使是自身跳转到自身也同样不例外,并本着先进后出的入栈出栈原则

2.singleTop栈顶复用模式

在这种模式下,如果待启动的Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,直接会被系统复用;如果待启动的Activity在栈内不存在或者存在实例但不位于栈顶,那么系统就会创建一个新的此Activity实例,下面我们就来具体的看下Activity的入栈出栈情况,其中我们设置ActivitySummary6的启动模式为singleTop:

    <activity android:name=".ActivitySummary6"
            android:launchMode="singleTop"
            />

或者我们也可以在代码中设置Flags,而且此种方式优先级高于在清单activity中设置

intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

接下来我们连续启动几个Activity看下,启动顺序为ActivitySummary4>>ActivitySummary5>>ActivitySummary6>>ActivitySummary6,具体情况如下:

//这是App启动后的当前前台界面
zmj.componentssummary I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@42734308,taskId:44
//从ActivitySummary4启动ActivitySummary5界面
zmj.componentssummary I/Tag123: ActivitySummary5 内存地址:zmj.componentssummary.ActivitySummary5@427aefc0,taskId:44
//从ActivitySummary5启动ActivitySummary6界面
zmj.componentssummary I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427db8b8,taskId:44
//从ActivitySummary6再启动自身ActivitySummary6界面,发现系统并没有重新创建ActivitySummary6对象,而是直接复用了,因为内存地址没有变
zmj.componentssummary I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427db8b8,taskId:44
//点击返回按钮,你也会发现栈内只有一个ActivitySummary6对象
zmj.componentssummary I/Tag123: ActivitySummary5 内存地址:zmj.componentssummary.ActivitySummary5@427aefc0,taskId:44

我们再来看下当待启动的Activity不在栈顶的这种情况,ActivitySummary4>>ActivitySummary6>>ActivitySummary5>>ActivitySummary6,具体情况如下:

//这是App启动后的当前前台界面
06-19 10:57:36.072 I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@42735640,taskId:50
//从ActivitySummary4启动ActivitySummary6
06-19 10:57:44.124 I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427b4e58,taskId:50
//从ActivitySummary6启动ActivitySummary5
06-19 10:57:46.367 I/Tag123: ActivitySummary5 内存地址:zmj.componentssummary.ActivitySummary5@427e1468,taskId:50
//从ActivitySummary5启动ActivitySummary6,我们会发现当再启动ActivitySummary6的时候,系统又会创建一个新的对象
06-19 10:57:48.532 I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@4280b5d0,taskId:50

需要注意的是,当我们发现Activity位于栈顶并直接复用时,就会调用onNewIntent(Intent intent)方法,我们可以在此方法中获得Intent中携带的数据,且点击跳转之后生命周期过程如下

onPause()>>onNewIntent(Intent intent)>>onResume()

3.singleTask栈内复用模式

在这种模式下,只要待启动的Activity在一个栈中存在,那么系统就不会再重新创建此Activity的实例,而是直接复用,在这种情况下,如果此Activity当前位于栈顶,则操作与singleTop一样,直接复用即可,而如果此Activity当前没有位于栈顶,那么系统会强制让当前栈内此Activity上面的其他Activity全部出栈,最后的结果就是此Activity位于了栈顶能被直接复用,因为别的Activity都被出栈了;还存在一种情况,即如果待启动的Activity在栈内不存在,那么系统就正常的创建一个Activity实例即可。

同时需要注意的是,singleTask为栈内复用模式,假设现在有两个任务栈分别为任务栈A和任务栈B,其中任务栈A中存在ActivityA,任务栈B中存在ActivityB,现在我需要启动一个singleTask模式的ActivityA放在任务栈B内,那么系统就会检查任务栈B中是否存在ActivityA,如果不存在,就会立即新建一个ActivityA对象放入任务栈B之内,并不会复用任务栈A中的ActivityA。下面就来看下Activity具体的入栈出栈情况,其中ActivitySummary6为singleTask启动模式:

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

或者我们也可以在代码中设置Flags

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

接下来我们连续启动几个Activity,启动顺序为ActivitySummary4>>ActivitySummary6>>ActivitySummary5>>ActivitySummary6,具体情况如下:

//这是App启动后的当前前台界面
06-19 11:21:38.384 I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@427376b8,taskId:54
//ActivitySummary4启动ActivitySummary6,系统创建了一个ActivitySummary6实例
06-19 11:21:49.287 I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427b75e8,taskId:54
//ActivitySummary6启动ActivitySummary5
06-19 11:21:52.087 I/Tag123: ActivitySummary5 内存地址:zmj.componentssummary.ActivitySummary5@427e3da8,taskId:54
//ActivitySummary5再启动ActivitySummary6,因为ActivitySummary6在栈内存在,所以系统直接复用
06-19 11:21:54.836 I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427b75e8,taskId:54
//点击返回键,我们发现将ActivitySummary6移除栈之后,直接来到了ActivitySmmary4,
说明ActivitySummary5在启动ActivitySummary6的时候,由于ActivitySummary6不在栈顶,所以系统移除了ActivitySummary6栈上面的ActivitySummary5,使ActivitySummary6来到了栈顶
06-19 11:22:01.269 I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@427376b8,taskId:54

同样我们也需要注意的是,当待启动Activity存在时,不管位于栈顶与否,当复用此Activity时,都会调用onNewIntent(Intent intent)方法,且生命周期与singleTop一样

4.singleInstance单实例模式

在这种模式下,其除了具有singleTask模式的所有特性外,还有一点那就是,如果待启动的Activity不存在,那么系统会为它创建一个新的任务栈,然后将Activity独自放在这个任务栈中,由于singleInstance也具备栈内复用原则,所以只要新的任务栈不被系统所销毁就不会重新创建Activity的实例,下面来看下Activity具体的入栈出栈情况,其中ActivitySummary6为singleInstance启动模式,需要注意的是singleInstance模式我们只能在清单中设置,系统不支持在代码中直接设置:

    <activity android:name=".ActivitySummary6"
            android:launchMode="singleInstance"
            />

接下来我们连续启动几个Activity,启动顺序为ActivitySummary4>>ActivitySummary6>>ActivitySummary5>>ActivitySummary6,具体情况如下:

//这是App启动后的当前前台界面
06-19 11:42:36.833 I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@4273e430,taskId:58
//ActivitySummary4启动ActivitySummary6,我们发现系统新建一个任务栈
06-19 11:42:43.889 I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427bde20,taskId:59
//ActivitySummary6启动ActivitySummary5,我们发现ActivitySummary5被放在了原来的任务栈中
06-19 11:42:49.769 I/Tag123: ActivitySummary5 内存地址:zmj.componentssummary.ActivitySummary5@427ea7b8,taskId:58
//ActivitySummary5再启动ActivitySummary6,我们发现ActivitySummary6复用了
06-19 11:42:55.623 I/Tag123: ActivitySummary6 内存地址:zmj.componentssummary.ActivitySummary6@427bde20,taskId:59
//当点击返回键的时候,系统移除ActivitySummary6,随后销毁没有Activity的taskId=59任务栈,回到了ActivitySummary5,
因为ActivitySummary6是独立的一个栈,并taskId=58的栈内不存在clearTop的情况
06-19 11:43:01.894 I/Tag123: ActivitySummary5 内存地址:zmj.componentssummary.ActivitySummary5@427ea7b8,taskId:58
//再点击返回键,移除ActivitySummary5,回到最初的ActivitySummary4
06-19 11:43:24.920 I/Tag123: ActivitySummary4 内存地址:zmj.componentssummary.ActivitySummary4@4273e430,taskId:58
(6)IntentFilter的匹配规则

隐式启动一个Activity在实际开发中用到的其实并不多,所以在这里我就大概的介绍下,隐式启动一个Activity需要我们的Intent能够匹配待启动Activity的IntentFilter中所设置的过滤信息,如果与之不匹配则无法启动该Activity。IntentFilter中的过滤信息包括action、categoty、data三种,那么接下来我们就来拿几个小例子逐一看下

1.匹配action

这是我们的ActivitySummary6在清单中的< activity >代码,注意当不想设置category特定值时,也需要将其设置为android.intent.category.DEFAULT,否则报错

    <activity android:name=".ActivitySummary6">
            <intent-filter>
                <action android:name="zmj.componentssummary.06"/>
                //当不想添加category时,也得添加DEFAULT,否则报错,原因如下
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

我们在ActivitySummary5界面启动上面的ActivitySummary6界面,代码如下

        Intent intent = new Intent();
        intent.setAction("zmj.componentssummary.06");
        startActivity(intent);

当我们没有调用addCategory()方法时,系统会默认调用intent.addCategory(“android.intent.category.DEFAULT”),这也是我们在清单中不加category为什么会报错的原因。当然在清单< activity >中,我们也可以定义多个action值,匹配规则即当我们Intent所携带的action值能与其中一个action值完全相同即可算匹配成功。

2.匹配category

这是我们的ActivitySummary5在清单中的< activity >代码,注意在< intent-filter >标签里,< action > 和 < category >标签需要同时存在,否则会报错

    <activity android:name=".ActivitySummary5">
            <intent-filter>
                //必须存在action,当只有categary时报错
                <action android:name="zmj.componentssummary.05"/>
                //这是我们自己定义的category标签
                <category android:name="zmj.componentssummary.category05"/>
                //DEFAULT这个category标签不能去掉,否则会报错
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

在我们的ActivitySummary6中启动我们的ActivitySummary5,代码如下

        Intent intent = new Intent();
        intent.setAction("zmj.componentssummary.05");
        intent.addCategory("zmj.componentssummary.category05");
        startActivity(intent);

当然我们也可以在清单中添加多个自定义category,匹配规则即如果我们的Intent含有category,那么我们所有的category都必须和过滤规则中的其中一个category相同。

3.匹配data

不同于action和category的字符串格式,data的数据格式稍微有点复杂

        <data
            android:mimeType="媒体类型,例如image/*、image/jpeg、audio/mpeg4-generis、video/*"
            android:scheme="URI的模式,例如http、file、content"
            android:host="主机名,例如www.baidu.com"
            android:port="端口号,例如8080"
            android:path="/完整的路径信息"
            android:pathPattern="完整的路径信息,可以包含通配符"
            android:pathPrefix="/路径的前缀信息"/>

不过其实常用的也很简单,例如这是咱们的ActivitySummary7,需要注意的是,< data >必须和< action >和< category >共同作用,自己不能单独使用,否则会报错

        <activity android:name=".ActivitySummary7">
            <intent-filter>
                <action android:name="zmj.componentssummary.07"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data
                    android:mimeType="image/png"
                   />
            </intent-filter>
        </activity>

这是我们启动ActivitySummary7的代码

    Intent intent = new Intent();
    intent.setAction("zmj.componentssummary.07");
    intent.setDataAndType(Uri.parse("file://abc"), "image/png");
    startActivity(intent);

还有一点需要注意的是,当我们没有指定过滤规则中的URI模式,即scheme的值时,URI就会取默认值content和file,所以我们启动的时候Intent必须携带URI模式类型才能与之匹配,同时,< data >也支持添加多个,匹配规则即Intent所携带的data必须能够和过滤规则中的其中一个完全匹配。

三、总结

关于Activity的知识点还有一些,包括和Fragment的使用、和服务广播的组合、源码层次的启动过程,在这里就不介绍了,我会在后续的总结中再做介绍。

猜你喜欢

转载自blog.csdn.net/MingJieZuo/article/details/80677136