前言
项目求人前段时间有个需求,希望提高应用的用户日活量,在用户使用Google或者Chrome浏览器检索“求人”相关关键字后,点击检索的网页,如果网页链接的前缀是项目的官网(https://www.xxxx.xx.com/…),那么需要启动App, 并在合适的地方解析出获取到的链接,跳转到指定的检索内容页面。
项目采用了谷歌官方提供的API-- DeepLink,也就是深度链接(https://developer.android.com/training/app-links/deep-linking)来实现。
DeepLink介绍
Deeplink又叫深度链接,是一项减少运营难度的技术,它在手机上的应用场景十分广泛。比如
朋友在微信上分享一条资讯, 我点击打开就能跳转到资讯来源的app,并在app指定页面进行打开。浏览网页点击微博相关的网页、点击知乎相关的网页等等,都会弹出以下类型的提示框,提示用户打开相关app进行访问。(当然你需要已经安装了与之相关的app应用)
提示弹框的样式在不同浏览器(或应用)中有所不同,UI效果由其本身决定。

DeepLink的分类

  1. Deeplink: 深度链接,指已安装相应App的情况下,把特定的参数通过url 的形式传递给
    App,从而直接打开指定的内部页面,实现从链接直达App内部页面的跳转。
  2. Deferred Deeplink:延迟深度链接,主要增加了一个是否已安装相应App的判断,用户点
    击链接时,如果未安装App,则引导用户前往应用市场,下载完对应App后,首次打开该
    App时自动跳转进入指定的内部页面。

DeepLink使用与否的区别

在整个流程中,Deeplink 起到的作用是显而易见的,一方面,随着操作步骤的减少,用户体
验也就随之提高;另一方面,用户点击H5链接后-键拉起App,不会再因为找不到相应页面而产生流失,App的分享页面、推广活动、投放广告的价值和转化率都将大大提高。

DeepLink实现原理
deepLink本质上是通过web网页调用Andriod原生App,然后将参数通过URL的形式传递给App实现拉起,App所注册的活动即为拉起的页面,通过获取的URL按照一定逻辑解析成跳转到指定页面所需的数据。

  • scheme
    scheme是用于标识具体跳转app,可以避免歧义对话框的出现(具体样式可参考上方Google浏览器提示框),唯一性的scheme大多用于 app A 启动 app B(比如微信分享的资讯打开相关的app)。而如果是点击检索网页实现跳转app,scheme自然是 http或者https。(允许网页和app皆可解析访问)
  • host
    为了进一步细化能够启动Activity的链接,需host指明URL的具体类型。当然,如果希望Activity能够接收更多链接,可以注册多个标签,对应不同的链接。
    如果多个链接的URL相似,前缀一致,只是路径有所区别,可按照实际需求分情况处理:
    eg: https://www.fenrir-inc.com.cn/aaaa/bbbb/?ccc=sfsifhihloh
    https://www.fenrir-inc.com.cn/ddd/
    https://www.fenrir-inc.comcn/eee/J12526/fff/
    1. 统一在一个Activity中处理,只需要注册scheme,以及host,根据网页跳转的app后获取的URL进行分类讨论。
<activityandroid:name="com.example.android.SplashActivity" ><intent-filter ><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><data android:scheme="https"android:host="www.fenrir-inc.com.cn" /></intent-filter>
</activity>
  1. 在多个Activity中处理,通过使用 android:path 属性或其 pathPattern 或 pathPrefix 变体区分系统应针对不同 URI 路径打开哪个 Activity。
    path、pathPattern、pathPrefix的区别:
  • path 用来匹配完整路径的,只有特定链接才能启动Activity.
  • pathPrefix 用来匹配路径的开头部分,只需指明host之后的部分前缀路径。
  • pathPattern 用来表达式匹配整个路径,就是下面这些匹配符号与转义。
    匹配符号:

    1. “*” 用来匹配0次或者更多次,比如:"a *“就可以包含"a”,“aa”,"aaa"等
    2. “.” 用来匹配任意字符,比如:".“就可以包含"a”,“b”,"c"等
    3. 两种符号组合,". *"就能够批撇任意字符任意次数,比如 ". *html"就包含了“abcchtml”,"pdf.html"等
    4. 当然也要注意转义符号
      在XML文件中,“\字符”是转义的,比如“\n”换行,所以如果想规定处理的链接是以 【“fenrir-inc.com.cn*happy”】结尾,比如这种链接【https://www.fenrir-inc.com.cn/…fenrir.com.cn *happy】(当然没有链接这么怪的)
      那么应该写成这样:
 <intent-filter><action android:name="android.intent.action.VIEW"></action><category android:name="android.intent.category.DEFAULT"></category><data android:scheme="https" android:host="www.fenrir-inc.com.cn" android:pathPattern=".*fenrir-inc\\.com\\.cn\\*happy"></data>
</intent-filter>

对于使用哪种方式处理,应根据项目实际需求决定。求人这边我用的就是第一种,省事些,需要跳转的链接scheme、host都一致的,拿到链接后对其分类讨论就可以了。

在Activity获取链接

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_browser);handleIntent(getIntent);}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);handleIntent(intent);}private void handleIntent(Intent intent){if(intent.getData != null && intent.getAction.equals(Intent.ACTION_VIEW)){String strUri = intent.getData.toString;}}

