当前位置: 主页 > 老夫子论坛 >

如何优雅地从浏览器打开本地应用

时间:2019-10-06 23:12来源:未知 作者:admin 点击:

  昨日,中国第一狗仔卓伟、名侦探赵五儿、全明星探等19个新浪微博八卦账号被封。公告称,“微博中的明星群体及其粉丝是微博生态的重要组成部分。这一生态和平台的健康发展不需要急功近利的炒作,更要摒弃低俗媚俗和造谣诽谤。“

  本篇来自zhuimengfb的投稿,分析了浏览器打开本地应用会遇到的问题,并给出了相应的解决方案。希望能够帮助到大家。

  某一天从微信打开知乎链接的时候,发现竟然可以从浏览器直接打开知乎应用并且进入到特定的页面,觉得十分神奇,于是就研究了一下怎么来实现这个功能。在实现的过程中,发现要完成知乎那样的体验,还是需要下很大功夫,下面我将详细介绍如何实现到知乎那样的完美体验。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  那么,知道了问题的原因,怎么来解决这个问题呢?由于从桌面点击应用会创建自己的应用栈,那么如果我们可以把浏览器任务栈中的界面移动到应用本身的任务栈中,则不就解决第一个问题了么。那么怎么将 Activity 从其他任务栈中移到自己的任务栈中呢?方法很简单,只需要在相应的 Activity 中配置 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 的数量决定是否是直接唤起页面还是先唤起主界面再打开需要打开的界面使得按返回键不至于直接从二级或者三级界面退出应用,提高用户体验。

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

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

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

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

  每天学习累了,看些搞笑的段子放松一下吧。关注最具娱乐精神的公众号,每天都有好心情。

  如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。返回搜狐,查看更多六合神棍论坛www493333com开马

------分隔线----------------------------
六和皇图库| 新报跑狗玄机图论坛| 天将图库| 香港惠泽社群全年资料| 香港赛马会赛马结果| 香港正版挂牌图彩| 宝码高手心水论坛百度| 香港马会开奖时间日期| 香港马会正挂挂牌最快| 香港挂牌最完整版|