Intent.parseUri()详解

本文深入探讨了Android中Intent.parseUri()方法的工作原理,详细分析了如何通过该方法从URI中解析并创建Intent对象,涵盖了从数据、action到额外参数的完整过程,特别强调了与toUri()方法的互操作性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Intent.parseUri()详解

由于经常会有类似下面的控制启动界面代码:

this.startActivity(Uri.parse(url));
this.startActivity(Intent.parseUri(url));

注意两者区别
第一种写法只能使用类似 http(s)/file等 协议开头的url,
而第二种写法除了以上协议外,还可以启动Activity界面

至于如何启动Activity,则是我们本篇分析的主要内容

实际上 我们分析源代码可知

源代码如下:

/**
 * Create an intent from a URI.  This URI may encode the action,
 * category, and other intent fields, if it was returned by
 * {@link #toUri}.  If the Intent was not generate by toUri(), its data
 * will be the entire URI and its action will be ACTION_VIEW.
 *
 * <p>The URI given here must not be relative -- that is, it must include
 * the scheme and full path.
 *
 * @param uri The URI to turn into an Intent.
 * @param flags Additional processing flags.
 *
 * @return Intent The newly created Intent object.
 *
 * @throws URISyntaxException Throws URISyntaxError if the basic URI syntax
 * it bad (as parsed by the Uri class) or the Intent data within the
 * URI is invalid.
 *
 * @see #toUri
 */
