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

本篇文章已授权微信公众号 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>标签中都不会指定过多的内容,常见的是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肯定有这个协议,我们就去上层源码看一看,找到如下代码:

<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:permission就是我们打电话要指定的权限。而往下看第一个intent-filter

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>

此时两个intent-filter,我们为了验证刚才的说法,把启动活动的代码修改一下:

Intent intent = new Intent();
intent.setData(Uri.parse("ydl2:234"));
intent.setAction("com.itydl2");
startActivity(intent);

我们发现都改成了ydl2,这样启动时匹配的是上边这个intent-filter。能正常启动活动。

注意,启动互动代码必须与某一个intent-filter相匹配才能正常启动活动,否则会报错。这是为什么,相信我们很简单就能理解原因了。

继续跟进:

我们在清单文件中除了可以配置多个intent-filter,还可以配置多个data,以及多个action吗?(很多源码中也是这样的)。我们再把清单文件中代码作如下修改来试试:

<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>

这里添加了多个data多个action,其实,在我们在启动活动的代码位置,只需要配置任意一条data以及任意一条action就可以成功启动这个activity了,不需要全部匹配。活动代码即使这么写,也是没有任何问题:

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用来传递什么数据类型的。例如,一般我们传递文本数据,我们就可以这么写:

<intent-filter>
<action android:name="com.itydl2"/> <data android:mimeType="text/username"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

我们首先测试只有android:mimeType的时候。

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>

那么我们启动的时候,需要通过

intent.setDataAndType(Uri.parse("ydl2:qwe"),"text/username");

这样就能完好匹配清单文件内容,可以正常启动actiivity了。

终于写完了,相信看完此篇文章你对隐式启动活动的原理更加清晰了。以后启动任何互动,只需要看看那个活动的清单文件内容,就能轻松通过隐式启动的方式去打开它了!

最后,祝大家圣诞快乐!

喜欢我的朋友可以关注我的博客专栏。

也可以打开微信搜索公众号  Android程序员开发指南  或者手机扫描下方二维码 在公众号阅读更多Android文章。

微信公众号图片:

Android隐式启动Activity可能存在的坑的更多相关文章

  1. Android隐式启动匹配:action,category,data

    简介 Android开发中,Activity,Service 和 BroadcastReceiver 启动有两种方式,显示启动和隐式启动. 为方便下面描述,我以Activity启动为例. 显示启动便是 ...

  2. 显式启动Activity和隐式启动Activity

    1.显式启动Intent intent = new Intent(this, class);startActivity(intent); 2.隐式启动AndroidManifest.xml中定义某个A ...

  3. android隐式intent使用场景解析

    Android 隐式intent相信大家都有用过,大部分场景我们用显式intent已经能满足我们的业务需求,隐式intent大部分都是用来启动系统自带的Activity或Service之类的组件.昨天 ...

  4. Intent显示启动与隐式启动

    Android的Acitivity启动大致有两种方式:显式启动与隐式启动.下面分别介绍: 1.显示启动: 清单文件注册Activity <activity android:name=" ...

  5. Android-隐藏app图标以及隐式启动

    隐藏APP桌面图标 <activity android:name=".LaunchActivity"> <intent-filter> <action ...

  6. ANdroid5.0不能隐式启动service,必须显示,解决办法,加服务端包名

    Intent intent = new Intent(); intent.setAction("com.viaembedded.veonvif.RemoteService");// ...

  7. Android 跨进程启动Activity黑屏(白屏)的三种解决方案

    原文链接:http://www.cnblogs.com/feidu/p/8057012.html 当Android跨进程启动Activity时,过程界面很黑屏(白屏)短暂时间(几百毫秒?).当然从桌面 ...

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

    显式Intent我已经简单使用过了,也介绍过概念,现在来说一说隐式Intent: 隐式Intent:就是只在Intent中设置要进行的动作,可以用setAction()和setData()来填入要执行 ...

  9. Android 学习之显式激活与隐式激活Activity

    在res界面里面有两个布局文件activity_main和acivity_two

随机推荐

  1. urlopen()&urlretrieve()

    1.urlopen()方法 urllib.request.urlopen(url[,data[,proxies]]) 创建一个表示远程url的类文件对象,然后像本地文件一样的操作这个类文件对象来获取远 ...

  2. jacascript 事件流

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 事件流 当浏览器发展到第四代时(IE4及Netscape4),浏览器开发团队遇到了一个很有意思的问题:页面 ...

  3. Python面向对象——基本继承

    1.基本继承图解 1.1实例化一个Contact类的对象c 1.2实例化一个Supplier类的对象s 1.3访问对象的属性 1.4访问对象s的方法 1.5类变量详解 如果从新定义c.all_cont ...

  4. iOS之AFSecurityPolicy

    AFSecurityPolicy是AFNetworking中负责对https请求进行证书验证的模块,本文主要是要搞清楚它是如何工作的. 在介绍AFSecurityPolicy之前,我们先来了解一下ht ...

  5. Python系列之 - 线程基础

    一.什么是线程 线程:顾名思义,就是一条流水线工作的过程,一条流水线必须属于一个车间,一个车间的工作过程是一个进程 所以,进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才 ...

  6. 关于Ueditor存储在mysql"UTF-8"乱码的问题

    关于Ueditor存储在mysql"UTF-8"乱码的问题 首先小编要声明之前出乱码的原因,mysql 字段设置的BLOB类型 紧接着我们要先确认两件事: 1.首先要确认你的mys ...

  7. .NET CORE 2.0之 httpcontext

    HttpContext  在之前的.NET framework 是一个非常常用且强大的类,在.NET CORE2.0中要像以前用是不太方便的了, 要是用sesson 首先需要在startup 的Con ...

  8. HNOI2018 滚粗记

    day0 说好了不复习,于是复习了一下配置,没想到一下就记住了,咋不退役去搞英语竞赛捏 皇室一波攒RP,chicken chicken一波攒RP day1 机子坏了,换到了最后面,但这个时候已经 \( ...

  9. Hdu2680 最短路

    给定一个有向图,多个起点,一个终点,求起点到终点的最短路. 1.可以加一个点,使其与那些起点的距离为0 2.将图反着来建,然后在所有点找出最小的 方案一: #include <iostream& ...

  10. 【网络流】【BZOJ1006】【SCOI2007】蜥蜴

    学弟@lher在周末训练赛中出的题目的原题(这个人拿省选题来当作提高组模拟,太丧了...) 题意简析:看题目:) 解题思路:题目显然是最大流. 首先拆点将点权变为边权,然后按照题意对于所有有跳板的点向 ...