意图(Intents)

用户点击一个mailto:的连接时,这实际上就被看作是一个意图,发邮件的意图。

关于意图有三点要说明:

* 如果是显式意图,Android就会立即启动那个activity

* 如果是隐式意图,Android先去intent filter寻找合适的activity再启动。

* 如果有多个合适的意图,Android就会列出一个意图选择列表供用户选择。

下面就举用户发邮件的例子,此时用户的Android上有两个邮件应用程序,当他在页面点击了mailto:链接的时候,Android会提示给他一个对话框,其中有两个可用的程序供其选择(GmailEmail)。

下面列举一些常用的意图和其对应的activity

* 查看联系人列表:对应联系人列表查看activity

* 查看指定的联系人:对应联系人查看activity

* 编辑指定的联系人:对应联系人编辑activity

* 发邮件:对应邮件activity

* 拨打电话:对应电话拨打activity

* 查看图片列表:对应图片列表查看activity

* 查看指定的图片:对应图片查看activity

* 裁剪指定的图片:对应图片裁剪activity

意图必须由两部分构成:动作和数据。

* 动作:由上面的意图列表中可得出,查看、编辑、打电话、裁剪

* 数据:由上面的意图列表中可得出,联系人的列表、指定的联系人、电话号码、图片列表、指定的图片。

注意:任何在桌面上启动的应用程序都是显式意图,目的是指定其内部特有的那个activity。同理,应用程序也可以在内部以显式意图的方式启动自身的activity,外部activity都是访问不到它们的。

关于意图更多信息,参见Intents and Intent Filters

切换任务

下面的例子描述的是用户如何在两个任务之间进行切换。

1. 开始第一个任务。你想要发送一条短消息并附加一张图片。你会这样操作:

桌面 > 短消息 > 新的短信息 > 菜单 > 附件 > 图片。最后一步启动了画廊activity来选择一张照片。注意画廊是另外的一个应用程序。

在选择照片之前,可以先去桌面打开日历,目的是为了开始第二个任务。

2. 开始第二个任务。你会这样操作:桌面 > 查看日历。从桌面上打开日历,就等于是开始一个新任务了。

3. 切换到第一个任务并完成后面的操作。

查看完日历之后,继续回到先前的任务上:桌面 > 短消息,此时进入的并不是短消息activity,而是画廊activity,也就是之前离开的activity。然后你就可以选择图片并发送短消息出去了,也就完成了第一个任务。

设计小贴士

下面的提示和指导都是针对应用设计者和开发者而提出的。

使用显式意图来防止外部应用调用你的activity

如果你不想自己的activity被外部使用,就别在manifest.xml里面配置intent-filter。这样的话,你的activity就只会在应用程序内部来启动了,同样也避免了安全漏洞。反之,创建一个意图并指定明确要启动的组件,这就是显示意图,在这个例子中,就不需要intent filtersIntent filters可以发布所有的应用程序,当你创建了一个intent filter时,其它应用程序就可以访问到你的activity了,至于它们怎么用,你就不知道了,这意味着不经意间形成了安全隐患。

如果使用外部的activity,但却没有匹配上,该怎么办?

有这一种情况,你利用Intent去调用外部应用程序中的activity,但遗憾的是,那个应用程序并没有安装进手机里,因此我们需要妥善的处理这种情况。

(译者注:官方提出了两个不太完善的解决方案,我们来看下:)

1. 在启动那个activity之前用intent先对其测试一下。

2. 如果启动activity会失败的话,则捕获它的异常信息。

以上更多信息请参阅官方文档提供的博客文章:Can I use this Intent?

该博文中提供了一种比较好的解决方式,正如其提供的样例代码中的isIntentAvailable()方法,我们可以在初始化阶段调用它;如果该应用不存在的话,我们就给用户提示一条消息,告诉他某某应用不存在,请去Google Market下载等友好信息。如果要让意图决定显示哪个activity,那我们就使用startActivity()或是startActivityForResult()来启动activity

思考:以怎样的方式来启动activity

做为Android设计者或开发者,完全取决于用户如何启动你的应用程序,而应用程序则是由一系列的activity组成,用户会从Home或是其他应用程序中启动这些activity

* 在桌面点icon来启动应用程序主activity

如果你的应用程序是独立运行的,它应该是用户在屏幕上触摸应用程序的icon或是任务选择器当中来启动(这个机制需要在manifest.xml中配置intent filteractionMAINcategoryLAUNCHER)。

* 在其它应用程序中启动你的activity

这种方式就意味着你的activity是可重用的,也就是隐式意图。许多应用程序中的数据都需要共享给其它用户的,例如,email、文本消息、上传下载等。

