---
title: Android 采集
description: 使用 Custom Tabs，通过 Human Plugin 创建流程返回的链接，在 Android 应用中运行 Journey 流程的采集。
canonical: https://developer.unico.io/zh-CN/human-plugin/integrations/android
locale: zh-CN
generated_by: markdown-export
---

# Android 采集

在 Custom Tab 中打开采集页面，并让用户返回您的应用。

您的后端使用 Journey 流程在 Human Plugin 中创建流程，并提供一个使用您应用 URL scheme 的 `callbackUri`。应用在 [Custom Tab](https://developer.chrome.com/docs/android/custom-tabs) 中打开响应里的 `link`。采集结束时，浏览器会打开您的 scheme，由应用接管。

请保存好创建响应中的 Human Plugin `id`。您的后端用它来读取结果。

## 前提条件

- Android 8（API 26）或更高版本
- `androidx.browser:browser:1.5.0`
- 您应用专属的 URL scheme，例如 `myapp`

## 1. 添加依赖

在 `build.gradle` 中：

```groovy
implementation("androidx.browser:browser:1.5.0")
```

## 2. 使用您的 scheme 创建流程

您的后端将 scheme 作为 `callbackUri` 发送：

```json
{
  "flow": "humantoken",
  "clientId": "client-a",
  "correlationId": "order-1001",
  "credential": { "type": "DUI_TYPE_NG_BVN", "value": "<document number>" },
  "callbackUri": "myapp://callback"
}
```

并将 `id` 和 `link` 返回给应用。

## 3. 创建 Activity

```kotlin
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent

class CaptureActivity : AppCompatActivity() {

    private var processId: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        processId = intent.getStringExtra(EXTRA_PROCESS_ID)
        val link = intent.getStringExtra(EXTRA_LINK) ?: return finish()

        CustomTabsIntent.Builder()
            .build()
            .launchUrl(this, Uri.parse(link))
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        if (intent.data?.scheme == "myapp") {
            // 采集已结束。请让您的后端使用 Human Plugin 的 id，
            // 通过 GET /v1/processes/{id} 读取结果。
            onCaptureEnded(processId)
        }
    }

    private fun onCaptureEnded(processId: String?) {
        finish()
    }

    companion object {
        const val EXTRA_PROCESS_ID = "process_id"
        const val EXTRA_LINK = "link"
    }
}
```

## 4. 在 `AndroidManifest.xml` 中注册 scheme

```xml
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />

<activity
    android:name=".CaptureActivity"
    android:launchMode="singleTop"
    android:exported="true">
    <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="myapp" android:host="callback" />
    </intent-filter>
</activity>
```

`singleTop` 会让 Android 将返回事件交给同一个 Activity 的 `onNewIntent()`。

## 5. 启动采集

```kotlin
val intent = Intent(this, CaptureActivity::class.java)
    .putExtra(CaptureActivity.EXTRA_PROCESS_ID, id)
    .putExtra(CaptureActivity.EXTRA_LINK, link)
startActivity(intent)
```

## 小结

| 文件 | 用途 |
|---|---|
| `build.gradle` | `androidx.browser` 依赖 |
| `CaptureActivity.kt` | 打开 Custom Tab 并处理返回 |
| `AndroidManifest.xml` | 您的 scheme 对应的 intent filter |

```mermaid
sequenceDiagram
  participant A as Your app
  participant B as Your back-end
  participant H as Human Plugin
  participant T as Custom Tab
  A->>B: start the capture
  B->>H: POST /v1/processes (Journey flow, callbackUri myapp://callback)
  H-->>B: 201, id + link
  B-->>A: id + link
  A->>T: open link
  Note over T: capture
  T-->>A: myapp://callback
  A->>B: capture ended (id)
  B->>H: GET /v1/processes/{id}
  H-->>B: 200, final state
```

## 使用 WebView

> **警告:** 优先使用 Custom Tabs。仅在无法使用 Custom Tabs 时才使用 WebView。

采集需要 JavaScript、DOM 存储、媒体自动播放，以及相机和位置权限：

```kotlin
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.settings.mediaPlaybackRequiresUserGesture = false
webView.settings.setGeolocationEnabled(true)

webView.webChromeClient = object : WebChromeClient() {
    override fun onPermissionRequest(request: PermissionRequest) {
        if (PermissionRequest.RESOURCE_VIDEO_CAPTURE in request.resources) {
            request.grant(arrayOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE))
        } else {
            request.deny()
        }
    }

    override fun onGeolocationPermissionsShowPrompt(
        origin: String,
        callback: GeolocationPermissions.Callback
    ) {
        callback.invoke(origin, true, false)
    }
}
```

请在 `AndroidManifest.xml` 中声明这些权限，并在加载 `link` 之前于运行时请求相机和位置权限：

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```

在 WebView 中，请直接打开 `link`。不要在其中加载 Journeys SDK。