相信Android开发者都会知道,在应用内跳转页面,我们肯定会用到 Intent。使用 Intent 跳转页面有显式跳转和隐式跳转两种方式。

Intent intent = new Intent();

intent.setClass(this, MainActivity.class);

startActivity(intent);

典型的显式跳转就如同上面的代码一样,如果需要携带数据的话,我们也有两种方式。一方面我们可以调用 Intent 的 putExtra 系列方法,通过键值对的方式把数据传递过去。Intent 支持传递的数据包括基本类型数据、字符串、序列化以及各自的数组,有意思的是 Intent 支持 Parcelable数组 但不支持 Serializable数组。然后在跳转的页面通过相应的 get方法 把数据拿出来即可。

另一方面,我们也可以用 Intent 的 setData方法 将统一资源标识符Uri传递过去,然后在接收端解析 Uri 来拿出数据。对于一个 Uri,主要由以下几部分组成:

scheme://host:port/path?query

相应地,我们便能从 Uri 中把各个部分的信息拿出来。

而隐式跳转则是通过 Intent 的 action 来实现。典型的便是我们在桌面点击了应用的图标,然后应用启动进入我们的第一个页面。相信大家都知道对于应用自动的第一个页面,我们在 AndroidManifest.xml 文件中对 Activity 注册时,会有这么一段代码。

实际上应用从点击图标到启动第一个页面的过程相当复杂,如果大家有兴趣的话可以去看老罗的博客:

Android应用程序启动过程源代码分析

http://blog.csdn.net/luoshengyang/article/details/6689748

从源代码的分析来看,桌面图标保存了相应应用的启动 intent,该 intent 保存了 AndroidManifest.xml 的信息。因此我们自定义隐式启动页面时,只需要在 AndroidManifest.xml 中相应的 Activity 配置相应的 action 信息,然后调用的时候设置相应的 action 便可启动相应 Activity。

比如我们之前开发应用时,想要调用系统拨打电话应用,我们并不知道拨打应用 Activity 的具体信息,所以只有通过隐式方式来调用。

嗯,有了这些基础知识,我们便可以来实现浏览器打开本地App了。

浏览器打开本地APP

常规下,浏览器打开一个 url 要么是 http 要么是 https,那么,我们要怎么让浏览器能识别到一个 url 是指向我们的应用的呢?那我们来看看 Url 吧,仔细看看一个 url 链接其实就是一个 Uri,有scheme,有host,有query等等。scheme 实际上相当于一种协议,比如 http,系统识别到这个协议便会交由网络部分去处理这个请求。

那么系统能否识别自定义的 scheme 呢?答案是肯定的。我们可以在自己的应用内自定义一种 scheme,系统安装我们的应用后便会在某个地方进行 scheme 注册,这样当有相应的 scheme 请求的时候,便能将请求导向到我们的应用。有意思的是如果在多个应用中配置了相同的 scheme 的话,从浏览器请求时便会提示用户选择某一个应用来打开。

那么,我们来看看基本做法怎么做吧。

配置要打开的页面,比如我们在应用的启动页面配置相应的scheme

配置浏览器访问的url

myapp://?id=1

为了简单,就没有配置 host 信息,读者可以自己将 host 加上即可。这样,当浏览器访问这个 url 的时候便会打开我们的App。那么怎么拿传递的数据呢,不要方,看代码。

之所以要作一个判断,是为了区别该界面是否是由浏览器打开的,然后我们便可以通过 Uri 的 getQueryParameter 来获取传递的信息了,当然,你还可以拿到 Uri 的其他信息,这里就不说明了。

至此,通过浏览器打开本地App功能算是完成了。相信读者看到这里也会说,这么简单,我分分钟便能搞定。没错,做到这个地步,相信只要有点Android开发知识的同学都能分分钟搞定,那么我在这里提几个问题:

假如我们从浏览器打开了应用,按下HOME键,然后又从桌面点击了应用图标,这时候会发生什么?

假如应用之前已经打开了某个页面,然后我们又从浏览器重新打开了应用,这时候按返回键,我们还能回到之前打开的页面么?

我们通过浏览器打开页面一般都会是二级甚至三级页面,如果之前没有打开过应用,那么直接按返回键就会退出应用,这似乎用户体验不太友好,怎么解决?

如果你能把这三个问题解决了,那么从浏览器打开本地应用就算掌握了。哈哈,没那么简单吧,要解答上面三个问题,必须熟练掌握 Activity 的启动方式并灵活应用,下面我来介绍我的方案吧。

仿知乎浏览器打开本地应用

其实我上面抛出的三个问题就是从知乎的体验来提出的,通过浏览器打开知乎的某个问题的回答页面,假如之前知乎是打开的,则按返回键能直接回到之前的页面,如果之前知乎没有打开过,则按返回键会回到主页面。当通过浏览器打开页面后,按HOME键之后再重新点击图标,知乎会直接打开到浏览器打开的那个页面。所以知乎是完美解决了以上三个问题的。那么,我们来看如果不做任何处理,会是什么效果。

由于手机界面截图太大了,就不贴图了,我们从打印日志来看界面的启动情况吧。分别在 onCreate方法 和 onResume方法 中放入日志打印代码。