服务器端配置

服务器端必须在指定路径配置好 assetlink.json,这个json文件采用Digital Asset Links协议让网站和app建立关联绑定。

  1. 自动生成assetlink.json的方法:https://developers.google.com/digital-asset-links/tools/generator

    生成的文件大致如下:
[{"relation": ["delegate_permission/common.handle_all_urls"],"target": {"namespace": "android_app","package_name": "com.example","sha256_cert_fingerprints":["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]}}]

参数说明:

  • relation 关系声明,用于描述目标程序与网站关系
delegate_permission/common.handle_all_urls 向目标授权处理所有url
delegate_permission/common.get_login_creds 向目标授权登录凭证(一般用于google smart lock)
  • target 用于描述需要建立关系的目标
  • namespace 应用标志,可为【web】或者【android_app】
  • package_name 就是Android App包名
  • sha256_cert_fingerprints: Android App key store 证书SHA265指纹

文件放置位置(引用施老师调查结果)

  • 将assetlinks.json文件放在网站根目录的/.well-known/ 中
  • 保证https://hostname/.well-known/assetlinks.json和http://hostname/.well-known/assetlinks.json能正常访问,并且不需要任何代理,以及没有重定向
  • robot.txt中允许爬虫抓取/.well-known/assetlinks.json文件

以上便是关于DeepLink基本配置的内容,接下来就是实际应用。
在实际应用前,我想先谈谈任务栈切换、回退栈的问题,当然这些都是基础性的知识,不过我一开始做的时候确实是没有具体去搞明白,所以走了些许弯路。

任务栈、回退栈

  1. 手机开机启动后, Home(Launcher)所在的Activity在回退栈的栈底。
  2. 从Launcher上的图标点击进入一个应用A(Activity)时,默认在启动整个Activity的Intent的flag里面加入了FLAG_ACTIVITY_NEW_TASK标记。
    (这个标记的作用是:首先会查找是否存在和被启动的Activity具有相同的亲和性的任务栈,如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,如果没有,则新建一个栈来存放被启动的activity)。
    也就是说从launcher启动的Activity默认会在一个新的Task里面。
    比如我们启动了一个应用,ABC三个Activity是同一个应用(ABC没有设置亲和性,默认都是跟随启动它的那个activity的亲和性的),都归为Task2。
  3. 如果在C中启动浏览器那么就会另起一个Task。
  4. 此时按下Home键,那么Task1就会到回退栈的栈顶.
  5. 点击应用A图标(Task2), 由于回退栈中已经存在这个应用的任务栈,所以会复用这个任务栈,并保持栈内的活动顺序不变,也就是点击应用A看到的就是C活动的UI界面。
  6. 此时,如果应用A(Task2)的A活动在IntentFilter注册了 www.xxxx.xx链接启动,这时,切换浏览器,点击检索此链接的网页,选择App打开方式,那么就能够看到启动了应用A(Task2)并显示的是A活动,
    【假设应用A中所有的活动启动模式是Standard】
    任务栈变化过程为:
  7. 为何要提及任务栈呢?
    因为当时做使用DeepLink跳转到SplashActivity时,总是会有以下现象。
    (1)有一个应用只有一个活动MainActivity【standard模式】, 如果先点击应用图标Launcher MainActivity后, 使用浏览器打开这个活动,那么就会新建一个新的活动,每用浏览器打开一次就会启动一个新的MainActivity,而此时点击图标pp却不会启动新的Activity,而是启动栈顶的活动。

(2)可如果,一开始应用并没有启动过,直接使用浏览器打开,启动MainActivity后,我们重复这个操作【使用浏览器打开MainActivity】,浏览器就不会启动新的MainActivity,而是从第二次操作开始,改为直接启动这个应用的任务栈顶部Activity(当然还是MainActivity)。
有意思的是,如果你此时选择点击app图标启动app,那么每点击一次就会创建一个新的MainActivity.

这个就是我不懂的地方
???????????

网上有很多方法,比如将要跳转的活动设置为singleTask等等,这类方法大多不符合实际需求。
为了避免【app通过Launcher启动在后台,使用浏览器跳转到app却仍然会启动新的SplashActivity】的出现,我做了一个较为优解的方法:

1. 新建一个BrowserIntentActivity,专门用于处理浏览器启动app情况

2.每次浏览器启动app时,都会启动这个活动,这个活动是透明的,如果app在后台,那么直接finish,即视觉上“打开了任务栈栈顶的活动”,如果app未启动,则finish,并跳转到SplashActivity,

3.在这个BrowserIntentActivity接收到 链接Uri, 并将其作为MainApplication的一个变量存放.

public class BrowserIntentActivity extends BaseActivity {@InjectBrowserIntentPresenter presenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_browser);val mainApp = MainApplication.getInstance(this);presenter.setMainAppUri(getIntent(), mainApp);finish();createIntent();}// Start SplashActivity or the Activity at the top of the task stackprivate void createIntent() {Intent intent = new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_LAUNCHER);intent.setClass(this, SplashActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);startActivity(intent);}

4. 在需要跳转的活动中,拿到Uri变量,将其分类,解析成目标跳转界面所需的参数,并进行跳转。

HomeFragment.java

 @Overridepublic void onResume() {super.onResume();handleMainAppIntent();}// Get Uri from MainApplicationprivate void handleMainAppIntent() {val mainApp = MainApplication.getInstance(getContext());String strUri = mainApp.getStrUri();if (!strUri.equals("")) {if (strUri.contains("/search/")) {startFragment(, ,);} else if (strUri.contains("/place/")) {startFragment(, ,);} else if (strUri.contains("/genre/")) {startFragment(, ,);} else if (strUri.contains("/style/")) {startFragment(, ,);} else if (strUri.contains("/rail/")) {startFragment(, ,);} else if (strUri.contains("/form")) {startFragment(, ,);} else if (strUri.contains("/job/J") && !strUri.contains("/tel/")) {startFragment(, ,);} else if (strUri.contains("/job/J") && strUri.contains("/tel/")) {sstartFragment(, ,);} else {// Do nothing}}   mainApp.setStrUri("");}

DeepLink的使用及任务栈问题相关推荐

  1. 浅谈2018年的MarTech技术栈

    MarTech越来越热,有时甚至成为一种营销社交术语,很多人高谈阔论,很多人迷茫困惑,很多人知道MarTech的缘起,却不知道未来,我也是算其中一个. 第一部分 MarTech和AdTech 经常有人 ...

  2. Android Jetpack Navigation组件(四):DeepLink(深链接)

    目录 前言 一.DeepLink定义 二.显式DeepLink 1.简介 2.创建显式DeepLink 3.NavDeepLinkBuilder接口说明 (1).NavDeepLinkBuilder( ...

  3. 伍六七带你学算法——栈的使用

    大家都知道栈这种数据结构,它有非常多的应用场景.但如果我们不经常接触这些应用场景的话,就可能不太熟悉栈的用法. 目录smd 1.栈的创建和使用 JAVA Stack类: 2.栈的实际应用示范 解题如下

  4. 翻转二叉树 c语言实现 递归 栈 队列

    前言 题目比较好理解,就是翻转二叉树 代码 c语言实现 #include<stdio.h> #include<stdlib.h> #include<string.h> ...

  5. python 通过双栈实现队列

    开始做法 # coding:utf-8# !/usr/bin/env python# Time: 2018/6/6 9:32# Author: sty# File: stack_queue.pycla ...

  6. 汇编语言将数据、代码、栈放入不同段基础

    Code内部逻辑: Start 初始化各段寄存器 入栈 出栈 物理逻辑: 代码实现: assume cs:code, ds:data, ss:stack; // 代码段cs.数据段ds.栈段ssdat ...

  7. 汇编语言中栈及栈操作的实现

    栈:数据始终采用先进后出的特点. 8086CPU提供入栈和出栈指令,基本操作:PUSH(入栈)和POP(出栈)操作. 在8086CPU中,有两个寄存器,段寄存器SS和寄存器SP: SS: 存放栈顶的段 ...

  8. 汇编语言中将数据、代码、栈放入不同的段

    数据.代码.栈放入不同的段 在学习汇编语言,将数据.代码.栈放入不同的段.参考王爽老师的<汇编语言>第四版,对P133的汇编代码,进行了个人理解标注.仅供参考,存在错误之处,请大家斧正. ...

  9. Java堆和栈的基本理解

    Java 堆和栈的区别 参考背景: 堆内存:用来存放由new创建的对象和数组: 栈内存:存放基本类型的变量,对象的引用变量: 堆存放的原因:由于在堆中创建对象(或数组)后,可在栈中定义一个特殊变量,让 ...

最新文章

  1. 2020-11-15(IEEE浮点数计算)
  2. centos7 时间设置
  3. r语言echarts画箱线图_R语言之数据可视化---交互式图表recharts
  4. JEECG商业版本授权说明(仅限企业用户)
  5. python3数据库配置,远程连接mysql服务器
  6. 二进制,八进制,十进制,十二进制之间的转换!!!!!!!!!
  7. jupyter notebook python插件_Python文学化编程-Jupyter notebook使用和插件拓展
  8. oracle adjusting parallel,ora-29740故障求救
  9. android root权限命令行,如何通过命令行取得安卓root权限
  10. 远程桌面由于以下原因之一无法连接到远程计算机解决方法(亲测)
  11. X310工作原理及设备描述详细信息
  12. Topic 14. 临床预测模型之校准曲线 (Calibration curve)
  13. Vue实现push数组并删除方法
  14. java实习报告范文
  15. Nginx做缓存服务器
  16. 微信小程序 —— 瀑布流简单写法(css3属性加wx:if判断轻松实现)
  17. ZeroDivisionError:Integer division or modulo by zero
  18. 基于SSM框架的旅游网站的设计与实现
  19. 基于web的HMI / SCADA软件
  20. 几道经典递归算法案例

热门文章

  1. 基于X5内核的UI自动化
  2. python中msg函数_Python之函数
  3. Python毕业设计论文计算机专业毕业论文基于Python实现的作业查重系统[包运行成功]
  4. Excel工作表加密、撤销保护
  5. 基于重要抽样技术的非序贯蒙特卡洛法(Matlab代码实现)
  6. 【Spring Data JPA】基于 JpaRepository 增删改查
  7. [BZOJ5042]LWD的分科岛
  8. shiro的 认证 与 授权
  9. 计算机病毒过滤论文,计算机病毒毕业论文
  10. windows删除远程桌面连接记录 | 历史IP记录