Android隐式启动Activity可能存在的坑

Posted 杨道龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android隐式启动Activity可能存在的坑相关的知识,希望对你有一定的参考价值。

  转载本专栏文章,请注明出处,尊重原创 。文章博客地址:道龙的博客

   本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布  

  本篇文章,对隐式启动Activity再做分析。

  有些人可能会说了,隐式启动活动不是很简单吗?这有什么不理解的?话先别说的这么早,对于隐式启动,还是具有很大的坑要爬的,当然,您如果是一个资深开发者就另当别论了。

  本篇文章,我们从最简单的开始,一步步引入,相信这样的方式,读起来也会轻松一些。

  我们平时启动一个活动,会通过两种方式。1、显示启动;2、隐式启动。

  (一)首先,我们来看两个很简单的小案例(实现打电话)。

我们再在布局文件提供一个TextView用于提示输入电话号码,在EditText里面输入号码,点击按钮的同时,获取到用户输入的号码,并且启动打电话功能。

主活动代码很简单:

MainActivity:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //给按钮设置点击侦听
        //1.拿到按钮对象
        Button bt = (Button) findViewById(R.id.bt_call);//Button类是View的子类,向下转型要强转。
        //2.设置侦听
        bt.setOnClickListener(new MyListener());
    }

    class MyListener implements View.OnClickListener {

        //按钮被点击时,此方法调用
        @Override
        public void onClick(View v) {
            //获取用户输入的号码
            EditText et = (EditText) findViewById(R.id.et_phone);
            String phone = et.getText().toString();

            //我们需要告诉系统,我们的动作:我要打电话
            //创建意图对象
            Intent intent = new Intent();
            //把打电话的动作ACTION_CALL封装至意图对象当中
            intent.setAction(Intent.ACTION_CALL);
            //设置打给谁
            intent.setData(Uri.parse("tel:" + phone));//这个tel:必须要加上,表示我要打电话。否则不会有打电话功能,由于在打电话清单文件里设置了这个协议
            //把动作告诉系统,启动系统打电话功能。
            startActivity(intent);
        }

    }

}
运行后如下:



这个简直太简单了,估计代码都能烂肚子里了。

嗯,的确很简单,那就紧跟脚步,我们继续看一个简单的自定义启动活动的代码。

(二)自定义Activity,并隐式方式启动

我们都知道,如果要想自定义隐式启动别的activity,需要给该Activity添加“意图过滤器”<intent-filter>。

通过在<activity>标签下配置<intent-filter>的内容,可以指定当前活动能够响应的 action
和 category,打开 androidManifest.xml,添加如下代码:

<activity android:name=".NextActivity">
    <intent-filter>
        <action android:name="com.itydl"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

在<action>标签中我们指明了当前活动可以响应 com.itydl 这个 action,我们可以随便写里面的内容,它的加入表示给我们的Activity添加一个动作,只有带动作的Activity才能被隐式启动。而<category>标签则包含了一些附加信息,更精确地指明了当前的活动能够响应的 Intent 中还可能带有的 category。 意图中设置的action必须跟"com.itydl"是完全匹配的,只有<action>和<category>中的内容同时能够匹配上 Intent中指定的 action和 category时,这个活动才能响应该 Intent。我们也道,android.intent.category.DEFAULT 是一种默认的 category,在调用startActivity()方法的时候会自动将这category添加到 Intent中(查看所有系统源码,也都带上了这个category,我们也无需多去关心category它可能存在的坑了)。

对于Action的原理是:当StartActivity()运行的时候,该Activity会去系统所有清单文件中找对应的Action("")里面能匹配的Activity,找有没有对应的action与我们所写入的能匹配的,如果有(这里是NextActivity),这样就启动了NextActivity。

既然,NextActivity有了动作,那么我们的MainActivity再添加一个按钮,使用隐式方式启动它。如下:

mNext = (Button) findViewById(R.id.bt_next);
mNext.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent();
        intent.setAction("com.itydl");
        startActivity(intent);
    }
});

此时运行程序,发现能够正常启动。我们发现第一个例子启动打电话功能时候,还添加了一个setData()。那么,我们自定义的也可以同样添加data。修改清单文件NextActivity配置代码如下:

<intent-filter>
    <action android:name="com.itydl"/>
    <data android:scheme="ydl"/>
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
这个时候,我们再运行程序,发现程序崩溃。这是因为我们隐式启动代码中,并没有完全跟清单文件中<intent-filter>下面的内容匹配。要想匹配成功,我们也要去代码中设置data。再回到小案例一中,我们也就知道了为什么启动打电话功能要设置setData了,还不是因为电话清单文件源码中有这个data标签吗。

在这里浅显介绍一下系统添加data下面的标签都有哪些:

<data>标签中主要可以配置以下内容。
1.  android:scheme
用于指定数据的协议部分。
2.  android:host
用于指定数据的主机名部分。
3.  android:port
用于指定数据的端口部分,一般紧随在主机名之后。
4.  android:path
用于指定主机名和端口之后的部分,如一段网址中跟在域名之后的内容。
5.  android:mimeType
用于指定可以处理的数据类型,允许使用通配符的方式进行指定。只有<data>标签中指定的内容和 Intent 中携带的 Data 完全一致时,当前活动才能够响应该 Intent。不过一般在<data>标签中都不会指定过多的内容,常见的是mimeTypescheme

给我们的隐式启动按钮加入如下代码:

intent.setData(Uri.parse("ydl:qwe"));
运行程序不再报错。

上边的内容,对于一个初学者来说是必须掌握的内容。那么再来看看一些细节问题,这些细节问题不知道,可能还真会让你不知道如何去隐式启动别的Activity!

(三)深入分析隐式启动的细节

首先先从自定义隐式启动开始讨论。上边代码Uri.parse()参数内容"ydl:qwe"我们看到qwe好像很别扭,其实setData的英文名称就告诉我们设置数据的了。基于上面对系统<data>里面配置介绍,我们清单文件中是android:scheme,这是一个协议,因而我们设置数据必须要以scheme后边内容开头。这里是"ydl"作为了协议。而后面"qwe"内容可以随便写。例如我改成如下代码:

intent.setData(Uri.parse("ydl:234"));
仍然可以启动下一个活动。

那么我们再回到最初的小案例,有如下代码:

intent.setData(Uri.parse("tel:" + phone));

这里不就是一个协议吗?在清单文件中对应的Activity肯定有这个协议,我们就去上层源码看一看,找到如下代码:

<activity android:name="OutgoingCallBroadcaster"
                android:permission="android.permission.CALL_PHONE"
                android:theme="@android:style/Theme.NoDisplay"
                android:configChanges="orientation|keyboardHidden">
            <!-- CALL action intent filters, for the various ways
                 of initiating an outgoing call. -->
            <intent-filter>
                <action android:name="android.intent.action.CALL" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="tel" />
            </intent-filter>
            <intent-filter android:icon="@drawable/ic_launcher_sip_call">
                <action android:name="android.intent.action.CALL" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="sip" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.CALL" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="voicemail" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.CALL" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.item/phone" />
                <data android:mimeType="vnd.android.cursor.item/phone_v2" />
                <data android:mimeType="vnd.android.cursor.item/person" />
            </intent-filter>
        </activity>

以上是关于Android隐式启动Activity可能存在的坑的主要内容,如果未能解决你的问题,请参考以下文章

android activity启动的4种方式记录及打开其他应用的activity的坑

显式与隐式启动Activity

android--显式跳转和隐式跳转

Android-----Intent中通过startActivity(Intent intent )隐式启动新的Activity

Intent显示启动与隐式启动

Android启动模式之singleinstance的坑