首先完全退出应用,然后通过浏览器调用 url 唤起界面,然后按下HOME键,点击图标重新进入,我们可以得到如下所示的日志

可以看出点击图标后会重新创建应用,之前在浏览器打开的界面找不到了。同样对于第二个问题,如果我们不做任何处理,浏览器唤起的页面按返回键是回不到之前打开的界面的。那么,这是为什么呢?

篇幅所限,就不介绍 Activity 的四种启动方式了。首先我们来了解一下任务栈这个概念吧。默认情况下,如果没有对 Activity 设置 TaskAffinity属性,一个应用的所有 Activity 都是运行在同一个任务栈的,任务栈的名称为应用的 PackageName。如果从应用A启动应用B的某个 Activity C,则 C 会运行在 A 的任务栈中。说到这里,相信大家应该明白为啥了吧。

从桌面启动的应用运行在应用本身的任务栈中,而从浏览器打开的界面则运行在浏览器的任务栈中,两个任务栈是分开的,所以在情景一,会重新创建出新的任务栈来打开应用,而在情景二中,由于浏览器的后台任务栈是桌面,在浏览器的任务栈中按返回键当然不能回到本地应用的任务栈咯而是回到桌面。

那么,知道了问题的原因,怎么来解决这个问题呢?由于从桌面点击应用会创建自己的应用栈,那么如果我们可以把浏览器任务栈中的界面移动到应用本身的任务栈中,则不就解决第一个问题了么。那么怎么将 Activity 从其他任务栈中移到自己的任务栈中呢?方法很简单,只需要在相应的 Activity 中配置 allowTaskReparenting属性 为 true即可。

android:allowTaskReparenting="true"

设置了该属性的 Activity 在应用真正启动时,会将在其他任务栈中移动到自己的任务栈中来,由于移动过来的界面处与栈顶,所以会直接显示之前在浏览器中打开的界面,是不是很厉害。

对于场景二,应用自己打开的 Activity 在自己的任务栈中,由于我们没办法把已启动的 Activity 移动到浏览器的任务栈中,所以只有另辟蹊径。我们知道浏览器唤起的那个界面如果不做任何处置,则会在浏览器的任务栈中新建 Activity,那么我们是不是可以指定唤起的 Activity 的打开方式为 SingleTask 呢,因为对于 SingleTask 类型启动的界面来说,如果在本任务栈中不存在对应的 Activity 的话,会在新的任务栈中新建 Activity。所以当浏览器唤起界面时,由于浏览器任务栈中没有对应 Activity,所以会在本地应用所在任务栈去创建 Activity,这样就链接到了本地应用的任务栈,并且将本来处于在后台任务栈的应用任务栈移动到了前台任务栈。

由于 Android 的返回机制是只有在某个任务栈为空时,才会退到上一个任务栈,所以按返回键的效果便是在本地应用栈中退出相应的 Activity,直到任务栈为空时,再把浏览器所在的任务栈移动到前台。当然有个小问题是如果我们在配置文件中声名 Activity 为 SingleTask 启动方式,如果该任务栈中存在相应的 Activity,则会把该 Activity 之上的所有 Activity 清除掉。而通过给 Intent 配置 New_Task 的 flag 方式默认不会清除 Activity 之上的其他 Activity,除非添加了 Clear_Top 的 flag。由于我们不能控制浏览器设置的 Intent,所以没法添加 New_Task 的 flag,所以直接在配置文件中设置 SingleTask 并行不通。

考虑到应用一般都会有启动页,并且启动后会自动 finish,那么我们可以利用该页面做做文章。我们通过浏览器打开启动页,然后在启动页面通过 New_Task 的方式去启动主页面,再逐级打开二级、三级页面。这样既不会清楚已经打开的 Activity,又可以实现任务栈的移动。法很简单,只需要在启动页面的 onCreate方法 中根据 Intent 的类型做页面跳转即可。

这样便解决了第二个问题,在浏览器中按返回键能回到之前打开的页面。对于第三个问题,就更简单了,我们只需要有个 AppManager类 来管理已经打开的界面,然后在启动页面判断应用是否有打开过的页面,如果有,则直接跳转到需要唤起的界面,如果之前没有打开界面,则先跳转到主界面,再跳转到需要唤起的界面,这样按返回键还能回到主界面不至于直接退出应用。这里值得注意的是跳转到主界面不要新建Intent,直接沿用获得的Intent重新设置跳转信息即可。思路比较简单,就不再赘述了。

这样三个问题都解决了,我们来简单回味一下整个优化过程:

通过设置 android:allowTaskReparenting=”true” 属性将其他任务栈中存在的 Activity 在应用启动时移动到自己的任务栈中,实现按HOME键启动应用能回到浏览器唤起的页面。

通过 Splash 页面做过渡,通过 New_Task 的方式启动浏览器唤起的页面,使得在浏览器中按返回键能接着本地应用已打开的页面。

通过判断 Activity 的数量决定是否是直接唤起页面还是先唤起主界面再打开需要打开的界面使得按返回键不至于直接从二级或者三级界面退出应用,提高用户体验。