public static Intent parseUri(String uri, @UriFlags int flags) throws URISyntaxException {
    int i = 0;
    try {
        final boolean androidApp = uri.startsWith("android-app:");
        // Validate intent scheme if requested.
        if ((flags&(URI_INTENT_SCHEME|URI_ANDROID_APP_SCHEME)) != 0) {
            if (!uri.startsWith("intent:") && !androidApp) {
                Intent intent = new Intent(ACTION_VIEW);
                try {
                    intent.setData(Uri.parse(uri));
                } catch (IllegalArgumentException e) {
                    throw new URISyntaxException(uri, e.getMessage());
                }
                return intent;
            }
        }
        i = uri.lastIndexOf("#");
        // simple case
        if (i == -1) {
            if (!androidApp) {
                return new Intent(ACTION_VIEW, Uri.parse(uri));
            }
        // old format Intent URI
        } else if (!uri.startsWith("#Intent;", i)) {
            if (!androidApp) {
                return getIntentOld(uri, flags);
            } else {
                i = -1;
            }
        }
        // new format
        Intent intent = new Intent(ACTION_VIEW);
        Intent baseIntent = intent;
        boolean explicitAction = false;
        boolean inSelector = false;
        // fetch data part, if present
        String scheme = null;
        String data;
        if (i >= 0) {
            data = uri.substring(0, i);
            i += 8; // length of "#Intent;"
        } else {
            data = uri;
        }
        // loop over contents of Intent, all name=value;
        while (i >= 0 && !uri.startsWith("end", i)) {
            int eq = uri.indexOf('=', i);
            if (eq < 0) eq = i-1;
            int semi = uri.indexOf(';', i);
            String value = eq < semi ? Uri.decode(uri.substring(eq + 1, semi)) : "";
            // action
            if (uri.startsWith("action=", i)) {
                intent.setAction(value);
                if (!inSelector) {
                    explicitAction = true;
                }
            }
            // categories
            else if (uri.startsWith("category=", i)) {
                intent.addCategory(value);
            }
            // type
            else if (uri.startsWith("type=", i)) {
                intent.mType = value;
            }
            // launch flags
            else if (uri.startsWith("launchFlags=", i)) {
                intent.mFlags = Integer.decode(value).intValue();
                if ((flags& URI_ALLOW_UNSAFE) == 0) {
                    intent.mFlags &= ~IMMUTABLE_FLAGS;
                }
            }
            // package
            else if (uri.startsWith("package=", i)) {
                intent.mPackage = value;
            }
            // component
            else if (uri.startsWith("component=", i)) {
                intent.mComponent = ComponentName.unflattenFromString(value);
            }
            // scheme
            else if (uri.startsWith("scheme=", i)) {
                if (inSelector) {
                    intent.mData = Uri.parse(value + ":");
                } else {
                    scheme = value;
                }
            }
            // source bounds
            else if (uri.startsWith("sourceBounds=", i)) {
                intent.mSourceBounds = Rect.unflattenFromString(value);
            }
            // selector
            else if (semi == (i+3) && uri.startsWith("SEL", i)) {
                intent = new Intent();
                inSelector = true;
            }
            // extra
            else {
                String key = Uri.decode(uri.substring(i + 2, eq));
                // create Bundle if it doesn't already exist
                if (intent.mExtras == null) intent.mExtras = new Bundle();
                Bundle b = intent.mExtras;
                // add EXTRA
                if      (uri.startsWith("S.", i)) b.putString(key, value);
                else if (uri.startsWith("B.", i)) b.putBoolean(key, Boolean.parseBoolean(value));
                else if (uri.startsWith("b.", i)) b.putByte(key, Byte.parseByte(value));
                else if (uri.startsWith("c.", i)) b.putChar(key, value.charAt(0));
                else if (uri.startsWith("d.", i)) b.putDouble(key, Double.parseDouble(value));
                else if (uri.startsWith("f.", i)) b.putFloat(key, Float.parseFloat(value));
                else if (uri.startsWith("i.", i)) b.putInt(key, Integer.parseInt(value));
                else if (uri.startsWith("l.", i)) b.putLong(key, Long.parseLong(value));
                else if (uri.startsWith("s.", i)) b.putShort(key, Short.parseShort(value));
                else throw new URISyntaxException(uri, "unknown EXTRA type", i);
            }
            // move to the next item
            i = semi + 1;
        }
        if (inSelector) {
            // The Intent had a selector; fix it up.
            if (baseIntent.mPackage == null) {
                baseIntent.setSelector(intent);
            }
            intent = baseIntent;
        }
        if (data != null) {
            if (data.startsWith("intent:")) {
                data = data.substring(7);
                if (scheme != null) {
                    data = scheme + ':' + data;
                }
            } else if (data.startsWith("android-app:")) {
                if (data.charAt(12) == '/' && data.charAt(13) == '/') {
                    // Correctly formed android-app, first part is package name.
                    int end = data.indexOf('/', 14);
                    if (end < 0) {
                        // All we have is a package name.
                        intent.mPackage = data.substring(14);
                        if (!explicitAction) {
                            intent.setAction(ACTION_MAIN);
                        }
                        data = "";
                    } else {
                        // Target the Intent at the given package name always.
                        String authority = null;
                        intent.mPackage = data.substring(14, end);
                        int newEnd;
                        if ((end+1) < data.length()) {
                            if ((newEnd=data.indexOf('/', end+1)) >= 0) {
                                // Found a scheme, remember it.
                                scheme = data.substring(end+1, newEnd);
                                end = newEnd;
                                if (end < data.length() && (newEnd=data.indexOf('/', end+1)) >= 0) {
                                    // Found a authority, remember it.
                                    authority = data.substring(end+1, newEnd);
                                    end = newEnd;
                                }
                            } else {
                                // All we have is a scheme.
                                scheme = data.substring(end+1);
                            }
                        }
                        if (scheme == null) {
                            // If there was no scheme, then this just targets the package.
                            if (!explicitAction) {
                                intent.setAction(ACTION_MAIN);
                            }
                            data = "";
                        } else if (authority == null) {
                            data = scheme + ":";
                        } else {
                            data = scheme + "://" + authority + data.substring(end);
                        }
                    }
                } else {
                    data = "";
                }
            }
            if (data.length() > 0) {
                try {
                    intent.mData = Uri.parse(data);
                } catch (IllegalArgumentException e) {
                    throw new URISyntaxException(uri, e.getMessage());
                }
            }
        }
        return intent;
    } catch (IndexOutOfBoundsException e) {
        throw new URISyntaxException(uri, "illegal Intent URI format", i);
    }
}

详细请看上面的注解

类似的可构造Uri如下:

intent:#Intent;component=com.android.calendar/com.android.calendar.LaunchActivity;end

参考

  1. Intent.java源代码
  2. 小米手机MIUI远程代码执行漏洞分析
