需求
如題
React Native 組件Webview 是不支持上傳和下載籍凝,需求使然周瞎,尋找解決方案,時間過去有些長饵蒂,很多借助搜索引擎搜索的資料已經(jīng)丟失声诸,先整理如下。
方案
官方文檔并沒有找到實現(xiàn)上傳下載功能退盯,需修改源碼彼乌,但并不想修改 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);
// 開始下載了峡钓,就發(fā)送一個 事件通知到 React Native 做相關處理,記得加上監(jiān)聽
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;
}
}
}
希望能幫助到一些人斥废。