assetlinks.json中的应用链接意图过滤器在Android上无法工作

47

我的应用程序定义了意图过滤器,用于处理由我的网站定义的URL。

<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <data android:host="www.host.com" android:scheme="http"/>
</intent-filter>
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW"/>
  <category android:name="android.intent.category.DEFAULT"/>
  <category android:name="android.intent.category.BROWSABLE"/>
  <data android:host="www.host.com" android:scheme="https"/>
</intent-filter>

此应用程序能够正确检测到来自正确主机的URL,但会询问用户是在应用程序中打开还是在浏览器中打开。我尝试使用此处指定的应用链接验证:https://developer.android.com/training/app-links/index.html

从我的服务器日志中可以看到,在安装应用程序时,设备会查询 /well-known/assetlinks.json,并响应200状态。使用以下数字资产文件进行测试:

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://<domain1>:<port>&relation=delegate_permission/common.handle_all_urls

API发现没有错误。

assetlinks.json文件中的SHA256是使用以下方式获得的:

keytool -list -v -keystore my-release-key.keystore 

应该使用与应用签名相同的 .keystore 文件。

运行 adb shell dumpsys package d 命令,返回链接验证状态为“询问”,表示验证失败。为什么验证会失败呢?


1
嗨@mohamed.ahmed,你能解决这个问题吗?我遇到了类似的问题。我的Android应用程序链接在上传到Play商店之前对签名APK有效,但在上线后停止工作。请参考以下链接获取更多信息: https://dev59.com/o1MH5IYBdhLWcg3w10B_ - Pruthvi
也许你已经将本地证书的SHA256放入了资产文件(在服务器上)。你需要放入来自PlayGoogle的SHA256。https://dev59.com/o1MH5IYBdhLWcg3w10B_#61204765 - linhadiretalipe
12个回答

18

对我们来说,问题出在Windows的行尾符!

测试"https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://domain1:port&relation=delegate_permission/common.handle_all_urls"非常有价值,因为它给了我们一个“无法解析语句列表(不是有效的JSON)”错误,这导致我们找到了问题所在。

提示:使用Android Studio App Links助手中的“保存文件”按钮而不是像我们一样复制和粘贴,这样它就会自动生成文件,保证不会出现此问题。


1
这个答案让我朝着正确的方向前进,但对我来说,实际上是字节顺序标记(BOM),而不是行结尾符。我删除了BOM,并且文件在Windows行结尾符下可以正常工作。请参阅我的答案,了解如何删除BOM的详细信息。 - Brian Rak
我先将其剪切并粘贴到记事本中,这为我解决了问题。找不到保存文件按钮。 - MadMac

17

