本篇文章,对隐式启动 Activity 再做分析。
有些人可能会说了,隐式启动活动不是很简单吗?这有什么不理解的?话先别说的这么早,对于隐式启动,还是具有很大的坑要爬的,当然,您如果是一个资深开发者就另当别论了。
本篇文章,我们从最简单的开始,一步步引入,相信这样的方式,读起来也会轻松一些。
我们平时启动一个活动,会通过两种方式。1、显示启动;2、隐式启动。
我们再在布局文件提供一个 TextView 用于提示输入电话号码,在 EditText 里面输入号码,点击按钮的同时,获取到用户输入的号码,并且启动打电话功能。
主活动代码很简单:
MainActivity:
- public class MainActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.);
- //给按钮设置点击侦听
- //1.拿到按钮对象
- Button bt = (Button) findViewById(R.id.);//Button类是View的子类,向下转型要强转。
- //2.设置侦听
- bt.setOnClickListener(new MyListener());
- }
- class MyListener implements View.OnClickListener {
- //按钮被点击时,此方法调用
- @Override
- public void onClick(View v) {
- //获取用户输入的号码
- EditText et = (EditText) findViewById(R.id.);
- String phone = et.getText().toString();
- //我们需要告诉系统,我们的动作:我要打电话
- //创建意图对象
- Intent intent = new Intent();
- //把打电话的动作ACTION_CALL封装至意图对象当中
- intent.setAction(Intent.);
- //设置打给谁
- intent.setData(Uri.parse("tel:" + phone));//这个tel:必须要加上,表示我要打电话。否则不会有打电话功能,由于在打电话清单文件里设置了这个"协议"
- //把动作告诉系统,启动系统打电话功能。
- startActivity(intent);
- }
- }
- }
运行后如下:
这个简直太简单了,估计代码都能烂肚子里了。
嗯,的确很简单,那就紧跟脚步,我们继续看一个简单的自定义启动活动的代码。
我们都知道,如果要想自定义隐式启动别的 activity,需要给该 Activity 添加 "意图过滤器"<intent-filter>。
- <activity android:name=".NextActivity">
- <intent-filter>
- <action android:name="com.itydl"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- </activity>
在
标签中我们指明了当前活动可以响应 com.itydl 这个 action,我们可以随便写里面的内容,它的加入表示给我们的 Activity 添加一个动作,只有带动作的 Activity 才能被隐式启动。而对于 Action 的原理是:当 StartActivity() 运行的时候,该 Activity 会去系统所有清单文件中找对应的 Action("") 里面能匹配的 Activity,找有没有对应的 action 与我们所写入的能匹配的,如果有(这里是 NextActivity),这样就启动了 NextActivity。
既然,NextActivity 有了动作,那么我们的 MainActivity 再添加一个按钮,使用隐式方式启动它。如下:
- mNext = (Button) findViewById(R.id.);
- mNext.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent();
- intent.setAction("com.itydl");
- startActivity(intent);
- }
- });
此时运行程序,发现能够正常启动。我们发现第一个例子启动打电话功能时候,还添加了一个 setData()。那么,我们自定义的也可以同样添加 data。修改清单文件 NextActivity 配置代码如下:
这个时候,我们再运行程序,发现程序崩溃。这是因为我们隐式启动代码中,并没有完全跟清单文件中下面的内容匹配。要想匹配成功,我们也要去代码中设置 data。再回到小案例一中,我们也就知道了为什么启动打电话功能要设置 setData 了,还不是因为电话清单文件源码中有这个 data 标签吗。
- <intent-filter>
- <action android:name="com.itydl"/>
- <data android:scheme="ydl"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
给我们的隐式启动按钮加入如下代码:
运行程序不再报错。
- 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:permission 就是我们打电话要指定的权限。而往下看第一个 intent-filter
- <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>
action android:name="android.intent.action.CALL" /> 不就是针对我们代码中要设置的 Action(intent.setAction(Intent.ACTION_CALL);)吗?
<data android:scheme="tel"不就是我们在代码中要指定的协议吗 (intent.setData(Uri.parse("tel:110"));)?只不过我们所以带过去一些数据 phone,用户输入的号码吗?
到了这里,相信这个细节大家都能掌握了。那么接着往下继续爬坑~
我们发现,打电话 OutgoingCallBroadcaster 的清单文件中有太多的 intent-filter,看花了眼,我们到底怎么知道匹配哪个 intent-filter 才能启动打电话这个活动呢?其实,原理很简单,在清单文件中的 intent-filter 代表我们可以有好几种启动方式好几种写法去启动这个打电话这个活动。我们使用哪一种方式都无所谓的,都可以。那么我们模仿这种情况,来对自定义启动活动也这么添加几个 intent-filter,看完下面的内容,这些坑也就爬完了。
首先我们在清单文件中继续添加 intent-filter
此时两个 intent-filter,我们为了验证刚才的说法,把启动活动的代码修改一下:
- <intent-filter>
- <action android:name="com.itydl2"/>
- <data android:scheme="ydl2"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
我们发现都改成了 ydl2,这样启动时匹配的是上边这个 intent-filter。能正常启动活动。
- Intent intent = new Intent();
- intent.setData(Uri.parse("ydl2:234"));
- intent.setAction("com.itydl2");
- startActivity(intent);
注意,启动互动代码必须与某一个 intent-filter 相匹配才能正常启动活动,否则会报错。这是为什么,相信我们很简单就能理解原因了。
继续跟进:
我们在清单文件中除了可以配置多个 intent-filter,还可以配置多个 data,以及多个 action 吗?(很多源码中也是这样的)。我们再把清单文件中代码作如下修改来试试:
这里添加了多个 data 多个 action,其实,在我们在启动活动的代码位置,只需要配置任意一条 data 以及任意一条 action 就可以成功启动这个 activity 了,不需要全部匹配。活动代码即使这么写,也是没有任何问题:
- <intent-filter>
- <action android:name="com.itydl2"/>
- <action android:name="com.itydl3"/>
- <action android:name="com.itydl4"/>
- <action android:name="com.itydl5"/>
- <data android:scheme="ydl2"/>
- <data android:scheme="ydl3"/>
- <data android:scheme="ydl4"/>
- <data android:scheme="ydl5"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
启动活动成功。
- Intent intent = new Intent();
- intent.setAction("com.itydl2");
- intent.setData(Uri.parse("ydl4:234"));
- startActivity(intent);
最后,还有一点点就完结了。即常见的 android:mimeType 这个属性
我们知道了,data 是用来传递数据的,听过 setData 把数据放进里面,会把数据传递给目标 Activity,在目标 activity 可以通过 getIntent()(获取启动这个 activity 的意图对象)方式来获取里面的数据。而 android:mimeType 是用来定义你 setData 用来传递什么数据类型的。例如,一般我们传递文本数据,我们就可以这么写:
我们首先测试只有 android:mimeType 的时候。
- <intent-filter>
- <action android:name="com.itydl2"/>
- <data android:mimeType="text/username"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
这个时候,代码和清单文件也是完全匹配的。我们运行程序,也是没有任何问题。
- Intent intent = new Intent();
- intent.setAction("com.itydl2");
- intent.setType("text/username");
- startActivity(intent);
当 android:mimeType 和 android:scheme 同时存在的时候,我们需要注意
如果代码先设置 setData(); 后 setType() 后者会把前者清理掉,反之亦然,这里就跟你女神问你 "你妈和我掉水里你先救谁" 原理是一样的。我们需要通过 setDataAndType() 方法,把 scheme 和 mimeType 同时设置进去才能匹配对应的清单文件中的 intent-filter。例如我清单文件中代码是:
那么我们启动的时候,需要通过
- <intent-filter>
- <action android:name="com.itydl2"/>
- <data android:scheme="ydl2"/>
- <data android:mimeType="text/username"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
这样就能完好匹配清单文件内容,可以正常启动 actiivity 了。
- intent.setDataAndType(Uri.parse("ydl2:qwe"),"text/username");
终于写完了,相信看完此篇文章你对隐式启动活动的原理更加清晰了。以后启动任何互动,只需要看看那个活动的清单文件内容,就能轻松通过隐式启动的方式去打开它了!
最后,祝大家圣诞快乐!
喜欢我的朋友可以关注我的博客专栏。
也可以打开搜索公众号 程序员开发指南 或者手机扫描下方二维码 在公众号阅读更多 Android 文章。
公众号图片:
来源: