"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
不是以 http
或 https
开头,则会导致此错误。
所以,为了解决这个问题,我不得不更改本机 java 代码。现在我完全没有使用 java
和 android
的经验,因此修复本机代码非常困难。我知道我必须修改 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;
},
希望这对你有用。它对我有用...
我已经使用 flutter 的 web_view_plugin
(webview) 构建了一个混合应用程序。
我们的一种支付方式需要打开第三方应用程序(在本例中为 kakaotalk)。但是flutter webview插件没有提供这个功能,返回net::ERR_UNKNOWN_URL_SCHEME
。我做了一些研究,我明白问题出在 url
。如果 url
不是以 http
或 https
开头,则会导致此错误。
所以,为了解决这个问题,我不得不更改本机 java 代码。现在我完全没有使用 java
和 android
的经验,因此修复本机代码非常困难。我知道我必须修改 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;
},
希望这对你有用。它对我有用...