深度链接(通用链接或应用程序链接)教程太多了。但大多数都展示了如何在 Android 或 IOS 应用程序中启用它。还有付费云解决方案,但它们提供了很多功能。但我在现实生活中面临三个主要问题:
- 某些浏览器不允许应用程序链接工作。例如,您可以将 http://example.com 配置为在应用程序中捕获,但如果用户通过 Facebook 应用程序单击此链接,则不会处理该链接,并且 Facebook 浏览器会显示该网站。
- 没有唯一的标准解决方案来处理 Android 和 IOS 应用程序的链接。
- 如果移动设备上未安装应用程序并且用户单击应用程序链接,则没有实际的解决方案。
我写了这个问答,这是我研究(花了太多时间)的结果,以获得一个独特且适用于所有案例的解决方案。
这些代码来自我的工作解决方案,但我删除了一些部分只是为了展示这个想法。如果编译出现问题,请按照算法编写自己的代码
这是解决方案,即使你知道一些步骤,也要一步一步来,因为代码中有技巧。代码部分的注释行中也有一些解释,请阅读它们。
示例是通过 Android 和 IOS 应用程序处理深度链接 http://example.com/v/,并在末尾添加参数,例如 http://example.com/v/id-of-user?key=value。
1. 配置安卓
1.1 将活动信息添加到 AndroidManifest.xml 文件中:
<activity
android:name=".appLinkHandlerActivity">
<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:host="example.com"
android:pathPrefix="/v/"
android:scheme="http" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!—this intent is needed to handle links to myapp://share, I will explain later why we need it -->
<data
android:host="share"
android:scheme="myapp" />
</intent-filter>
1.2 创建一个名为 appLinkHandlerActivity 的活动,它将处理单击的链接
public class appLinkHandlerActivity extends AppCompatActivity {
/* assume that user is clicked http://example.com/v/my-user-id
actCode will be “v”, pCode will be “my-user-id” */
String actCode="", pCode="";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ATTENTION: This was auto-generated to handle app links.
Intent appLinkIntent = getIntent();
String appLinkAction = appLinkIntent.getAction();
Uri appLinkData = appLinkIntent.getData();
String code = null;
try {
code = getIntent().getData().getLastPathSegment();
} catch (Exception e) {
}
if (code == null) {
Intent i = new Intent(this, {your main activity.class});
startActivity(i);
}
List<String> params=appLinkData.getPathSegments();
if (params.size()>0)
actCode=params.get(0);
if (params.size()>=2)
pCode=params.get(1);
/* assume that user is clicked http://example.com/v/my-user-id actCode is “v”, pCode is “my-user-id” Do now whatever you need. */
}
}
2.配置IOS
这比Android更复杂,我将在这里解释必要的要点。请参考文档:https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1 https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1
https://www.raywenderlich.com/128948/universal-links-make-connection https://www.raywenderlich.com/128948/universal-links-make-connection
2.1 在 Apple Developer Portal 上创建 App ID 时,您必须启用关联域。重要问题:您需要拥有购买的 Apple 开发者帐户才能启用此选项,这意味着如果不购买开发者帐户,您将无法在 IOS 项目上尝试 AppLinks。
2.2 在XCode项目中,打开“Capabilites”选项卡并将Associated Domains切换为On。如果您没有在 Apple Developer Portal App ID 部分启用关联域,则可能无法选择
单击关联域选项下的 + 按钮添加权利,写入“applinks:example.com”。
2.3 在您的 Web 服务器上创建一个名为“apple-app-site-association”的文件,并且必须通过 https://example.com/apple-app-site-association 访问此文件 如果 HTTPS 不是有效的,则 HTTPS 是强制的SSL 证书应用程序链接可能不起作用。将以下行添加到 apple-app-site-association 文件中:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "6HY7TF56.com.example.app",
"paths": [ "/ios/*", "/v/*" ]
}
]
}
}
appID 的格式为{“Team ID”.“Bundle ID of your App”}。您可以在开发者门户的“会员详细信息”部分找到您的 teamID。
我们处理链接 http://example.com/v/parameters,但在这里您会看到“/ios/*”有另一个路径配置。需要绕过不支持的浏览器,我稍后会解释。
2.4 在 AppDelegate.m 文件中添加两个方法来处理用户对 example.com 的点击
-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler{
if ([userActivity.activityType isEqualToString: NSUserActivityTypeBrowsingWeb]) {
NSURL *url = userActivity.webpageURL;
[self parseUrl:url];
}
return YES;
}
- (void) parseUrl:(NSURL * )handledUrl {
NSString *urlStr=@"";
NSString *pCode=@"";
NSString *handledUrlStr=[handledUrl parameterString];
NSString *handledUrlQueryPart;
NSArray<NSString *> *pathsArray= [handledUrl pathComponents];
//remember that we only added paths “/v/*” and “/ios/*” to handle in apple-app-site-association file. If you want to handle more subpaths you can add them into apple-app-site-association file, then below if-else conditions. Don’t touch to config and code for “/ios/*” path, it is needed to bypass unsopported browsers.
if ([pathsArray[1] isEqual: @"v"]){
//sample url= http://example.com/v/menu?aaa=bbb
pCode = pathsArray[2];
handledUrlQueryPart=[handledUrl query];
} else if ([pathsArray[1] isEqual: @"ios"]){
//sample url= http://example.com/ios/deeplink-ios.php?/v/menu?aaa=bbb
NSArray *uriArray = [[handledUrl query] componentsSeparatedByString:@"?"];
NSArray *queryPathsArray = [uriArray[0] componentsSeparatedByString:@"/"];
if ([queryPathsArray count] > 2)
pCode = queryPathsArray[2];
if ([uriArray count] > 1 ){
handledUrlQueryPart=uriArray[1];
}
}
/* here pCode is the parameter what is passed from user. If the link clicked is http://example.com/v/menu it is “menu”. If the link clicked is http://example.com/v/menu?aaa=bbb it is “menu?aaa=bbb”. So you can do now what ever you need. */
}
3. 管理未捕获的点击。
3.1 好的,您的 Android 和 IOS 应用程序应该处理链接 http://example.com/v/blabla 的点击,并将“blabla”参数传递给我展示的方法中使用的 pCode 变量。但某些浏览器(例如 Facebook 应用程序)可能会禁用应用程序链接。在这种情况下,用户单击将转到您的 Web 服务器,浏览器会尝试显示 http://example.com/v/blabla 的内容,这可能是 404 Page Not Found。为了处理这些点击,我们将配置 Apache Web 服务器并将用户重定向到您的应用程序。如果你使用 IIS 或其他,我不知道该怎么做,但你可以以此为示例并使用相同的算法来配置你的 Web 服务器。
3.2 在 example.com 根目录下的 .htaccess 文件中写入以下行
#redirect to deeplink
<IfModule mod_rewrite.c>
#if there is a request to example.com/v/any-sub-path, redirect them to example.com/deeplink.php file. This rule is for both IOS and Android
RewriteRule ^(v)/.* /deeplink.php [L]
#if there is a request to example.com/ios/any-sub-path, redirect them to app installation page. That means your app is not installed on IOS device. This rule is for IOS devices only
RewriteRule ^(ios)/.* http://itunes.apple.com/install-of-your-app [L]
</IfModule>
4. 将用户重定向到应用程序
4.1 步骤3中显示的.htaccess文件中的重定向规则将用户重定向到deeplink.php文件。因此,这是该文件的内容,用于将用户重定向到您的应用程序。
<?php
$request_uri=$_SERVER[REQUEST_URI];
$ua = strtolower($_SERVER['HTTP_USER_AGENT']);
if(stripos($ua,'android') == true){
//if user device is android, redirect it to intent url which is handled by Android.
$redir_tag="<meta http-equiv='refresh' content='0;url=intent://share$request_uri/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;package=com.example.app;end' />";
//scheme=myapp and host named “share” is configured in AndroidManifest.xml file to be handled by the activity.
//fallback url is the url, if your app is not installed on android device, so you can redirect them to a page to install android app. In some cases users are redirected to Play Store directly for application id of com.example.app
}
else if ( (stripos($ua,'iPhone') == true) || (stripos($ua,'iPad') == true) ) {
//if user device is IOS, redirect them to a web page to see. There will be a link here to the another handled link: http://example.com/ios/blabla.
// due to my experience there is no way to redirect IOS to app directly at this stage, user must click a link on browser and that link must be different than the link which was shown and clicked at first.
// another experience taught me ther ecan be problems if we redirect users to a web page under example.com which is configured as applink, so I redirect users to a page under deep.example.com here
$redir_tag="<meta http-equiv='refresh' content='0;url=http://deep.example.com/deeplink-ios.php?$request_uri' />";
}
else {
//If the device is neither IOS nor Android, redirect users to a web page which gives information that this link is for Android and IOS only
$redir_tag="<meta http-equiv='refresh' content='0;url=http://example.com/non-mobile' />";
}
?>
<html>
<head>
<!— add tags for no-caching, this is important, the date below is my son’s birth time and date, he is now 6, so you can use it as a date in the past -->
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="-1" />
<meta http-equiv="expires" content="Tue, 31 May 2011 10:15:00 GMT+3" />
<meta http-equiv="pragma" content="no-cache" />
<?php echo $redir_tag; ?>
</head>
</html>
5.向IOS用户显示一个可以点击的链接
5.1 这是http://deep.example.com/deeplink-ios.php 文件的内容。用户将看到如下页面,当他们点击链接时,该请求应该由您的 IOS 应用程序处理。
<?php
//we create a link to http://example.com/ios/… which is configure to be handled by IOS app. IOS needs to be a user click in some cases to handle the request, that is why this page is shown to the user
$request_uri=$_SERVER[REQUEST_URI];
$link="<div class='w3-container w3-card'><h1><a href='http://example.com/ios$request_uri'>Click here to open MyApp</a></h1></div>";
?>
<html>
<head>
<!—adding no-cache tags is also important here-->
<meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="-1" />
<meta http-equiv="expires" content="Tue, 31 May 2011 10:15:00 GMT+3" />
<meta http-equiv="pragma" content="no-cache" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3mobile.css">
</head>
<body>
<?php echo $link ?>
</body>
</html>
六、案例分析:
6.1 安卓
6.1.1 案例1 – 应用程序安装在设备上
• 浏览器请求http://example.com/v/blabla
• Android 捕获链接并创建在清单文件中配置的活动
6.1.2 案例2 - 应用程序安装在设备上
• 浏览器请求http://example.com/v/blabla
• Android 无法捕获链接。
• 浏览器连接到 Web 服务器,请求 /v/blabla
• 由于.htaccess 文件中的配置,它被重定向到deeplink.php?/v/blabla
• deeplink.php 得知它是android,并重定向到:intent://share/v/blabla/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;包=com.example.app
• Android 捕获针对 Intent:// 的请求,因此由于清单文件 myapp://share/v/blabla 中的配置,由 Activity 处理
6.1.3 情况3-应用程序未安装
• 浏览器请求http://example.com/v/blabla
• Android 无法捕获链接。
• 浏览器连接到 Web 服务器,请求 /v/blabla
• 由于.htaccess 文件中的配置,它被重定向到deeplink.php?/v/blabla
• deeplink.php 得知它是android,并重定向到:intent://share/v/blabla/#Intent;scheme=myapp;S.browser_fallback_url=http%3A%2F%2Fexample.com%2Fget-app%2F;包=com.example.app
• Android 捕获针对intent:// 的请求,但没有为id: com.example.app 安装应用程序。在某些情况下,它会退回并将浏览器重定向到 http://example.com/get-app 或应用程序的 Play 商店安装页面
6.2 IOS
6.2.1 案例1 – 应用程序安装在设备上
• 浏览器请求http://example.com/v/blabla
• IOS 捕获链接并调用 AppDelegate.m 中的 continueUserActivity 方法
6.2.2 案例2 – 应用程序安装在设备上
• 浏览器请求http://example.com/v/blabla
• IOS 无法捕获链接。
• 浏览器连接到 Web 服务器,请求 /v/blabla
• 由于.htaccess 文件中的配置,它被重定向到deeplink.php?/v/blabla
• deeplink.php 得知它是 IOS,并重定向到:http://deep.example.com/deeplink-ios.php?/v/blabla
• deeplink-ios.php 文件向用户显示URL。网址是:http://lify.me/ios/v/blabla
• 用户单击 URL,浏览器请求 http://lify.me/ios/v/blabla
• IOS 根据 apple-app-site-association 文件中路径“/ios/*”的配置捕获请求,并调用 AppDelegate.m 中的 continueUserActivity 方法
• 如果 IOS 由于任何原因无法捕获 http://lify.me/ios/v/blabla 的请求,则会表现为设备上未安装应用程序。看看那个案例。
6.2.3 案例2 – 设备上未安装应用程序
• 浏览器请求http://example.com/v/blabla
• IOS 无法捕获链接。
• 浏览器连接到 Web 服务器,请求 /v/blabla
• 由于.htaccess 文件中的配置,它被重定向到deeplink.php?/v/blabla
• deeplink.php 得知它是 IOS,并重定向到:http://deep.example.com/deeplink-ios.php?/v/blabla
• deeplink-ios.php 文件向用户显示URL。网址是:http://lify.me/ios/v/blabla
• 用户单击 URL,浏览器请求 http://lify.me/ios/v/blabla
• 如果 IOS 无法捕获 http://lify.me/ios/v/blabla 的请求
• 浏览器连接到 Web 服务器,请求 /ios/v/blabla
• 由于 Web 服务器上 .htaccess 文件中的配置,它被重定向到 http://itunes.apple.com/install-of-your-app
6.3 非Android或IOS设备上点击App Link
• 浏览器请求http://example.com/v/blabla
• 设备操作系统无法捕获链接。
• 浏览器连接到 Web 服务器,请求 /v/blabla
• 由于.htaccess 文件中的配置,它被重定向到deeplink.php?/v/blabla
• deeplink.php 得知它既不是 IOS 也不是 Android,并重定向到:http://example.com/non-mobile