Android WebView 跳转第三方App

Android的WebView自身是支持网页挑起第三方App的,但是如果为WebView设置了自定义的WebViewClient,这个功能就失效了,解决方案如下。

WebViewClient中有一个方法:shouldOverrideUrlLoading,这个方法的作用是让业务层可以根据url的值决定用什么方式来处理这个url。

 /**
     * Give the host application a chance to take over the control when a new
     * url is about to be loaded in the current WebView. If WebViewClient is not
     * provided, by default WebView will ask Activity Manager to choose the
     * proper handler for the url. If WebViewClient is provided, return true
     * means the host application handles the url, while return false means the
     * current WebView handles the url.
     * This method is not called for requests using the POST "method".
     *
     * @param view The WebView that is initiating the callback.
     * @param url The url to be loaded.
     * @return True if the host application wants to leave the current WebView
     *         and handle the url itself, otherwise return false.
     */
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return false;
    }

如果没有为WebView设置WebViewClient,那么WebView就会让Activity Manager来决定如何处理这个url,如果设置了WebViewClient,就需要通过该方法的返回值通知WebView是否需要让它处理,如果需要自行处理,就可以在该方法中处理该url。

具体代码如下


        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (shouldOverrideUrlLoadingByApp(view, url)) {
                return true;
            }
            return super.shouldOverrideUrlLoading(view, url);
        }
    
         /**
         * 根据url的scheme处理跳转第三方app的业务
         */
        private boolean shouldOverrideUrlLoadingByApp(WebView view, String url) {
            if (url.startsWith("http") || url.startsWith("https") || url.startsWith("ftp")) {
            //不处理http, https, ftp的请求
                return false;
            }
            Intent intent;
            try {
                intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
            } catch (URISyntaxException e) {
                XLLog.e(TAG, "URISyntaxException: " + e.getLocalizedMessage());
                return false;
            }
            intent.setComponent(null);
            try {
                getContext().startActivity(intent);
            } catch (ActivityNotFoundException e) {
                XLLog.e(TAG, "ActivityNotFoundException: " + e.getLocalizedMessage());
                return false;
            }
            return true;
        }   

遇到的问题

1. 未安装目标app,DeepLink链接处理失败,浏览器也不能处理,导致出现无法访问的界面;

如上面的解决方案,当设备上没有安装目标app时,startActivity()方法会抛出ActivityNotFoundExceptionshouldOverrideUrlLoading()方法就会返回false,该url就会交给浏览器处理,浏览器肯定无法处理这种自定义scheme的协议,就会抛出NOT_SUPPORT_SCHEME错误,导致网页出现无法加载的错误页面。解决方法是判断浏览器能处理哪些scheme,如果检测到该url是浏览器无法处理的,就不让浏览器处理了,那么自然就不会出现这个问题。

2. 未安装目标app,点击DeepLink链接,根据链接内容跳转至应用市场或者通过网页触发目标app的下载链接

例如:
intent://dangdang://product//pid=23248697#Intent;scheme=dangdang://product//pid=23248697;package=com.dangdang.buy2;end,该url通过Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);解析后,intent中会包含应用报名,那么就可以先通过mActivity.getPackageManager().resolveActivity(intent, 0) == null判断设备中是否有app能处理这个url,如果没有,就根据包名跳转应用市场进行下载。当然站在产品的角度,是不希望无偿帮应用市场导量的,所以也可以不跳转应用市场。一般来说,如果网页中的deeplink有这种需求,在检测到设备中无法处理该url时,还会触发目前app的下载链接,这种方案在网页中的实现目前没有去了解,只是知道这样会回调WebView的DownloadListener,客户端可以在回调中处理目标app的下载操作。

以上两个问题的解决方案

public class ADIntentUtils {

    private static final String TAG = "IntentUtils";
    /**
     * 系统可以处理的url正则
     */
    private static final Pattern ACCEPTED_URI_SCHEME = Pattern.compile("(?i)"
            + // switch on case insensitive matching
            '('
            + // begin group for scheme
            "(?:http|https|ftp|file)://" + "|(?:inline|data|about|javascript):" + "|(?:.*:.*@)"
            + ')' + "(.*)");

    private Activity mActivity = null;

    public ADIntentUtils(Activity activity) {
        this.mActivity = activity;
    }

    public boolean shouldOverrideUrlLoadingByApp(WebView view, String url) {
        return shouldOverrideUrlLoadingByAppInternal(view, url, true);
    }

    private boolean shouldOverrideUrlLoadingByAppInternal(WebView view, String url, boolean interceptExternalProtocol) {
        if (isAcceptedScheme(url)) return false;
        Intent intent;
        try {
            intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
        } catch (URISyntaxException e) {
            Log.e(TAG, "URISyntaxException: " + e.getLocalizedMessage());
            return interceptExternalProtocol;
        }

        intent.setComponent(null);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
            intent.setSelector(null);
        }

        //intent://dangdang://product//pid=23248697#Intent;scheme=dangdang://product//pid=23248697;package=com.dangdang.buy2;end
        //该Intent无法被设备上的应用程序处理
        if (mActivity.getPackageManager().resolveActivity(intent, 0) == null) {
            return tryHandleByMarket(intent) || interceptExternalProtocol;
        }

        try {
            mActivity.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Log.e(TAG, "ActivityNotFoundException: " + e.getLocalizedMessage());
            return interceptExternalProtocol;
        }
        return true;
    }

    private boolean tryHandleByMarket(Intent intent) {
        String packagename = intent.getPackage();
        if (packagename != null) {
            intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id="
                    + packagename));
            try{
                mActivity.startActivity(intent);
            } catch (ActivityNotFoundException e) {
                Log.e(TAG, "tryHandleByMarket ActivityNotFoundException: " + e.getLocalizedMessage());
                return false;
            }
            return true;
        } else {
            return false;
        }
    }


    /**
     * 该url是否属于浏览器能处理的内部协议
     */
    private boolean isAcceptedScheme(String url) {
        //正则中忽略了大小写,保险起见,还是转成小写
        String lowerCaseUrl = url.toLowerCase();
        Matcher acceptedUrlSchemeMatcher = ACCEPTED_URI_SCHEME.matcher(lowerCaseUrl);
        if (acceptedUrlSchemeMatcher.matches()) {
            return true;
        }
        return false;
    }

}
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值