有一些常见的问题,您应该仔细检查(我不是说您做错了,这只是一个检查清单):

  1. 验证assetlinks.json文件是否有效,并且可以从https://example.com/.well-known/assetlinks.json访问存储。您需要访问https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls来进行验证,不能有任何错误。
  2. 如果您同时链接多个域名,请检查所有域名是否像第一步设置正确。
  3. 确保那些包含您的<data>标签的<intent-filters>具有属性android:autoVerify="true"
  4. 验证您的<application>标签中是否有所需的<meta-data>标签:

    <meta-data
        android:name="asset_statements"
        android:resource="@string/asset_statements"/>
    

    asset_statements字符串的内容必须为:

    <string name="asset_statements" translatable="false">[{\"include\": \"https://example.com/.well-known/assetlinks.json\"}]
    
  5. 还可以使用发布签名证书进行调试(不要担心,您不会意外上传它),在build.gradle文件中使用以下代码:

  6. buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
        debug {
            debuggable true
            signingConfig signingConfigs.release
        }
    }
    

另外,我想补充一点,如果您在实现时使用了“.well-known”进程,并且具有相同根主机但指向同一活动的不同路径值,则应用程序链接将无法正常工作。我需要这个功能来同时在我的测试服务器和生产环境中测试我的功能。 - Beemo
@string/asset_statements 如何帮助?特别是当它只有一个 URL 时,而在 AndroidManifest 中我们有几个。 - CoolMind
很奇怪,但是当我将AndroidManifest中的两个域名都添加到asset_statements中时,它起了作用。但后来又不行了,所以我删除了<meta-data> - CoolMind

15

看了j__m的评论,我发现了这个。

  1. In AndroidManifest write so:

     <intent-filter android:autoVerify="true">
         <action android:name="android.intent.action.VIEW" />
    
         <category android:name="android.intent.category.DEFAULT" />
         <category android:name="android.intent.category.BROWSABLE" />
    
         <!-- Write <data> tags with one attribute, if you use several domains. -->
         <data android:scheme="https" />
         <data android:host="example.com" />
     </intent-filter>
     <!-- Other domains-->
     <intent-filter android:autoVerify="true">
         <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" />
         <data android:host="server.com" />
     </intent-filter>
    

在App链接中需要使用android:autoVerify="true"

  1. Create assetlinks.json using Tools > App Links Assistant. Then press Open Digital Asset Links File Generator, enter domain, application id, select release signing config and press Generate Digital Asset Links File. Then you can save file or copy to clipboard.

  2. You can create several assetlinks.json files (for several applications) and join them into one JSON. To my mind it doesn't depend on Windows line endings (I used Notepad to concatenate JSONs). First time I auto-formatted it with Ctrl + Alt + L, and after uploading to domains App Link didn't work (probably because of later errors in AndroidManifest), so on the 2nd try I didn't format JSON. I created assetlinks.json for release and debug builds of the application.

  3. Upload assetlinks.json to https://example.com/.well-known/assetlinks.json (in this answer I write: example.com and mean your domain like company.name). Check it with https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls. The file and the domain have some restrictions. In my case everything was simple, we didn't change settings.

  4. In your DeepLinkActivity you can parse URLs with regular expressions. Use JUnit to create tests. Call this method from onCreate():

     private fun processDeepLink() {
         if (intent?.data?.isHierarchical == true) {
             val data = intent?.dataString
             if (intent?.action == Intent.ACTION_VIEW && data != null) {
                 when {
                     REGEX.matches(data) -> // Get id and open some screen.
                     else -> // Either open MainActivity or skip this URL (open web browser instead).
                 }
                 finish()
             }
         }
     }
    
     companion object {
         val REGEX = "^https://example.com/some_request/(\\d+).*".toRegex()
     }
    

警告!如果你在应用程序中打开一个网页浏览器,你将会陷入循环。当在应用程序中点击到你的域名链接时,不会出现浏览器,但你的应用程序会自动打开!这是多么惊喜啊!所以,在processDeepLink中,你应该检查URL并在URL与你的掩码之一匹配时打开MainActivity。跳过其他的。现在,用户将看到一个对话框,其中列出了浏览器和你的应用程序(就像在Deep Link中一样)。这是因为你的应用程序也处理指向你的域名的链接,就像浏览器一样。

你也可以使用WebView代替浏览器(不是一个好的解决方案),打开Chrome Custom TabsChrome

  1. Use device with Android 6 or later.

  2. If https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls returned no errors, build the application. Create an Email message, SMS, QR-code or another application with a link to your domain. Click it, and App Link will open your application or Deep Link will show a dialog to choose an application. If App Link didn't work, read later.

  3. In LogCat select No Filters and type IntentFilter into search box. There should be:

     I/IntentFilterIntentOp: Verifying IntentFilter. verificationId:2 scheme:"https" hosts:"example.com" package:"com.my_package".
     I/IntentFilterIntentOp: Verification 0 complete. Success:true. Failed hosts:.
    
可能您会获得以下内容:
I/IntentFilterIntentOp: Verifying IntentFilter. verificationId:0 scheme:"https" hosts:"example.com server.com" package:"com.my_package".
I/IntentFilterIntentOp: Verification 0 complete. Success:false. Failed hosts:server.com.
  1. Later you will try to fix domains in the application, so sometimes you can launch for clean install:

     adb shell pm clear com.android.statementservice
    
  2. Start adb shell dumpsys package d and find your domains. There should be:

    Package Name: com.my_package
    Domains: example.com server.com
    Status:  always : 200000000
    
但很可能会是这样的:
Package Name: com.my_package
Domains: example.com server.com
Status: ask

请参考https://chris.orr.me.uk/android-app-linking-how-it-works/。在模拟器中,它总是写着always,但是应用链接却无法使用。
我还尝试了adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "https://example.com"来测试应用链接,但后来它也无法使用。
  1. If you have several domains, comment (or remove) other domains in AndroidManifest (retain only one domain, for example, "example.com"). Then click a URL https://example.com/something and check it uses App Link. In my case I checked release and debug builds of the application. While the debug build worked with App Link, release didn't (and sometimes vise versa). I used the solution of rekire:

    <meta-data
        android:name="asset_statements"
        android:resource="@string/asset_statements"/>
    

最初我使用了App Link,它对两个域名起到了帮助作用,但后来停止了,所以我将其移除。最终,我在AndroidManifest中编写了一个属性为j__m所说的<data>标签。

即使只有一个域名失败,App Link也无法为其他域名工作。您可以在AndroidManifest中逐个检查域名,每次只保留一个域名。

另请参阅http://androidideas.com/handling-app-links-in-android/https://willowtreeapps.com/ideas/a-better-user-experience-for-deep-linking-on-androidhttps://developer.android.com/training/app-links/verify-site-associations


11

对我而言,这归结于检查所有基本要素:

  1. 使用此工具验证我的assetLinks文件是否正确:(将domain1:port替换为您的域名) https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://domain1:port&relation=delegate_permission/common.handle_all_urls
  2. 始终使用已签名的APK进行测试
  3. 确保测试设备运行Android 6.0或更高版本(这是因为我忘记了它而导致的问题-在旧版本的Android上,您总是会收到用户提示)

6

更新

我解决了我的问题。不确定哪一步起作用(可能是多方面的因素),但这是我所做的:

  • 卸载“Google Play Services for Instant Apps”:我之前曾尝试使用即时应用程序,因此我认为可能会保留一些旧配置,例如调试包名称,但这不太可能。
  • 停止使用代理:代理可用于调试网络请求,但工具可能不完全支持HTTP/2。
  • 删除旧版子域的intent筛选器:这是关键。我的一个子域已被弃用,并不再可用。在AndroidManifest中,如果您为包含至少一个自动验证intent筛选器的活动声明了多个主机名,则检查每个主机是否有Digital Asset Link JSON文件。 如果甚至对其中一个主机的自动验证失败,则所有主机都无法自动验证。

原始内容

当我第一次遇到这个问题时,是因为我的网络阻塞了与Google服务器验证应用链接的调用。

正如楼主和其他答案所提到的那样,通常情况下,要绕过选择器对话框,必须成功调用以下端点的API:

digitalassetlinks.googleapis.com

这是Android系统进行的Web调用,用于验证Digital Asset Link JSON文件,并似乎在应用程序安装/更新时进行。一个有用的查找位置是Logcat,搜索具有文本“I/SingleHostAsyncVerifier:”的项目。如果在日志的结尾处看到“--> true”,则您的应用程序

然而最近,这些调用一直失败,因为似乎出了一些错误,最近可能引入了这个bug。设备从上述API调用中收到以下响应:

Error: unavailable: Wrong content type in HTTP response headers while fetching statements from {host}/.well-known/assetlinks.json (which is equivalent to '{host}/.well-known/assetlinks.json'): expected 'Content-Type: application/json' but found text/html [11] while fetching Web statements from {host}./.well-known/assetlinks.json

自我上次查看这些请求以来已经有一段时间了,因此我不记得它们之前的样子。但是似乎可能有一些最近的更新涉及应用链接或Android网络框架,其中它们转换为协议缓冲区以支持此功能(并忘记在另一个功能上支持它)。

另一个表明情况可能发生变化的迹象是,今天的请求路径似乎与先前答案中提到的不同:

https://digitalassetlinks.googleapis.com/google.digitalassetlinks.v1.AssetLinks/Check


6
对我来说,问题在于我的assetlinks.json文件是UTF-8编码的,并且包含一个字节顺序标记(BOM),它是文件头部的三个字节的特殊字符,用于向消费程序表示编码方式。BOM是可选的,显然Google/Android工具不喜欢看到它。当存在BOM时,Google的数字资产链接验证器(下面附有URL)会给我一个“格式错误的JSON”错误。
如果您正在使用Visual Studio,则可以按照以下步骤确定文件中是否存在BOM,并在必要时将其删除:
  1. 右键单击您的assetlinks.json文件。
  2. 从上下文菜单中选择“打开方式...”。
  3. 在“打开方式”对话框中选择“二进制编辑器”。
  4. 检查文件字节。如果文件以EF BB BF开头,那就是问题所在。
  5. 删除这些字符(您可以通过任一列来完成此操作)并保存文件。
  6. 重新上传文件并使用Google工具进行测试(下面附有URL),则应该可以正常工作。
这是您可以使用的URL来检查文件(将example.com替换为实际URL):

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls


3

我相信这并不能回答原始问题,因为我认为这比Android App Bundles更早,但最终导致我的失败的是我启用了Google Play Console重新签名应用程序(需要AABs),因此我从 keytool 获取的SHA-256指纹与下载应用程序的数字签名不匹配。

使用控制台中的指纹更新我的 assetlinks.json 解决了这个问题。


1
在我们的情况下,我们在清单文件中有两个带有应用链接的意图过滤器:一个具有autoVerify="true",一个没有。
因此,验证器尝试验证第二个意图过滤器的域名失败,并将所有我们的应用链接视为"未经验证"。您可以在this question中找到更多详细信息。
您必须确保每个应用链接都可以得到验证(这意味着为每个要验证的域添加assetlinks.json)。

0

两种情况下的系统应用选择窗口

1)用户通过进入设置>应用程序>齿轮图标>打开链接相关设置>选择一个应用程序>打开支持的链接>选择每次提示来更改设置。

2)用户未设置默认应用程序,并且在支持应用程序之一的应用链接中未启用自动验证。

我认为在您的情况下,自动验证已启用,请检查用户设置。


0
在我的情况下,adb shell dumpsys package d 显示 packageNameassetlinks.json 中配置错误。我在 AndroidManifest.xml 文件中使用了 manifest 标签的 package 属性值,但我应该在 build.gradle 文件中使用 android.defaultConfig.packageId 的值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接