"net::ERR_UNKNOWN_URL_SCHEME" 当 url 为 "intent://kakaopay..." 时出错

"net::ERR_UNKNOWN_URL_SCHEME" error when url is"intent://kakaopay..."

我已经使用 flutter 的 web_view_plugin(webview) 构建了一个混合应用程序。 我们的一种支付方式需要打开第三方应用程序(在本例中为 kakaotalk)。但是flutter webview插件没有提供这个功能,返回net::ERR_UNKNOWN_URL_SCHEME。我做了一些研究,我明白问题出在 url。如果 url 不是以 httphttps 开头,则会导致此错误。

所以,为了解决这个问题,我不得不更改本机 java 代码。现在我完全没有使用 javaandroid 的经验,因此修复本机代码非常困难。我知道我必须修改 shouldOverrideUrlLoading 部分,以允许以 intent:// 开头的 url 并且我还必须进行一些验证以检查该应用程序是否已安装.(如果没有安装,用户应该被重定向到 playstore)

我添加的代码在shouldOverrideUrlLoading中。 我也做了三个进口。剩下的就是代码,由flutter

生成
package com.flutter_webview_plugin;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.os.Build;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.content.Intent; //added import
import android.net.Uri; //added import
import android.content.ActivityNotFoundException; //added import
/**
 * Created by lejard_h on 20/12/2017.
 */

public class BrowserClient extends WebViewClient {
    private Pattern invalidUrlPattern = null;

    public BrowserClient() {
        this(null);
    }

    public BrowserClient(String invalidUrlRegex) {
        super();
        if (invalidUrlRegex != null) {
            invalidUrlPattern = Pattern.compile(invalidUrlRegex);
        }
    }

    public void updateInvalidUrlRegex(String invalidUrlRegex) {
        if (invalidUrlRegex != null) {
            invalidUrlPattern = Pattern.compile(invalidUrlRegex);
        } else {
            invalidUrlPattern = null;
        }
    }

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
        Map<String, Object> data = new HashMap<>();
        data.put("url", url);
        data.put("type", "startLoad");
        FlutterWebviewPlugin.channel.invokeMethod("onState", data);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        Map<String, Object> data = new HashMap<>();
        data.put("url", url);

        FlutterWebviewPlugin.channel.invokeMethod("onUrlChanged", data);

        data.put("type", "finishLoad");
        FlutterWebviewPlugin.channel.invokeMethod("onState", data);

    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        // returning true causes the current WebView to abort loading the URL,
        // while returning false causes the WebView to continue loading the URL as usual.
        String url = request.getUrl().toString();
        boolean isInvalid = checkInvalidUrl(url);
        Map<String, Object> data = new HashMap<>();
        data.put("url", url);
        data.put("type", isInvalid ? "abortLoad" : "shouldStart");

        FlutterWebviewPlugin.channel.invokeMethod("onState", data);
        return isInvalid;
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        // returning true causes the current WebView to abort loading the URL,
        // while returning false causes the WebView to continue loading the URL as usual.
        if (url.startsWith(INTENT_PROTOCOL_START)) {
                final int customUrlStartIndex = INTENT_PROTOCOL_START.length();
                final int customUrlEndIndex = url.indexOf(INTENT_PROTOCOL_INTENT);
                if (customUrlEndIndex < 0) {
                    return false;
                } else {
                    final String customUrl = url.substring(customUrlStartIndex, customUrlEndIndex);
                    try {
                        view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(customUrl)));
                    } catch (ActivityNotFoundException e) {
                        final int packageStartIndex = customUrlEndIndex + INTENT_PROTOCOL_INTENT.length();
                        final int packageEndIndex = url.indexOf(INTENT_PROTOCOL_END);

                        final String packageName = url.substring(packageStartIndex, packageEndIndex < 0 ? url.length() : packageEndIndex);
                        view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(GOOGLE_PLAY_STORE_PREFIX + packageName)));
                    }
                    return true;
                }
            } else {
                return false;
            }
        // boolean isInvalid = checkInvalidUrl(url);
        // Map<String, Object> data = new HashMap<>();
        // data.put("url", url);
        // data.put("type", isInvalid ? "abortLoad" : "shouldStart");

        // FlutterWebviewPlugin.channel.invokeMethod("onState", data);
        // return isInvalid;
    }


    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
        super.onReceivedHttpError(view, request, errorResponse);
        Map<String, Object> data = new HashMap<>();
        data.put("url", request.getUrl().toString());
        data.put("code", Integer.toString(errorResponse.getStatusCode()));
        FlutterWebviewPlugin.channel.invokeMethod("onHttpError", data);
    }

    @Override
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
        super.onReceivedError(view, errorCode, description, failingUrl);
        Map<String, Object> data = new HashMap<>();
        data.put("url", failingUrl);
        data.put("code", errorCode);
        FlutterWebviewPlugin.channel.invokeMethod("onHttpError", data);
    }

    private boolean checkInvalidUrl(String url) {
        if (invalidUrlPattern == null) {
            return false;
        } else {
            Matcher matcher = invalidUrlPattern.matcher(url);
            return matcher.lookingAt();
        }
    }
}

代码编译成功,但当我尝试使用“第 3 方应用程序(kakaotalk)”

付款时,它仍然 returns 同样的错误 net::ERR_UNKNOWN_URL_SCHEME

我在 Android 之前遇到过类似的错误,当时 Firebase 动态链接被强制加载到 WebView 中。就我而言,FDL 预计将由 Android 中的 Google Play Services 处理。但是由于 WebView 不知道如何处理 link 它被强制显示,WebView returns “net::ERR_UNKNOWN_URL_SCHEME” 错误。我不确定这是否与您的情况相同,因为我无法验证您尝试加载的 link 除了“intent://kakaopay...”

您可以尝试使用 url_launcher 从外部打开 link。使用 RegEx 过滤意图 URLs 并检查 URL 是否可以在外部(应用程序外部)启动和处理。

var yourURL = "URL goes here";
// Check if URL contains "intent"
yourURL.contains(RegExp('^intent://.*$')){
  // Check if the URL can be launched
  if (await canLaunch(yourURL)) {
    await launch(yourURL);
  } else {
    print('Could not launch $yourURL');
  }
}

另外,您使用的插件(web_view_plugin)好像已经过时了,我在这里找不到https://pub.dev/packages?q=web_view_plugin. Flutter has its official WebView plugin (webview_flutter),已经发布了,建议查看一下如果它适合您的用例。

听着,在某些情况下使用@omatt 的答案可能行不通,尤其是 webview_flutter。我努力寻找解决方案,所以我这样做了:

_launchURL(url) async {
  var link = "https://hiddenwords.page.link/deposit";

  if (await canLaunch(link)) {
      await launch(link,
      forceWebView: false, enableJavaScript: true, forceSafariVC: 
    false);
  } else {
    throw 'Could not launch $link';
  }
}

我手动将 url/link 我希望它在 _launch 函数中打开...不要介意 _launch 括号中的 url。

我还将此添加到 Webview 小部件中:

navigationDelegate: (NavigationRequest request) {
   if (request.url.contains(RegExp('^intent://.*$')))  {
        _launchURL(request.url);
        return NavigationDecision.prevent;
   }
     return NavigationDecision.navigate;
},

希望这对你有用。它对我有用...