需求
如题
React Native 组件Webview 是不支持上传和下载,需求使然,寻找解决方案,时间过去有些长,很多借助搜索引擎搜索的资料已经丢失,先整理如下。
方案
官方文档并没有找到实现上传下载功能,需修改源码,但并不想修改 RN core 源码,麻烦,维护也麻烦。
我们可以使用 npm 组件 react-native-webview-bridge:
https://github.com/alinz/react-native-webview-bridge/
最开始React Native webview 不支持web页面和RN交互,所有诞生了这个组件,虽说 “the bridge is fully functional”,
但实际上仍然不支持我们需要的 “上传 ”和 “下载”功能,不过可以在这个组件的 Android 源码做处理。
开始搞吧
- npm install react-native-webview-bridge
- 在android工程中找到这个包 reactnativewebviewbridge,拷贝到自己的项目底下
- 修改自己项目的 build.gradle 文件,注释 这一行
//compile project(':react-native-webview-bridge')
- 修改 MainApplication.java 文件
//import com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage;
import com.your_package_name.reactnativewebviewbridge.WebViewBridgePackage;
- 修改文件 WebViewBridgeManager.java 记得替换下文中的 your_package_name ,之后应该可以直接用了,希望能帮助到一些人。
package com.your_package_name.reactnativewebviewbridge;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.webkit.DownloadListener;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.views.webview.ReactWebViewManager;
import com.your_package_name.MainActivity;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
public class WebViewBridgeManager extends ReactWebViewManager {
private static final String REACT_CLASS = "RCTWebViewBridge";
private Activity mActivity = null;
public static final int COMMAND_SEND_TO_BRIDGE = 101;
@Override
public String getName() { return REACT_CLASS; }
@Override
public
@Nullable
Map<String, Integer> getCommandsMap() {
Map<String, Integer> commandsMap = super.getCommandsMap();
commandsMap.put("sendToBridge", COMMAND_SEND_TO_BRIDGE);
return commandsMap;
}
@Override
protected WebView createViewInstance(ThemedReactContext reactContext) {
WebView root = super.createViewInstance(reactContext);
final Activity mActivity = reactContext.getCurrentActivity();
this.setmActivity(mActivity);
root.addJavascriptInterface(new JavascriptBridge(root), "WebViewBridge");
root.setDownloadListener(new WebviewDownload(reactContext));
root.setWebChromeClient(new WebChromeClient() {
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
((MainActivity) mActivity).setUploadMessage(uploadMsg);
openFileChooserView();
}
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return true;
}
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return true;
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
((MainActivity) mActivity).setUploadMessage(uploadMsg);
openFileChooserView();
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
((MainActivity) mActivity).setUploadMessage(uploadMsg);
openFileChooserView();
}
// For Android > 5.0
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
((MainActivity) mActivity).setmUploadCallbackAboveL(filePathCallback);
openFileChooserView();
return true;
}
private void openFileChooserView() {
try {
final Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
final Intent chooserIntent = Intent.createChooser(galleryIntent, "choose file");
mActivity.startActivityForResult(chooserIntent, 1);
} catch (Exception e) {
Log.d("error", e.toString());
}
}
});
return root;
}
@Override
public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray args) {
super.receiveCommand(root, commandId, args);
switch (commandId) {
case COMMAND_SEND_TO_BRIDGE:
sendToBridge(root, args.getString(0));
break;
default:
//do nothing!!!!
}
}
private void sendToBridge(WebView root, String message) {
String script = "WebViewBridge.onMessage('" + message + "');";
WebViewBridgeManager.evaluateJavascript(root, script);
}
static private void evaluateJavascript(WebView root, String javascript) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
root.evaluateJavascript(javascript, null);
} else {
root.loadUrl("javascript:" + javascript);
}
}
@ReactProp(name = "allowFileAccessFromFileURLs")
public void setAllowFileAccessFromFileURLs(WebView root, boolean allows) {
root.getSettings().setAllowFileAccessFromFileURLs(allows);
}
@ReactProp(name = "allowUniversalAccessFromFileURLs")
public void setAllowUniversalAccessFromFileURLs(WebView root, boolean allows) {
root.getSettings().setAllowUniversalAccessFromFileURLs(allows);
}
@ReactProp(name = "uploadEnabledAndroid")
public void uploadEnabledAndroid(WebView view, boolean enabled) {
if (enabled) {
view.setWebChromeClient(new WebChromeClient() {
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
((MainActivity) mActivity).setUploadMessage(uploadMsg);
openFileChooserView();
}
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return true;
}
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return true;
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
((MainActivity) mActivity).setUploadMessage(uploadMsg);
openFileChooserView();
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
((MainActivity) mActivity).setUploadMessage(uploadMsg);
openFileChooserView();
}
// For Android > 5.0
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
((MainActivity) mActivity).setmUploadCallbackAboveL(filePathCallback);
openFileChooserView();
return true;
}
private void openFileChooserView() {
try {
final Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
final Intent chooserIntent = Intent.createChooser(galleryIntent, "choose file");
mActivity.startActivityForResult(chooserIntent, 1);
} catch (Exception e) {
Log.d("error", e.toString());
}
}
});
}
}
public void setmActivity(Activity mActivity) { this.mActivity = mActivity; }
}
/**
* 下载
*/
class WebviewDownload implements DownloadListener {
ReactContext reactContext;
public WebviewDownload(ReactContext reactContext) {
this.reactContext = reactContext;
}
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype,
long contentLength) {
try {
if (!isDownloadManagerAvailable(reactContext)) {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
reactContext.startActivity(intent);
} else {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
Uri uri = Uri.parse(url);
String[] path = uri.getPath().split("/");
String fileName = "";
if(path.length>1){
fileName = path[path.length - 1];
}
request.setTitle(fileName);
request.setDescription("下载完成后,点击开始安装。");
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
// get download service and enqueue file
DownloadManager manager = (DownloadManager) reactContext.getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
// 开始下载了,就发送一个 事件通知到 React Native 做相关处理,记得加上监听
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("NativeCustomEvent", "startDownload");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断是否支持 Download Manager
* @param context used to check the device version and DownloadManager information
* @return true if the download manager is available
*/
public static boolean isDownloadManagerAvailable(Context context) {
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
return false;
}
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setClassName("com.android.providers.downloads.ui", "com.android.providers.downloads.ui.DownloadList");
List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
} catch (Exception e) {
return false;
}
}
}
希望能帮助到一些人。