还会有一种情况是这样,就是当用户选择了一个功能,正好有一个或多个activity符合用户的这种需求,就会向用户提供一个activity列表供其选择。举一个具体的例子,Gallery(画廊),它能让用户查看并共享图片,这时用户选择了“共享”菜单,Android系统会在intent filter中寻找适合该请求的activity,如果有多个,就会以列表的形式展现给用户,供其选择。在这个例子中,intent filter能找到EmailGmailMessagingPicasa等。

当其它的activity启动了你的activity时,会根据需求给它们返回一个结果。

> 启动一个activity并需要返回一个结果

官方称这种方式为closed loop,也就是说当启动一个activity之后,会返回一个结果回来。再拿上面那个例子来说,当用户完成上传或者发送的过程之后,会将图片信息返回给Gallery。这个例子中的上传过程所用到的activity就是由外部的Gallery启动的。(这种方式需使用startActivityForResult()

> 启动一个activity不需要返回结果

官方称这种方式为open-ended。举个例子,在Email中可定位一个住址,那么应用程序便会启动地图activity来定位地址,完成之后不会再给Email返回任何的结果;此时用户可以按BACK键回到Email中来。(这种方式需使用startActivity()

* 只从其他应用程序中启动activity -前面所说的例子,Gmail、消息、Picasa(在Gallery启动)都是activity,它们均从桌面上的icon来启动的,与之形成对比的是,像裁剪图片和添加附件则不是在桌面启动的,因为这些都不是独立运行的。

实际上,并非所有的应用程序都有icon以供启动,它们都算作是一种小小的应用而已,因为它们使用并不频繁,而且其启动点都嵌在已有的应用程度当中。例如,Android手机里面的打电话程序,其内部有个铃声设置功能,它存在于Android手机里面的设置(Setting)菜单里面,你也可以使用同样的Intent开发出一个定制的铃声设置应用,这样,在用户需要改变铃声时,会向其展示出两个铃声设置应用,一个是Android内置的,另一个就是你开发出来的。如下图:

铃声设置并不是经常使用,而且其定义的功能也很明确,所以也就不需要在桌面提供应用程序icon了。

* 不同的图标能够启动多个相同的应用程序 -由于Android应用程序的运行代码均存在于.apk文件中,因此就把这个文件看作是一个应用程序。我们甚至还可以让其内部存在两个主activity,也就是两个应用程序启动入口点。

Camera.apk(照相机)就是一个非常好的例子,它内部就含有两个独立的主activityCameraCamcorder(摄像机);它们均拥有自己的icon并且独立运行;在用户角度上来看,这就是两个应用程序。它们都共享使用一个镜头、在Gallery里面保存图片等。

实现这样的功能其实很简单,只需将它们都关联到不同的任务上即可。(每个activity都其各自的任务,每个任务都有不同的亲缘性(affinity)。(这个例子的两个应用它们所在的两个包是"com.android.camera""com.android.videocamera",有兴趣的可以深入研究)。

联系人和拨号器也是同一个应用两个主activity的典型例子。

* 应用程序部件 - 我们也可以将应用程序以部件的形式嵌进桌面上或是其它应用程序中并能它们持续更新。

允许你的activity添加到当前任务中

如果你的activity是在外部应用程序已经启动的话,那么也允许把它们添加到当前的任务中来(或者是已存在的任务——它有自身的affinity),这样做的话能会使用户能够在其它任务和你的activity之间进行自由切换。但不包括你的activity仅有一个实例的情况。

对于这种行为,你的activity应该有一个standard singleTop的启动模式,而不是singleTasksingleInstance,这样,你的activity就会以多实例的模式来运行。

通知,应该能让用户更容易的返回上一个activity

利用后台运行的服务能够给用户发出他们感兴趣的事件消息。下面举个例子,主要是以Calendar为主,这个例子含两部分,一个是以Email的形式发出即将来临的消息,另一个是当有新消息时就发出通知。

我们来模拟一个应用场景,当某个用户处于activity A中,这时获取到了activity B发出的通知,他打开了这个通知,也就是进入到了activity B,当用户按下BACK键,他应该回退到activity A

下面的具体流程描述了当用户响应通知时,activity栈是怎样工作的:

1. 首先,用户在Calendar中设置了一个开会通知,也就是创建了一个新的事件,并将已写进Email中的部分信息复制到该事务上。

2. 其它用户选择 桌面 > Gmail

3. 他们打开Gmail,接收到来自Calendar发出的一个开会通知。

4. 接着他们打开了那条通知,进入到Calendar activity中,并查看会议的简要说明。

5. 这时用户进入到其里面查看更为详细的内容(就是在第一步当中复制的信息)。

6. 当用户完成查看的操作时,按下了BACK键。他们回到了Gmail上,也就是打开通知的那个地方。

但上面的流程在默认情况下却不是这样的。

通常情况下主要有两种方式来发出通知:

* 通知专用的activity - 接着上面的应用场景来说,某用户接受了一条Calendar通知,用户首先进入Gmail,查看详细的内容就要进入Calendar activity中;用户查看完后,按下BACK键必须要返回到Gmail activity上。实现此功能的前提是,Calendar activity不能有与Calendar或其它activity同样的亲缘性(affinity),也就是将该亲缘性设为空字符串即可。下来解释一下为什么这么做。

那个Calendar activity拥有其默认的任务亲缘性(taskAffinity),当按下BACK键时(如上述第六步)回到了Calendar,而不是Gmail,这就是上面那样做的主要原因。特定应用程序中的所有activity都具有相同的任务亲缘性,因此Calendar activity的亲缘性匹配了Calendar的任务,这个任务是在第一步当中运行起来的,那第四步就表示打开Calendar activity,又回到了Calendar的任务中,所以最后返回的还是Calendar activity。但这不是我们想要的结果,只有将任务亲缘性设为空字符串才能解决这个问题。

* 选择已有的activity,但只会展示其初始的状态 -例如,用户在于Gmail交互的过程中进入到了其它Activity,稍后再在回到Gmail activity时显示要显示其初始的状态,而不是先前的状态。首先,你要确保通知触发器起作用时,intent的标识是“clear top”,所以当activity启动时,它显示的是初始化之后的activity,防止Gmail再次来到前台时还是用户上一次的看到的那些状态。(你需要在intent对象中设置FLAG_ACTIVITY_CLEAR_TOP标志)

另外还有其它方式去处理通知,比如让一个activity到前台并设置好其显示的指定数据,比如短信息。

一般情况下都是以新任务的形式来启动通知activity的(也就是说,在intent对象中设置FLAG_ACTIVITY_NEW_TASK),这么做是避免这个任务成为另一个任务中的一部分。

请使用通知系统,而不要使用对话框来代替通知

如果你的后台服务在某个时刻要通知给用户一个消息,那么请使用标准通知系统-,不要使用dialogtoast来通知。这两个会直接弹出来提示用户,再说通俗一些就是会突然打断用户的当前操作,这是一个极为不友好的用户体验。通知系统在这方面就做得就比较好,用户可以在适当的时候从屏幕上方拉下通知列表以便回应消息。

不要重新设置BACK键的功能,除非你有绝对的需要

BACK键的主要功能就是从当前的activity回退到上一个activity,就是所谓的导航功能。大多数的activity都是一些比较通用的操作,诸如查看联系人列表,查看照片等,如果按BACK键,就直接返回先前调用它的activity就好了,不需其它的功能需求。

但要考虑一个问题,如果是应用程序非常得大,并需要细粒度的BACK键来加以控制该如何呢?例如Google浏览器,已经打开了几个web页面和地图页面,其中有一些关于地图数据的图层面板,我们需要在它们之间进行切换操作,也就是说在其内部通过BACK键来对其进行回退导航,而不是针对整体的activity

接上面的例子继续说,地图应用程序展现给用户不同的数据图层面板:有用来显示查询结果的定位信息、有显示朋友的位置、有显示街道方向的路线等。地图应用程序将这些图层面板保存在自身的历史记录里面,所以需要BACK键来进行回退导航。

同样地,浏览器使用浏览器窗体为用户展示多个的web页面,每个窗体都有它自身的历史导航,也就是桌面操作系统上的浏览器中的标签。例如,你在Android浏览器中的一个窗体上打开Google进行查询,并点击一条查询结果,那么这个结果页面会在当前窗体上打开,然后按BACK键就会回到了查询页面。总结一句话就是,当前窗体是从先前的窗体上跳转过来的,这时按BACK键就会回到了先前的窗体。如果用户一直按BACK键的话,最后就会离开浏览器所在的activity,回到了桌面。

这些例子就是你有绝对的需要才要重新设置BACK键功能的理由。

更多相关文章

  1. Android(安卓)7.0 Nougat不得不知的11项新功能
  2. Android应用程序框架层和系统运行库层日志系统源代码分析
  3. Android(安卓)安全机制(1)uid 、 gid 与 pid
  4. Android应用程序框架层和系统运行库层日志系统源代码分析
  5. Android开机自启动程序设置及控制方法
  6. Android启动过程
  7. Android中的一些重要概念
  8. 2013年3月17日----Android主题(Theme)实现
  9. Android(安卓)NFC 应用程序记录ARR

随机推荐

  1. android中view的宽高测量
  2. Android(安卓)自定义Toast,不使用系统Toa
  3. Android(安卓)用ScrollView和HorizontalS
  4. 【Android】Zip文件解压方法
  5. android 报错问题解析
  6. 在Android(安卓)的Camera 预览上执行 Dra
  7. Android(安卓)APK安装常见错误
  8. Android(安卓)微信朋友圈多图分享,不用申
  9. Android显示GIF动画的几种方法
  10. android中读取短信