文字写得有点啰嗦,那来一张简单明了的图吧。按照这个设计流程,并且在 AndroidManifest.xml 中将 MainActivity 以及浏览器需要唤起的 Activity 设置 android:allowTaskReparenting=”true” 属性,你也可以实现知乎那样的浏览器唤起应用。

能看到这里的都是真爱啊,希望大家在Android的道路上越走越远,越走越远,越走越远!

html调用deeplink,如何优雅地从浏览器打开本地应用deeplink相关推荐

  1. 优雅地从浏览器打开本地应用

    Android自定义URL使用Scheme方式唤起Activity或App 一.浏览器打开本地App 常规模式下,浏览器打开一个url要么是http要不就是https,那么浏览器是如何识别url指向我 ...

  2. 浏览器打开本地exe

    浏览器打开本地exe程序有很多方法ActiveXObject方法只支持IE,谷歌.火狐等浏览器并不支持此操作. 网上搜了很多资料还有自己加注册表方法的比如: http://blog.csdn.net/ ...

  3. 通过浏览器打开本地exe应用(支持任意浏览器)

    IE浏览器下打开指定路径的EXE文件: function Run(strPath) {//alert(strPath);try {var objShell = new ActiveXObject(&q ...

  4. java 安卓 html文件怎么打开方式_android 浏览器 打开本地html文件的方法

    android 浏览器 打开本地html文件的方法有些html文件放在本地磁盘和sdcard,如何用打开这个网页呢? 这种应用在测试时非常有用. 有2个方法: 1. 使用文件管理器 如ES等,需要幸运 ...

  5. 【护眼阅读】PC端通过主流常用浏览器打开本地WEB页面阅读本地TXT小说

    自用-通过WEB页面阅读本地小说,生成章节导航,设置字色和背景色达到护眼目的,HTML+CSS+JS 章节导航 需求分析 HTML代码 CSS代码 JS代码 总结 章节导航 从分析需求入手,查找资料, ...

  6. android 淘宝天猫支付宝浏览器打开本地app传递参数打开应用内页

    近期由于项目需要,通过浏览器打开本地app应用.经过多方的查询反复的尝试和阅读.总结出来.有价值的知识的分享出来.虽说不是很难. 第一步: 写好相关的js方便调取如下: <!DOCTYPE ht ...

  7. android 浏览器 打开本地html文件的方法

    android 浏览器 打开本地html文件的方法 有些html文件放在本地磁盘和sdcard,如何用打开这个网页呢? 这种应用在测试时非常有用. 有2个方法: 1. 使用文件管理器 如ES等,需要幸 ...

  8. C#注册URL协议,使用浏览器打开本地程序,类似网页上点了QQ交谈打开本地QQ客户端程序...

    本教程适用于Windows系统 从浏览器上打开本地程序,主要用到了本地URL协议,其实主要就是改注册表 先建立一个项目,我建的是控制台项目 在Program.cs写2个静态方法用来添加注册表.移除注册 ...

  9. android 浏览器打开本地html文件,如何在Android浏览器中加载本地HTML文件

    我试图在设备浏览器中加载本地html文件.我尝试过使用WebView,但它不适用于所有设备.如何在Android浏览器中加载本地HTML文件 //WebView method that didnt w ...

最新文章

  1. EMC全球调查显示:企业对新的安全威胁还未做好准备
  2. 尚学堂java 答案解析 第五章
  3. 9.关于Unicode字符集
  4. 键盘事件与JS Filter
  5. #开工新姿势#开启一年新征程,云社区叫你来充电啦!
  6. Oracle和al,ORACLEAL TERTABLE
  7. 解决了昨天差点让人崩溃的问题.
  8. Hibernate使用的一些细节注意事项
  9. HDU-1159-Common Subsequence
  10. 算法:罗马数字转换为整数13. Roman to Integer
  11. win10怎么更新Android驱动,w10怎么更新驱动程序_教你win10更新驱动程序的方法步骤...
  12. 运筹说 第36期 | 算法介绍之运输问题
  13. 阿里 delphi java_DelphiCodeToDoc--像JavaDoc一样的Delphi源码文档生成工具
  14. 华为交换机配置IPSG防止DHCP动态主机私自更改IP地址
  15. 百度贴吧恶意代码分析
  16. DDR中ODT(on-die termination)
  17. 工程伦理(李正风)——第一章习题
  18. python中绘制柱形图、饼形图等
  19. 4.通过Opencv采集摄像头视频数据
  20. 数学建模中的常用编程软件

热门文章

  1. Chrome浏览器里的-webkit-focus-ring-color
  2. 用代码查看SAP Spartacus购物车内的行项目
  3. SAP Spartacus的url parameter
  4. Angular应用里的@Input和@Output注解使用方法介绍
  5. wordpress插件在服务器上的存储位置
  6. SAP Marketing Cloud功能简述(五) : 销售计划管理
  7. CRM One Order search max hit实现原理讨论
  8. SAP WebIDE编辑器的主题设置
  9. when is One Order gt_plan_exets filled
  10. Table accessed during One order advanced search