【问题标题】:How can I setAllowFileAccess for a react-native android WebView?如何为 react-native android WebView 设置AllowFileAccess?
【发布时间】:2018-01-27 17:18:47
【问题描述】:

我想在我使用 react-native 的内置 WebView 组件创建的 Android WebView 中禁用文件访问。

Android WebView docs 说“默认情况下启用文件访问。”,这对我的组织来说是一个安全问题。

react-native 0.31 docs 提到了一个getWebViewHandle 方法,可以用来访问底层的WebView 节点;如果这行得通,那么我可以(大概)写:

import { WebView, Platform } from 'react-native';
//...
var reactWebview = <Webview [props here] />
if (Platform.OS === 'android') {
    var webview = reactWebview.getWebViewHandle();
    webview.setAllowFileAccess(false);
}

但是,react-native docs 的更高版本没有提及getWebViewHandle,当我在 Android 设备上以 react-native 0.44 运行这样的代码时,我收到错误 webview.getWebViewHandle is not a function

我的问题是:

  1. react-native 创建的 Android WebView 是否默认启用文件访问?

  2. 如果是这样,我们如何禁用此文件访问?我们可以通过扩展 WebView 类来实现这一点,还是需要 fork 和修改 react-native?

感谢您的宝贵时间!

【问题讨论】:

    标签: android react-native webview mobile-application file-access


    【解决方案1】:

    问题一:从ReactWebViewManager.java的源码看,RN并没有调用WebView.setAllowFileAccess,所以文件访问是通过Android WebView开启的,不是RN。

    问题2:您可以创建一个自定义WebView来做您需要的事情,或者只是从Native Module获取您的WebView的引用,然后您可以访问Android WebView的所有api,例如setAllowFileAccess在那个@987654323 @。

    原生模块

    public class WebViewSettingModule extends ReactContextBaseJavaModule {
    
        public WebViewSettingModule(ReactApplicationContext reactContext) {
            super(reactContext);
        }
    
        @Override
        public String getName() {
            return "WebViewSetting";
        }
    
        @ReactMethod
        public void setWebView() {
    
            Activity activity = getCurrentActivity();
            //the id for the ReactRootView is always be 1
            @IdRes int id = 1;
            View view = activity.findViewById(id);
            if (view instanceof ReactRootView) {
                ReactRootView reactRootView = (ReactRootView) view;
                //make sure the WebView is directly child of ReactRootView
                reactRootView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
                    @Override
                    public void onChildViewAdded(View parent, final View child) {
                        if (child instanceof WebView) {
                            Log.e("onChildViewAdded: ", ((WebView) child).getUrl());
                            //get the reference to the WebView and setAllowFileAccess
                            ((WebView) child).getSettings().setAllowFileAccess(false);
                        }
                    }
    
                    @Override
                    public void onChildViewRemoved(View parent, View child) {
    
                    }
                });
            }
        }
    }
    

    index.android.js

    import React, {Component} from "react";
    import {AppRegistry, View, WebView} from "react-native";
    //the native module
    import MyWebView from "./src/MyWebView";
    
    export default class WebViewSetting extends Component {
    
        componentDidMount() {
            //notify native code to modify WebView setting
            MyWebView.setWebView();
        }
    
        render() {
            return (
                <View style={{flex: 1}}>
                    <WebView
                        source={{uri: 'https://github.com/'}}
                        style={{marginTop: 20}}/>
                </View>
            );
        }
    }
    
    
    AppRegistry.registerComponent('WebViewSetting', () => WebViewSetting);
    

    整个代码可以在here找到

    【讨论】:

      【解决方案2】:

      node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/ReactWebViewManager.java

      /**
       * Copyright (c) 2015-present, Facebook, Inc.
       * All rights reserved.
       * This source code is licensed under the BSD-style license found in the
       * LICENSE file in the root directory of this source tree. An additional grant
       * of patent rights can be found in the PATENTS file in the same directory.
       */
      
      package com.facebook.react.views.webview;
      import javax.annotation.Nullable;
      import java.io.File;
      import java.io.IOException;
      import java.io.UnsupportedEncodingException;
      import java.text.SimpleDateFormat;
      import java.util.Date;
      import java.util.HashMap;
      import java.util.Locale;
      import java.util.Map;
      import android.annotation.SuppressLint;
      import android.app.Activity;
      import android.content.ActivityNotFoundException;
      import android.content.ComponentName;
      import android.content.Intent;
      import android.content.pm.PackageManager;
      import android.graphics.Bitmap;
      import android.graphics.Picture;
      import android.net.Uri;
      import android.os.Build;
      import android.os.Environment;
      import android.provider.MediaStore;
      import android.text.TextUtils;
      import android.util.Log;
      import android.view.ViewGroup.LayoutParams;
      import android.webkit.ConsoleMessage;
      import android.webkit.GeolocationPermissions;
      import android.webkit.WebChromeClient;
      import android.webkit.WebView;
      import android.webkit.WebViewClient;
      import android.webkit.JavascriptInterface;
      import android.webkit.ValueCallback;
      import android.webkit.WebSettings;
      import com.facebook.common.logging.FLog;
      import com.facebook.react.bridge.ActivityEventListener;
      import com.facebook.react.common.ReactConstants;
      import com.facebook.react.bridge.Arguments;
      import com.facebook.react.bridge.LifecycleEventListener;
      import com.facebook.react.bridge.ReactContext;
      import com.facebook.react.bridge.ReadableArray;
      import com.facebook.react.bridge.ReadableMap;
      import com.facebook.react.bridge.ReadableMapKeySetIterator;
      import com.facebook.react.bridge.WritableMap;
      import com.facebook.react.common.MapBuilder;
      import com.facebook.react.common.build.ReactBuildConfig;
      import com.facebook.react.module.annotations.ReactModule;
      import com.facebook.react.uimanager.SimpleViewManager;
      import com.facebook.react.uimanager.ThemedReactContext;
      import com.facebook.react.uimanager.UIManagerModule;
      import com.facebook.react.uimanager.annotations.ReactProp;
      import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
      import com.facebook.react.uimanager.events.Event;
      import com.facebook.react.uimanager.events.EventDispatcher;
      import com.facebook.react.views.webview.events.TopLoadingErrorEvent;
      import com.facebook.react.views.webview.events.TopLoadingFinishEvent;
      import com.facebook.react.views.webview.events.TopLoadingStartEvent;
      import com.facebook.react.views.webview.events.TopMessageEvent;
      import org.json.JSONObject;
      import org.json.JSONException;
      @ReactModule(name = ReactWebViewManager.REACT_CLASS)
      public class ReactWebViewManager extends SimpleViewManager<WebView> {
      
          protected static final String REACT_CLASS = "RCTWebView";
      
          private static final String HTML_ENCODING = "UTF-8";
          private static final String HTML_MIME_TYPE = "text/html; charset=utf-8";
          private static final String BRIDGE_NAME = "__REACT_WEB_VIEW_BRIDGE";
      
          private static final String HTTP_METHOD_POST = "POST";
      
          public static final int COMMAND_GO_BACK = 1;
          public static final int COMMAND_GO_FORWARD = 2;
          public static final int COMMAND_RELOAD = 3;
          public static final int COMMAND_STOP_LOADING = 4;
          public static final int COMMAND_POST_MESSAGE = 5;
          public static final int COMMAND_INJECT_JAVASCRIPT = 6;
      
          private static final String BLANK_URL = "about:blank";
          public static final int INPUT_FILE_REQUEST_GALLERY_IMAGE = 1001;
          public static final int REQUEST_SELECT_FILE_LEGACY = 1012;
      
          private WebViewConfig mWebViewConfig;
          private
          @Nullable
          WebView.PictureListener mPictureListener;
          private ValueCallback<Uri[]> mFilePathCallbackArr;
          private ValueCallback<Uri> mFilePathCallback; // Legacy (Android 4.1+)
          private String mCameraPhotoPath;
      
          protected static class ReactWebViewClient extends WebViewClient {
      
              private boolean mLastLoadFailed = false;
      
              @Override
              public void onPageFinished(WebView webView, String url) {
                  super.onPageFinished(webView, url);
      
                  if (!mLastLoadFailed) {
                      ReactWebView reactWebView = (ReactWebView) webView;
                      reactWebView.callInjectedJavaScript();
                      reactWebView.linkBridge();
                      emitFinishEvent(webView, url);
                  }
              }
              @Override
              public void onPageStarted(WebView webView, String url, Bitmap favicon) {
                  super.onPageStarted(webView, url, favicon);
                  mLastLoadFailed = false;
      
                  dispatchEvent(
                          webView,
                          new TopLoadingStartEvent(
                                  webView.getId(),
                                  createWebViewEvent(webView, url)));
              }
              @Override
              public boolean shouldOverrideUrlLoading(WebView view, String url) {
                  if (url.startsWith("http://") || url.startsWith("https://") ||
                          url.startsWith("file://")) {
                      return false;
                  } else {
                      try {
                          Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                          intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                          view.getContext().startActivity(intent);
                      } catch (ActivityNotFoundException e) {
                          FLog.w(ReactConstants.TAG, "activity not found to handle uri scheme for: " + url, e);
                      }
                      return true;
                  }
              }
      
              @Override
              public void onReceivedError(
                      WebView webView,
                      int errorCode,
                      String description,
                      String failingUrl) {
                  super.onReceivedError(webView, errorCode, description, failingUrl);
                  mLastLoadFailed = true;
      
                  emitFinishEvent(webView, failingUrl);
      
                  WritableMap eventData = createWebViewEvent(webView, failingUrl);
                  eventData.putDouble("code", errorCode);
                  eventData.putString("description", description);
      
                  dispatchEvent(
                          webView,
                          new TopLoadingErrorEvent(webView.getId(), eventData));
              }
      
              @Override
              public void doUpdateVisitedHistory(WebView webView, String url, boolean isReload) {
                  super.doUpdateVisitedHistory(webView, url, isReload);
      
                  dispatchEvent(
                          webView,
                          new TopLoadingStartEvent(
                                  webView.getId(),
                                  createWebViewEvent(webView, url)));
              }
      
              private void emitFinishEvent(WebView webView, String url) {
                  dispatchEvent(
                          webView,
                          new TopLoadingFinishEvent(
                                  webView.getId(),
                                  createWebViewEvent(webView, url)));
              }
      
              private WritableMap createWebViewEvent(WebView webView, String url) {
                  WritableMap event = Arguments.createMap();
                  event.putDouble("target", webView.getId());
                  event.putString("url", url);
                  event.putBoolean("loading", !mLastLoadFailed && webView.getProgress() != 100);
                  event.putString("title", webView.getTitle());
                  event.putBoolean("canGoBack", webView.canGoBack());
                  event.putBoolean("canGoForward", webView.canGoForward());
                  return event;
              }
          }
      
          protected static class ReactWebView extends WebView implements LifecycleEventListener {
              private
              @Nullable
              String injectedJS;
              private boolean messagingEnabled = false;
      
              private class ReactWebViewBridge {
                  ReactWebView mContext;
      
                  ReactWebViewBridge(ReactWebView c) {
                      mContext = c;
                  }
      
                  @JavascriptInterface
                  public void postMessage(String message) {
                      mContext.onMessage(message);
                  }
              }
              public ReactWebView(ThemedReactContext reactContext) {
                  super(reactContext);
              }
      
              @Override
              public void onHostResume() {
                  // do nothing
              }
      
              @Override
              public void onHostPause() {
                  // do nothing
              }
      
              @Override
              public void onHostDestroy() {
                  cleanupCallbacksAndDestroy();
              }
      
              public void setInjectedJavaScript(@Nullable String js) {
                  injectedJS = js;
              }
      
              public void setMessagingEnabled(boolean enabled) {
                  if (messagingEnabled == enabled) {
                      return;
                  }
      
                  messagingEnabled = enabled;
                  if (enabled) {
                      addJavascriptInterface(new ReactWebViewBridge(this), BRIDGE_NAME);
                      linkBridge();
                  } else {
                      removeJavascriptInterface(BRIDGE_NAME);
                  }
              }
      
              public void callInjectedJavaScript() {
                  if (getSettings().getJavaScriptEnabled() &&
                          injectedJS != null &&
                          !TextUtils.isEmpty(injectedJS)) {
                      loadUrl("javascript:(function() {\n" + injectedJS + ";\n})();");
                  }
              }
      
              public void linkBridge() {
                  if (messagingEnabled) {
                      if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                          // See isNative in lodash
                          String testPostMessageNative = "String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
                          evaluateJavascript(testPostMessageNative, new ValueCallback<String>() {
                              @Override
                              public void onReceiveValue(String value) {
                                  if (value.equals("true")) {
                                      FLog.w(ReactConstants.TAG, "Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
                                  }
                              }
                          });
                      }
      
                      loadUrl("javascript:(" +
                              "window.originalPostMessage = window.postMessage," +
                              "window.postMessage = function(data) {" +
                              BRIDGE_NAME + ".postMessage(String(data));" +
                              "}" +
                              ")");
                  }
              }
      
              public void onMessage(String message) {
                  dispatchEvent(this, new TopMessageEvent(this.getId(), message));
              }
      
              private void cleanupCallbacksAndDestroy() {
                  setWebViewClient(null);
                  destroy();
              }
          }
      
          public ReactWebViewManager() {
              mWebViewConfig = new WebViewConfig() {
                  public void configWebView(WebView webView) {
                  }
              };
          }
      
          public ReactWebViewManager(WebViewConfig webViewConfig) {
              mWebViewConfig = webViewConfig;
          }
      
          @Override
          public String getName() {
              return REACT_CLASS;
          }
      
          @Override
          protected WebView createViewInstance(final ThemedReactContext reactContext) {
              ReactWebView webView = new ReactWebView(reactContext);
              webView.setWebChromeClient(new WebChromeClient() {
                  @Override
                  public boolean onConsoleMessage(ConsoleMessage message) {
                      if (ReactBuildConfig.DEBUG) {
                          return super.onConsoleMessage(message);
                      }
                      return true;
                  }
      
                  @Override
                  public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
                      callback.invoke(origin, true, false);
                  }
      
                  private File createImageFile() throws IOException {
                      String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
                      String imageFileName = "JPEG_" + timeStamp + "_";
                      File storageDir = Environment.getExternalStoragePublicDirectory(
                              Environment.DIRECTORY_PICTURES);
                      File imageFile = new File(
                              storageDir,
                              imageFileName + ".jpg"
                      );
      
                      return imageFile;
                  }
      
      
                  private Intent getVideoCaptureIntent() {
                      Intent recordVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
                      recordVideoIntent
                              .resolveActivity(
                                      reactContext
                                          .getCurrentActivity()
                                          .getPackageManager()
                              );
      
                      recordVideoIntent.putExtra("type", "foobar");
                      return recordVideoIntent;
                  }
      
                  public boolean onShowFileChooser(
                          WebView webView,
                          ValueCallback<Uri[]> filePathCallback,
                          WebChromeClient.FileChooserParams fileChooserParams
                  ) {
                      if (mFilePathCallbackArr != null) {
                          mFilePathCallbackArr.onReceiveValue(null);
                      }
                      mFilePathCallbackArr = filePathCallback;
      
                      Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                      ComponentName comp = takePictureIntent
                                              .resolveActivity(
                                                      reactContext
                                                              .getCurrentActivity()
                                                              .getPackageManager()
                                              );
                      if (comp != null) {
                          File photoFile = null;
                          try {
                              photoFile = createImageFile();
                              takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
                          } catch (IOException ex) {
                              FLog.e(ReactConstants.TAG, "Unable to create Image File", ex);
                          }
      
                          if (photoFile != null) {
                              mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
                              takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                                      Uri.fromFile(photoFile));
                          } else {
                              takePictureIntent = null;
                          }
                      }
      
                      Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
                      contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
                      contentSelectionIntent.setType("*/*");
      
                      Intent videoCaptureIntent = getVideoCaptureIntent();
      
                      Intent[] intentArray;
                      if (takePictureIntent != null) {
                          intentArray = new Intent[]{takePictureIntent};
                      } else {
                          intentArray = new Intent[0];
                      }
      
                      intentArray = new Intent[]{
                              intentArray[0],
                              videoCaptureIntent
                      };
      
                      Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
                      chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
                      chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
                      chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
      
                      reactContext.getCurrentActivity().startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_GALLERY_IMAGE);
      
                      return true;
                  }
              });
      
      
              reactContext.addLifecycleEventListener(webView);
      
              reactContext.addActivityEventListener(new ActivityEventListener() {
      
                  // Android 5+
                  @Override
                  public void onActivityResult (Activity activity, int requestCode, int resultCode, Intent data) {
                      if(requestCode != INPUT_FILE_REQUEST_GALLERY_IMAGE || mFilePathCallbackArr == null) {
                          return;
                      }
                      Uri[] results = null;
      
                      if(resultCode == Activity.RESULT_OK) {
                          if(data == null || data.getData() == null) {
                              if(mCameraPhotoPath != null) {
                                  results = new Uri[]{Uri.parse(mCameraPhotoPath)};
                              }
                          } else {
                              String dataString = data.getDataString();
                              if (dataString != null) {
                                  results = new Uri[]{Uri.parse(dataString)};
                              }
                          }
                      }
      
                      if(results == null) {
                          mFilePathCallbackArr.onReceiveValue(new Uri[]{});
                      }
                      else {
                          mFilePathCallbackArr.onReceiveValue(results);
                      }
                      mFilePathCallbackArr = null;
                      return;
                  }
      
                  @Override
                  public void onNewIntent(Intent intent) {}
              });
      
              mWebViewConfig.configWebView(webView);
              webView.getSettings().setBuiltInZoomControls(true);
              webView.getSettings().setDisplayZoomControls(false);
              webView.getSettings().setDomStorageEnabled(true);
              webView.setLayoutParams(
                      new LayoutParams(LayoutParams.MATCH_PARENT,
                              LayoutParams.MATCH_PARENT));
      
              if (ReactBuildConfig.DEBUG && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                  WebView.setWebContentsDebuggingEnabled(true);
              }
      
              return webView;
          }
      
          @ReactProp(name = "javaScriptEnabled")
          public void setJavaScriptEnabled(WebView view, boolean enabled) {
              view.getSettings().setJavaScriptEnabled(enabled);
          }
      
          @ReactProp(name = "scalesPageToFit")
          public void setScalesPageToFit(WebView view, boolean enabled) {
              view.getSettings().setUseWideViewPort(!enabled);
          }
      
          @ReactProp(name = "domStorageEnabled")
          public void setDomStorageEnabled(WebView view, boolean enabled) {
              view.getSettings().setDomStorageEnabled(enabled);
          }
      
          @ReactProp(name = "userAgent")
          public void setUserAgent(WebView view, @Nullable String userAgent) {
              if (userAgent != null) {
                  // TODO(8496850): Fix incorrect behavior when property is unset (uA == null)
                  view.getSettings().setUserAgentString(userAgent);
              }
          }
      
          @ReactProp(name = "mediaPlaybackRequiresUserAction")
          public void setMediaPlaybackRequiresUserAction(WebView view, boolean requires) {
              view.getSettings().setMediaPlaybackRequiresUserGesture(requires);
          }
      
          @ReactProp(name = "allowUniversalAccessFromFileURLs")
          public void setAllowUniversalAccessFromFileURLs(WebView view, boolean allow) {
              view.getSettings().setAllowUniversalAccessFromFileURLs(allow);
          }
      
          @ReactProp(name = "injectedJavaScript")
          public void setInjectedJavaScript(WebView view, @Nullable String injectedJavaScript) {
              ((ReactWebView) view).setInjectedJavaScript(injectedJavaScript);
          }
      
          @ReactProp(name = "messagingEnabled")
          public void setMessagingEnabled(WebView view, boolean enabled) {
              ((ReactWebView) view).setMessagingEnabled(enabled);
          }
      
          @ReactProp(name = "source")
          public void setSource(WebView view, @Nullable ReadableMap source) {
          if (source != null) {
              if (source.hasKey("html")) {
                  String html = source.getString("html");
                  if (source.hasKey("baseUrl")) {
                      view.loadDataWithBaseURL(
                              source.getString("baseUrl"), html, HTML_MIME_TYPE, HTML_ENCODING, null);
                  } else {
                      view.loadData(html, HTML_MIME_TYPE, HTML_ENCODING);
                  }
                  return;
              }
              if (source.hasKey("uri")) {
                  String url = source.getString("uri");
                  String previousUrl = view.getUrl();
                  if (previousUrl != null && previousUrl.equals(url)) {
                      return;
                  }
                  if (source.hasKey("method")) {
                      String method = source.getString("method");
                      if (method.equals(HTTP_METHOD_POST)) {
                          byte[] postData = null;
                          if (source.hasKey("body")) {
                              String body = source.getString("body");
                              try {
                                  postData = body.getBytes("UTF-8");
                              } catch (UnsupportedEncodingException e) {
                                  postData = body.getBytes();
                              }
                          }
                          if (postData == null) {
                              postData = new byte[0];
                          }
                          view.postUrl(url, postData);
                          return;
                      }
                  }
                  HashMap<String, String> headerMap = new HashMap<>();
                  if (source.hasKey("headers")) {
                      ReadableMap headers = source.getMap("headers");
                      ReadableMapKeySetIterator iter = headers.keySetIterator();
                      while (iter.hasNextKey()) {
                          String key = iter.nextKey();
                          if ("user-agent".equals(key.toLowerCase(Locale.ENGLISH))) {
                              if (view.getSettings() != null) {
                                  view.getSettings().setUserAgentString(headers.getString(key));
                              }
                          } else {
                              headerMap.put(key, headers.getString(key));
                          }
                      }
                  }
                  view.loadUrl(url, headerMap);
                  return;
              }
          }
          view.loadUrl(BLANK_URL);
          }
      
          @ReactProp(name = "onContentSizeChange")
          public void setOnContentSizeChange(WebView view, boolean sendContentSizeChangeEvents) {
              if (sendContentSizeChangeEvents) {
                  view.setPictureListener(getPictureListener());
              } else {
                  view.setPictureListener(null);
              }
          }
      
          @ReactProp(name = "mixedContentMode")
          public void setMixedContentMode(WebView view, @Nullable String mixedContentMode) {
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                  if (mixedContentMode == null || "never".equals(mixedContentMode)) {
                      view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
                  } else if ("always".equals(mixedContentMode)) {
                      view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
                  } else if ("compatibility".equals(mixedContentMode)) {
                      view.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
                  }
              }
          }
      
          @Override
          protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
              view.setWebViewClient(new ReactWebViewClient());
          }
          @Override
          public
          @Nullable
          Map<String, Integer> getCommandsMap() {
              return MapBuilder.of(
                      "goBack", COMMAND_GO_BACK,
                      "goForward", COMMAND_GO_FORWARD,
                      "reload", COMMAND_RELOAD,
                      "stopLoading", COMMAND_STOP_LOADING,
                      "postMessage", COMMAND_POST_MESSAGE,
                      "injectJavaScript", COMMAND_INJECT_JAVASCRIPT
              );
          }
      
          @Override
          public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray args) {
              switch (commandId) {
                  case COMMAND_GO_BACK:
                      root.goBack();
                      break;
                  case COMMAND_GO_FORWARD:
                      root.goForward();
                      break;
                  case COMMAND_RELOAD:
                      root.reload();
                      break;
                  case COMMAND_STOP_LOADING:
                      root.stopLoading();
                      break;
                  case COMMAND_POST_MESSAGE:
                      try {
                          JSONObject eventInitDict = new JSONObject();
                          eventInitDict.put("data", args.getString(0));
                          root.loadUrl("javascript:(function () {" +
                                  "var event;" +
                                  "var data = " + eventInitDict.toString() + ";" +
                                  "try {" +
                                  "event = new MessageEvent('message', data);" +
                                  "} catch (e) {" +
                                  "event = document.createEvent('MessageEvent');" +
                                  "event.initMessageEvent('message', true, true, data.data, data.origin, data.lastEventId, data.source);" +
                                  "}" +
                                  "document.dispatchEvent(event);" +
                                  "})();");
                      } catch (JSONException e) {
                          throw new RuntimeException(e);
                      }
                      break;
                  case COMMAND_INJECT_JAVASCRIPT:
                      root.loadUrl("javascript:" + args.getString(0));
                      break;
              }
          }
      
          @Override
          public void onDropViewInstance(WebView webView) {
              super.onDropViewInstance(webView);
              ((ThemedReactContext) webView.getContext()).removeLifecycleEventListener((ReactWebView) webView);
              ((ReactWebView) webView).cleanupCallbacksAndDestroy();
          }
      
          private WebView.PictureListener getPictureListener() {
              if (mPictureListener == null) {
                  mPictureListener = new WebView.PictureListener() {
                      @Override
                      public void onNewPicture(WebView webView, Picture picture) {
                          dispatchEvent(
                                  webView,
                                  new ContentSizeChangeEvent(
                                          webView.getId(),
                                          webView.getWidth(),
                                          webView.getContentHeight()));
                      }
                  };
              }
              return mPictureListener;
          }
          private static void dispatchEvent(WebView webView, Event event) {
              ReactContext reactContext = (ReactContext) webView.getContext();
              EventDispatcher eventDispatcher =
                      reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
              eventDispatcher.dispatchEvent(event);
          }
      }
      

      【讨论】:

      • 要回答一些几乎“是”/“否”的问题,需要大量代码。我什至不知道“如何”被埋在那里,因为有这么多!
      【解决方案3】:

      我在任何地方都找不到这个解决方案,所以我想我会分享一下,希望对您有所帮助...

      要使浏览按钮能够工作并随后允许文件访问,您可以用这些修改版本替换上面和这个 react-native 文件:

      node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ThemedReactContext.java

      /**
       * Copyright (c) 2015-present, Facebook, Inc.
       * All rights reserved.
       * This source code is licensed under the BSD-style license found in the
       * LICENSE file in the root directory of this source tree. An additional grant
       * of patent rights can be found in the PATENTS file in the same directory.
       */
      
      package com.facebook.react.uimanager;
      
      import javax.annotation.Nullable;
      
      import android.app.Activity;
      import android.content.Context;
      
      import com.facebook.react.bridge.ActivityEventListener;
      import com.facebook.react.bridge.ReactApplicationContext;
      import com.facebook.react.bridge.ReactContext;
      import com.facebook.react.bridge.LifecycleEventListener;
      
      //
      
      /**
       * Wraps {@link ReactContext} with the base {@link Context} passed into the constructor.
       * It provides also a way to start activities using the viewContext to which RN native views belong.
       * It delegates lifecycle listener registration to the original instance of {@link ReactContext}
       * which is supposed to receive the lifecycle events. At the same time we disallow receiving
       * lifecycle events for this wrapper instances.
       * TODO: T7538544 Rename ThemedReactContext to be in alignment with name of ReactApplicationContext
       */
      public class ThemedReactContext extends ReactContext {
      
          private final ReactApplicationContext mReactApplicationContext;
      
          public ThemedReactContext(ReactApplicationContext reactApplicationContext, Context base) {
              super(base);
              initializeWithInstance(reactApplicationContext.getCatalystInstance());
              mReactApplicationContext = reactApplicationContext;
          }
      
          @Override
          public void addLifecycleEventListener(LifecycleEventListener listener) {
              mReactApplicationContext.addLifecycleEventListener(listener);
          }
      
          @Override
          public void removeLifecycleEventListener(LifecycleEventListener listener) {
              mReactApplicationContext.removeLifecycleEventListener(listener);
          }
      
          @Override
          public void addActivityEventListener(ActivityEventListener listener) {
              mReactApplicationContext.addActivityEventListener(listener);
          }
      
      
          @Override
          public void removeActivityEventListener(ActivityEventListener listener) {
              mReactApplicationContext.removeActivityEventListener(listener);
          }
      
          @Override
          public boolean hasCurrentActivity() {
              return mReactApplicationContext.hasCurrentActivity();
          }
      
          @Override
          public
          @Nullable
          Activity getCurrentActivity() {
              return mReactApplicationContext.getCurrentActivity();
          }
      }
      

      上面列出了第二个文件

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-26
        • 1970-01-01
        • 2018-08-22
        • 2016-05-04
        • 1970-01-01
        • 2018-02-24
        • 2020-07-11
        • 2016-08-15
        相关资源
        最近更新 更多