我们都知道在Android中,我们是通过Intent来打开一个Activity的,但是你知道这个过程是如何做到的吗?当然如果你不知道,那太好了,这篇文章也不会告诉你的。如果有兴趣知道请看罗大的文章:Android应用程序启动过程源代码分析。本文所要探讨是我们打开Activity的方式。这里面没有干货,更多的只是总结!
我们知道Activity有一个接近于教科书似的使用方法。如下:
Intent intent = new intent(this,AActivity.class);
startActivity(intent);
这就是Activity被打开的普通方法。这种方法被称为显式调用。想要调用那个Activity一目了然。这样的方法在开发的过程中最常见,也是大部分新手最早的学习内容。当然如下的方式也可以看成是一种扩展。
Intent intent = new Intent();
intent.setClass(this,AActivity.class);
startActivity(intent);
可以看到本质上没有什么差别。
隐式调用在开发中其实也是比较常见的,特别是要跨应用调用的时候,我们也经常会使用隐式调用的方式。比较经典的如下:
Uri uri = Uri.parse("tel:119");
Intent it = new Intent(Intent.ACTION_DIAL, uri);
startActivity(it);
这段代码运行后会拨打119电话,可以看到它使用了一个action,然后通过uri来传递消息。而运行这段代码之所以能够运行是因为系统为拨打电话这个功能加上了ACTION_DIAL这个action,所以这个Itent便能打电话了。当然你也可以在你的应用中的某个Activity加上这个ACTION_DIAL,这样的话,如果有应用通过这段代码来打电话就会跳出选择框来选择。这个就是Android中的隐式调用,可以看出来它并没有指定哪个Activity来处理,这样就把选择权交给了用户。这种方式通常会应用在跨应用调用上。因为本应用只能显式的打开自己Manifest里面组册的应用。不在自己Manifest里面注册的Activity如果通过显式调用就会抛出未找到Activity的异常。而通过action这种方式就能够让系统去查找所有拥有这个Action的应用。当然这个还是普通的隐式调用,更有趣的如下:
Intent intent = new Intent();
Uri uri = Uri.parse("market://details?id=" + applicationName);
intent.setData(uri);
intent.setPackage(mMarketPackageName);
startActivity(intent);
这段代码能够实现在mMarketPackageName这个应用市场打开applicationName这个应用。前提是该应用市场定义这个方式的Scheme。这也是通过了隐式调用来实现的。只不过这次指定的是Scheme。不过隐式调用有个需要注意的地方,就是要判断这个Intent能否被系统处理,否则一样会崩溃,判断方法如下:
intent.resolveActivity(mContext.getPackageManager()) != null
这段代码便能够判断目标Intent是否可以被处理。
上面说到Scheme,它是一种协议,通过这个协议,我们可以从web打开应用。所以Activity不仅仅可以通过应用来打开,web/wap这样的方式也是可以的。这时候你需要的是在你的onclick时间里填上Scheme地址。静态的打开方式可以参考下面:
<a href="weixin://dl/moments">朋友圈a>
将该代码嵌入到网页中,点击它,你就能打开朋友圈了。这是Scheme协议的使用,至于实现,以后用空再谈,或者自己动手查找一下,其实还是很简单的。
上文提到了两种调用Activity的方式。一种隐式一种显式。隐式需要注意的是你要调用的Activity是否存在,因为不同的系统会用不同的情况,所以要先判断再调用,除非特别有把握的,比如发短信打电话。而显式调用就是不能跨应用调用,否则会出异常。另外有几个tips来分享:
如果是服务的提供者,就是被调用的Activity,这个时候你需要关注页面是否存在安全性的问题。当你提供了跨域调用的功能后,你的Activity就有可能会被置于其它应用的Task中。所以如果你要提供给其它应用使用的Activity,你需要关注这个Activity是否有敏感数据,它会不会被盗取。由于Scheme协议其实也是一种隐式调用方式,所以如果开启了Scheme协议,你也需要关注这个问题。
之前在公司的工程中看到了这样的代码:
if (bundle != null && bundle.containsKey(KEY)) {
intent.putExtra(KEY,bundle.getBoolean(KEY));
}
在没有看过源码之前我并没有任何想法,当我看过Intent源码之后就发现了这是有问题的。因为在源码中,有一个Bundle变量mExtra,当我们putExtra的时候,其实是把数据放到了这个mExtra中了,而putExtras()来添加Bundle的时候,添加进来的Bundle实例会被遍历一遍,然后加入到mExtra中,也就是说通过Bundle加入的数据和putExtra加入的数据是被放在了统一的地方,所以上面的步骤其实是多此一举。包括getExtras获得Bundle实例再分析也是多此一举,直接从Intent中取就行了。