<think>我们正在讨论的是支付宝的URI协议:alipays://platformapi/startapp。根据引用内容,这个URI协议主要用于在移动设备上启动支付宝应用并执行特定操作。以下是对其作用的详细解释: 1. **启动支付宝应用**:`alipays://`是支付宝自定义的URI协议(Scheme),类似于`http://`用于网页。当在Android或iOS设备上触发这个URI时,如果设备安装了支付宝,系统会尝试打开支付宝应用。 2. **执行特定操作**:`platformapi/startapp`是路径部分,它指示支付宝执行一个特定的操作,即启动一个应用(这里指支付宝内部的某个功能或第三方应用通过支付宝平台启动)。通常,这个URI会附带一些参数,用于指定要启动的具体功能(如支付、授权等)。例如,完整的URI可能类似于: ``` alipays://platformapi/startapp?appId=20000067&url=encodedURL ``` 其中`appId`指定要启动的应用ID(如20000067代表快速支付),`url`可能包含一些传递给支付宝的数据。 3. **在WebView中调起支付宝**:引用[3]和[4]提到,在WebView中加载的网页如果包含`alipays://`链接,默认情况下WebView无法处理(因为这不是WebView支持的协议),所以需要通过重写`shouldOverrideUrlLoading`方法,使用`Intent.ACTION_VIEW`来启动外部应用(即支付宝)[^3][^4]。 4. **检查支付宝是否安装**:引用[2]展示了如何检查设备是否安装了支付宝。通过尝试解析`alipays://platformapi/startApp`的Intent,如果返回的`ComponentName`不为空,则表示已安装[^2]。 5. **处理第三方调用**:引用[1]和[3]中的代码示例展示了如何捕获包含`alipays://platformapi`的URL,并通过启动Activity来调起支付宝[^1][^3]。 **总结**:`alipays://platformapi/startapp`是支付宝提供的一种深层链接(Deep Link)机制,允许第三方应用或网页通过URI直接调起支付宝并执行特定功能(如支付、授权、跳转到指定页面等)。开发者需要在应用中处理这个URI,确保在用户点击相关链接时能够正确启动支付宝。 §§相关问题§§ 1. 如何构建一个完整的支付宝调起URI(包括参数)? 2. 在iOS系统中如何处理alipays协议? 3. 支付宝Deep Link调起失败有哪些常见原因? 4. 除了alipays,支付宝还有哪些自定义URI协议?</think>### `alipays://platformapi/startapp` URI 协议的作用详解 `alipays://platformapi/startapp` 是支付宝提供的**深层链接协议**(Deep Link),用于在移动应用中直接调起支付宝客户端并执行特定操作。其核心作用如下: --- #### 1. **调起支付宝客户端** - 当用户点击或访问此 URI 时,系统会检测设备是否安装支付宝: ```java // 检测支付宝是否安装(Android 示例) Uri uri = Uri.parse("alipays://platformapi/startApp"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); if (intent.resolveActivity(getPackageManager()) != null) { // 已安装支付宝,调起客户端 startActivity(intent); } else { Toast.makeText(this, "未安装支付宝", Toast.LENGTH_SHORT).show(); } ``` [^2] --- #### 2. **执行特定功能** 通过附加参数实现不同功能: - **支付场景** 传递订单信息,直接跳转到支付宝支付页面: ``` alipays://platformapi/startapp?appId=20000067&orderSuffix=h5_route_token%3Dxxx ``` - **授权登录** 调起支付宝授权界面获取用户信息: ``` alipays://platformapi/startapp?appId=20000067&url=auth_url ``` - **小程序/服务跳转** 打开支付宝内的小程序或特定服务页面: ``` alipays://platformapi/startapp?appId=小程序ID ``` --- #### 3. **在 WebView 中兼容处理** 由于 WebView 默认不支持 `alipays://` 协议,需重写逻辑: ```java webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("alipays://")) { try { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); return true; // 拦截链接,由系统处理 } catch (Exception e) { return false; // 防止未安装支付宝时崩溃 } } view.loadUrl(url); // 其他链接由 WebView 处理 return true; } }); ``` [^3] --- #### 4. **处理 Intent 协议变体** 部分场景可能使用 `intent://` 协议(需特殊解析): ```java if (url.startsWith("intent://platformapi/startapp")) { try { Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); intent.addCategory("android.intent.category.BROWSABLE"); startActivity(intent); } catch (Exception e) { e.printStackTrace(); } return true; } ``` [^4] --- ### 关键注意事项 1. **安全性** 需验证参数来源,防止恶意链接伪造支付请求。 2. **兼容性** - iOS 需配置 `LSApplicationQueriesSchemes` 声明 `alipays` 协议 - Android 需捕获 `ActivityNotFoundException` 处理未安装情况 3. **参数规范** 参数需按支付宝官方文档进行 URL 编码,例如 `orderSuffix` 需包含加密的交易令牌。 通过此协议,开发者可实现**从 App 或网页无缝跳转至支付宝**,完成支付、授权等关键操作,提升用户体验[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值