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

当筵意气临九霄,星离雨散不终朝。这篇文章主要讲述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); }}}

运行后如下:
Android隐式启动Activity可能存在的坑

文章图片




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

嗯, 的确很简单, 那就紧跟脚步, 我们继续看一个简单的自定义启动活动的代码。
( 二) 自定义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> 标签中都不会指定过多的内容, 常见的是mimeType和scheme。

给我们的隐式启动按钮加入如下代码:
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肯定有这个协议, 我们就去上层源码看一看, 找到如下代码:
【Android隐式启动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>


    推荐阅读