RN集成自签名https及双向认证-android(2)

上一篇文章讲了ios的这里接着讲RN-android-自签名https的配置,首先android比较复杂,首先用AS打开android工程

android有4个地方得改

  1. fetch (.jar包里的代码不能直接改)
  2. webview (.jar包里的代码不能直接改)
  3. Image (.jar包里的代码不能直接改--)
  4. react-native-fetch-blob(可以直接改源码-RNFetchBlobReq
    RNFetchBlob 这两个类)
  5. 如果使用了react-native-navigation时,在我们copy出MainReactPackage使用时会报错,注释掉react-native-navigation报错的地方就行

注意:RN的不同版本,会有不同的依赖问题,得根据报错一个一个改,很麻烦

fetch

  • 修改路径和原理
    安卓三方的依赖库在这可以看到


    AADFC784-0EDE-4B65-8633-7577568F4294.png
17A28174-6D4C-4C0C-9773-FA77D93D2C0F.png

NetworkingModule.java文件就是我们android最底层的网络请求工具类,每次创建NetworkingModule对象的时候,我们会传入一个okhttpclient的对象:

public NetworkingModule(ReactApplicationContext context) {
        this(context, (String)null, OkHttpClientProvider.createClient(), (List)null);
    }

    public NetworkingModule(ReactApplicationContext context, List<NetworkInterceptorCreator> networkInterceptorCreators) {
        this(context, (String)null, OkHttpClientProvider.createClient(), networkInterceptorCreators);
    }

    public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) {
        this(context, defaultUserAgent, OkHttpClientProvider.createClient(), (List)null);
    }

可以看到,每次都是通过:

OkHttpClientProvider.createClient()

的方式创建了一个okhttpclient对象,最后发送请求为:

 @ReactMethod
    public void sendRequest(ExecutorToken executorToken, String method, String url, final int requestId, ReadableArray headers, ReadableMap data, final String responseType, final boolean useIncrementalUpdates, int timeout) {
        okhttp3.Request.Builder requestBuilder = (new okhttp3.Request.Builder()).url(url);
        if(requestId != 0) {
            requestBuilder.tag(Integer.valueOf(requestId));
        }

        final RCTDeviceEventEmitter eventEmitter = this.getEventEmitter(executorToken);
        Builder clientBuilder = this.mClient.newBuilder();
        ....

所以我们只需要替换掉默认的httpclient,然后为其添加上证书认证就可以了,我们打开 OkHttpClientProvider.createClient()方法:

 public static OkHttpClient createClient() {
        Builder client = (new Builder()).connectTimeout(0L, TimeUnit.MILLISECONDS).readTimeout(0L, TimeUnit.MILLISECONDS).writeTimeout(0L, TimeUnit.MILLISECONDS).cookieJar(new ReactCookieJarContainer());
        return enableTls12OnPreLollipop(client).build();
    }

OkHttpClientProvider这个工具类就是为我们提供了一个修改默认okhttpclient的方法,我们看到其中有一个方法:

public static void replaceOkHttpClient(OkHttpClient client) {
        sClient = client;
    }

这个方法就是替换掉rn中网络请求默认的okhttpclient方法。
所以我们在程序初始化的时候改掉默认的okhttpclient.

  1. 首先找到你项目的application文件,在oncreate方法中提供其方法:
 @Override
    public void onCreate() {
      ...
        //RN OKHTTP添加https证书
        OkHttpClientProvider.replaceOkHttpClient(initCustomOkHttpClient());
      ...
    }

//自定义初始化client方法-
public OkHttpClient initCustomOkHttpClient() {
        OkHttpClient.Builder client = new OkHttpClient.Builder()
                .connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .readTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .writeTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .cookieJar(new ReactCookieJarContainer());
        client.addNetworkInterceptor(new StethoInterceptor());
        client.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
        try {
        //你的证书文件,放在android的assets文件夹下
            setCertificates(client, getAssets().open("CA.crt"));
            client.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        OkHttpClient.Builder builder = OkHttpClientProvider.enableTls12OnPreLollipop(client);
        return builder.build();
    }

//处理证书
public void setCertificates(OkHttpClient.Builder client, InputStream... certificates) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));

                try {
                    if (certificate != null)
                        certificate.close();
                } catch (IOException e) {
                }
            }

            SSLContext sslContext = SSLContext.getInstance("TLS");

            TrustManagerFactory trustManagerFactory =
                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            trustManagerFactory.init(keyStore);
            sslContext.init(
                    null,
                    trustManagerFactory.getTrustManagers(),
                    new SecureRandom()
            );
            client.sslSocketFactory(sslContext.getSocketFactory());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  1. 修改NetworkingModule.java文件的以下方法,把默认创建okhttpclient的方式:OkHttpClientProvider.createClient(),全部改为:OkHttpClientProvider.getOkHttpClient()
public NetworkingModule(ReactApplicationContext context) {
        this(context, (String)null, OkHttpClientProvider.createClient(), (List)null);
    }

    public NetworkingModule(ReactApplicationContext context, List<NetworkInterceptorCreator> networkInterceptorCreators) {
        this(context, (String)null, OkHttpClientProvider.createClient(), networkInterceptorCreators);
    }

    public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) {
        this(context, defaultUserAgent, OkHttpClientProvider.createClient(), (List)null);
    }

如下:

  /**
   * @param context the ReactContext of the application
   */
  public NetworkingModule(final ReactApplicationContext context) {
    this(context, null, OkHttpClientProvider.getOkHttpClient(), null);
  }

  /**
   * @param context the ReactContext of the application
   * @param networkInterceptorCreators list of {@link NetworkInterceptorCreator}'s whose create()
   * methods would be called to attach the interceptors to the client.
   */
  public NetworkingModule(
    ReactApplicationContext context,
    List<NetworkInterceptorCreator> networkInterceptorCreators) {
    this(context, null, OkHttpClientProvider.getOkHttpClient(), networkInterceptorCreators);
  }

  /**
   * @param context the ReactContext of the application
   * @param defaultUserAgent the User-Agent header that will be set for all requests where the
   * caller does not provide one explicitly
   */
  public NetworkingModule(ReactApplicationContext context, String defaultUserAgent) {
    this(context, defaultUserAgent, OkHttpClientProvider.getOkHttpClient(), null);
  }

问题:react-native的第三方依赖库,改不了源码咋办呢?
我们直接copy一份NetworkingModule跟mainreactpackage的代码,然后在application中把mainreactpackage替换成我们copy出去的那一份,

private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
            //替换成我们copy的那个文件
                    new MainReactPackage(),
                    new VectorIconsPackage(),
                    new RCTSplashScreenPackage(),
                    new RNDeviceInfo(),
                    new AndroidModulePackage(),
                    new RCTSwipeRefreshLayoutPackage()
            );
        }
    };

然后点进我们copy的那个MainReactPackage文件,再把MainReactPackage中的:

new ModuleSpec(NetworkingModule.class, new Provider<NativeModule>() {
        @Override
        public NativeModule get() {
        //替换成我们自己copy的那个文件
          return new NetworkingModule(context);
        }
      }),

MainReactPackage路径.png
  • Image部分


    android_Image路径.png
android图片https修改的地方.png

但是RN对应的那一部分.jar包是不可改的,,主要的问题还是怎么去改动代码

摘要 :

如何改动android.jar 包rn_androi_https

https需要复制出来的文件.png

MainReactPackage路径.png

  • webView
    • ReactWebviewManager.java
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
* <p>
* 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 petrochina.cplh.qqgl2.htttps;

import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Picture;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.view.ViewGroup.LayoutParams;
import android.webkit.ConsoleMessage;
import android.webkit.CookieManager;
import android.webkit.GeolocationPermissions;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.facebook.common.logging.FLog;
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.ReactConstants;
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.WebViewConfig;
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.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Nullable;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;

/**
* Manages instances of {@link WebView}
* <p>
* Can accept following commands:
* - GO_BACK
* - GO_FORWARD
* - RELOAD
* <p>
* {@link WebView} instances could emit following direct events:
* - topLoadingFinish
* - topLoadingStart
* - topLoadingError
* <p>
* Each event will carry the following properties:
* - target - view's react tag
* - url - url set for the webview
* - loading - whether webview is in a loading state
* - title - title of the current page
* - canGoBack - boolean, whether there is anything on a history stack to go back
* - canGoForward - boolean, whether it is possible to request GO_FORWARD command
*/
@ReactModule(name = ReactWebViewManager.REACT_CLASS)
public class ReactWebViewManager extends SimpleViewManager<WebView> {

   protected static final String REACT_CLASS = "RCTWebView";

   protected static final String HTML_ENCODING = "UTF-8";
   protected static final String HTML_MIME_TYPE = "text/html";
   protected static final String BRIDGE_NAME = "__REACT_WEB_VIEW_BRIDGE";

   protected 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;

   // Use `webView.loadUrl("about:blank")` to reliably reset the view
   // state and release page resources (including any running JavaScript).
   protected static final String BLANK_URL = "about:blank";

   protected WebViewConfig mWebViewConfig;
   protected @Nullable
   WebView.PictureListener mPictureListener;

   private static ThemedReactContext reactContext;

   protected static class ReactWebViewClient extends WebViewClient {

       //    ---https 双向认证------
       private SSLContext sslContext;

       public ReactWebViewClient() {

           // 添加https证书
           try {
               InputStream is = reactContext.getAssets().open("CNPCCA.cer");
               NetConfig.addCertificate(is); // 这里将证书读取出来,,放在配置中byte[]里
           } catch (IOException ioe) {
               ioe.printStackTrace();
           }
           // 添加证书cer
           List<InputStream> certificates = new ArrayList<>();
           List<byte[]> certs_data = NetConfig.getCertificatesData();
           // 将字节数组转为数组输入流
           if (certs_data != null && !certs_data.isEmpty()) {
               for (byte[] bytes : certs_data) {
                   certificates.add(new ByteArrayInputStream(bytes));
               }
           }
           try {
               prepareSslPinning(certificates);
           } catch (IOException e) {
               e.printStackTrace();
           }
       }

       @Override
       public WebResourceResponse shouldInterceptRequest(final WebView view, String url) {
           Log.i("shouldInterceptRequest", "shouldInterceptRequest1");

           if (url.indexOf("https") != -1) {
               return processRequest(url);
           } else {
               return null;
           }

       }

       private WebResourceResponse processRequest(String webUrl) {
//      LogUtils.i("SSL_PINNING_WEBVIEWS", "GET: " + webUrl.toString());

           try {
               // Setup connection
               URL url = new URL(webUrl);
               HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();

               // Set SSL Socket Factory for this request
               urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
               urlConnection.setHostnameVerifier(new HostnameVerifier() {
                   @Override
                   public boolean verify(String hostname, SSLSession session) {
                       return true;
                   }
               });//很重要,校验证书

               // Get content, contentType and encoding
               InputStream is = urlConnection.getInputStream();
               String contentType = urlConnection.getContentType();
               String encoding = urlConnection.getContentEncoding();

               // If got a contentType header
               if (contentType != null) {

                   String mimeType = contentType;

                   // Parse mime type from contenttype string
                   if (contentType.contains(";")) {
                       mimeType = contentType.split(";")[0].trim();
                   }

//          LogUtils.i("SSL_PINNING_WEBVIEWS", "Mime: " + mimeType);


                   // Return the response
                   return new WebResourceResponse(mimeType, encoding, is);
               }

           } catch (Exception e) {
               e.printStackTrace();

//        LogUtils.i("SSL_PINNING_WEBVIEWS", e.getLocalizedMessage());
           }

           // Return empty response for this request
           return new WebResourceResponse(null, null, null);
       }

       private void prepareSslPinning(List<InputStream> certificates) throws IOException {

           try {

               // 服务器端需要验证的客户端证书,其实就是客户端的keystore
               KeyManagerFactory keyManagerFactory = KeyManagerFactory
                       .getInstance("X509");

               TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");

               KeyStore keyStore = KeyStore.getInstance("PKCS12");

               KeyStore keyStore2 = KeyStore.getInstance(KeyStore.getDefaultType());
               //读取证书
               InputStream ksIn = reactContext.getResources().getAssets().open("client.p12");//***:你的p12证书

               //加载证书
               keyStore2.load(null);
               keyStore.load(ksIn, "cplh123456".toCharArray());//***:p12证书的密码,必须

               ksIn.close();
               CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
               try {
                   for (int i = 0, size = certificates.size(); i < size; ) {
                       InputStream certificate = certificates.get(i);
                       String certificateAlias = Integer.toString(i++);
                       keyStore2.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
                       if (certificate != null)
                           certificate.close();
                   }

               } catch (IOException e) {

                   e.printStackTrace();

               }
               sslContext = SSLContext.getInstance("TLS");
//            TrustManagerFactory trustManagerFactory =
//
//                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());


               keyManagerFactory.init(keyStore, "cplh123456".toCharArray());
               trustManagerFactory.init(keyStore2);
               sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);


           } catch (Exception e) {

               e.printStackTrace();

           }
       }


      //    ---https 双向认证------

       protected boolean mLastLoadFailed = false;
       protected @Nullable
       ReadableArray mUrlPrefixesForDefaultIntent;

       @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) {
           boolean useDefaultIntent = false;
           if (mUrlPrefixesForDefaultIntent != null && mUrlPrefixesForDefaultIntent.size() > 0) {
               ArrayList<Object> urlPrefixesForDefaultIntent =
                       mUrlPrefixesForDefaultIntent.toArrayList();
               for (Object urlPrefix : urlPrefixesForDefaultIntent) {
                   if (url.startsWith((String) urlPrefix)) {
                       useDefaultIntent = true;
                       break;
                   }
               }
           }

           if (!useDefaultIntent &&
                   (url.startsWith("http://") || url.startsWith("https://") ||
                           url.startsWith("file://") || url.equals("about:blank"))) {
               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;

           // In case of an error JS side expect to get a finish event first, and then get an error event
           // Android WebView does it in the opposite way, so we need to simulate that behavior
           emitFinishEvent(webView, failingUrl);

           WritableMap eventData = createWebViewEvent(webView, failingUrl);
           eventData.putDouble("code", errorCode);
           eventData.putString("description", description);

           dispatchEvent(
                   webView,
                   new TopLoadingErrorEvent(webView.getId(), eventData));
       }

       protected void emitFinishEvent(WebView webView, String url) {
           dispatchEvent(
                   webView,
                   new TopLoadingFinishEvent(
                           webView.getId(),
                           createWebViewEvent(webView, url)));
       }

       protected WritableMap createWebViewEvent(WebView webView, String url) {
           WritableMap event = Arguments.createMap();
           event.putDouble("target", webView.getId());
           // Don't use webView.getUrl() here, the URL isn't updated to the new value yet in callbacks
           // like onPageFinished
           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;
       }

       public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
           mUrlPrefixesForDefaultIntent = specialUrls;
       }
   }

   /**
    * Subclass of {@link WebView} that implements {@link LifecycleEventListener} interface in order
    * to call {@link WebView#destroy} on activity destroy event and also to clear the client
    */
   protected static class ReactWebView extends WebView implements LifecycleEventListener {
       protected @Nullable
       String injectedJS;
       protected boolean messagingEnabled = false;
       protected @Nullable
       ReactWebViewClient mReactWebViewClient;

       protected class ReactWebViewBridge {
           ReactWebView mContext;

           ReactWebViewBridge(ReactWebView c) {
               mContext = c;
           }

           @JavascriptInterface
           public void postMessage(String message) {
               mContext.onMessage(message);
           }
       }

       /**
        * WebView must be created with an context of the current activity
        * <p>
        * Activity Context is required for creation of dialogs internally by WebView
        * Reactive Native needed for access to ReactNative internal system functionality
        */
       public ReactWebView(ThemedReactContext reactContext) {
           super(reactContext);
       }

       @Override
       public void onHostResume() {
           // do nothing
       }

       @Override
       public void onHostPause() {
           // do nothing
       }

       @Override
       public void onHostDestroy() {
           cleanupCallbacksAndDestroy();
       }

       @Override
       public void setWebViewClient(WebViewClient client) {
           super.setWebViewClient(client);
           mReactWebViewClient = (ReactWebViewClient) client;
       }

       public @Nullable
       ReactWebViewClient getReactWebViewClient() {
           return mReactWebViewClient;
       }

       public void setInjectedJavaScript(@Nullable String js) {
           injectedJS = js;
       }

       protected ReactWebViewBridge createReactWebViewBridge(ReactWebView webView) {
           return new ReactWebViewBridge(webView);
       }

       public void setMessagingEnabled(boolean enabled) {
           if (messagingEnabled == enabled) {
               return;
           }

           messagingEnabled = enabled;
           if (enabled) {
               addJavascriptInterface(createReactWebViewBridge(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));
       }

       protected 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;
   }

   protected ReactWebView createReactWebViewInstance(ThemedReactContext reactContext) {
       return new ReactWebView(reactContext);
   }

   @Override
   protected WebView createViewInstance(ThemedReactContext reactContext) {
       ReactWebView webView = createReactWebViewInstance(reactContext);
       webView.setWebChromeClient(new WebChromeClient() {
           @Override
           public boolean onConsoleMessage(ConsoleMessage message) {
               if (ReactBuildConfig.DEBUG) {
                   return super.onConsoleMessage(message);
               }
               // Ignore console logs in non debug builds.
               return true;
           }

           @Override
           public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
               callback.invoke(origin, true, false);
           }
       });
       reactContext.addLifecycleEventListener(webView);
       mWebViewConfig.configWebView(webView);
       webView.getSettings().setBuiltInZoomControls(true);
       webView.getSettings().setDisplayZoomControls(false);
       webView.getSettings().setDomStorageEnabled(true);
       webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
       webView.getSettings().setAllowFileAccess(true);
       webView.getSettings().setAllowFileAccessFromFileURLs(true);
       webView.getSettings().setJavaScriptEnabled(true);
       webView.getSettings().setBlockNetworkImage(false);
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
           webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
       }

       // Fixes broken full-screen modals/galleries due to body height being 0.
       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 = "thirdPartyCookiesEnabled")
   public void setThirdPartyCookiesEnabled(WebView view, boolean enabled) {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
           CookieManager.getInstance().setAcceptThirdPartyCookies(view, 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 = "saveFormDataDisabled")
   public void setSaveFormDataDisabled(WebView view, boolean disable) {
       view.getSettings().setSaveFormData(!disable);
   }

   @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);
           }
       }
   }

   @ReactProp(name = "urlPrefixesForDefaultIntent")
   public void setUrlPrefixesForDefaultIntent(
           WebView view,
           @Nullable ReadableArray urlPrefixesForDefaultIntent) {
       ReactWebViewClient client = ((ReactWebView) view).getReactWebViewClient();
       if (client != null && urlPrefixesForDefaultIntent != null) {
           client.setUrlPrefixesForDefaultIntent(urlPrefixesForDefaultIntent);
       }
   }

   @Override
   protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
       this.reactContext = reactContext;
       // Do not register default touch emitter and let WebView implementation handle touches
       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();
   }

   protected 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;
   }

   protected static void dispatchEvent(WebView webView, Event event) {
       ReactContext reactContext = (ReactContext) webView.getContext();
       EventDispatcher eventDispatcher =
               reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
       eventDispatcher.dispatchEvent(event);
   }
}
  • react-native-fetch-blob
    • RNFetchBlobReq


      RNFetchBlobReq.png
    • RNFetchBlob


      RNFetchBlob.png
package com.RNFetchBlob;

import android.app.Activity;
import android.app.DownloadManager;
import android.content.Intent;
import android.net.Uri;

import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;

// Cookies
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.network.ForwardingCookieHandler;
import com.facebook.react.modules.network.CookieJarContainer;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;

import okhttp3.OkHttpClient;
import okhttp3.JavaNetCookieJar;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManagerFactory;

import static android.app.Activity.RESULT_OK;
import static com.RNFetchBlob.RNFetchBlobConst.GET_CONTENT_INTENT;

public class RNFetchBlob extends ReactContextBaseJavaModule {

    // Cookies
    private final ForwardingCookieHandler mCookieHandler;
    private final CookieJarContainer mCookieJarContainer;
    private final OkHttpClient mClient;

    static ReactApplicationContext RCTContext;
    static LinkedBlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
    static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
    static LinkedBlockingQueue<Runnable> fsTaskQueue = new LinkedBlockingQueue<>();
    static ThreadPoolExecutor fsThreadPool = new ThreadPoolExecutor(2, 10, 5000, TimeUnit.MILLISECONDS, taskQueue);
    static public boolean ActionViewVisible = false;
    static HashMap<Integer, Promise> promiseTable = new HashMap<>();

     //https --修改 --
    public OkHttpClient initCustomOkHttpClient() {
        OkHttpClient.Builder client = new OkHttpClient.Builder()
                .connectTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .readTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .writeTimeout(30 * 1000, TimeUnit.MILLISECONDS)
                .cookieJar(new ReactCookieJarContainer());
//        client.addNetworkInterceptor(new StethoInterceptor());
//        client.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
        try {
            //你的证书文件,放在android的assets文件夹下
            setCertificates(client, RCTContext.getAssets().open("CNPCCA.cer"));
            client.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        OkHttpClient.Builder builder = OkHttpClientProvider.enableTls12OnPreLollipop(client);
        return builder.build();
    }

    public void setCertificates(OkHttpClient.Builder client, InputStream... certificates) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));

                try {
                    if (certificate != null)
                        certificate.close();
                } catch (IOException e) {
                }
            }

            SSLContext sslContext = SSLContext.getInstance("TLS");

            TrustManagerFactory trustManagerFactory =
                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            trustManagerFactory.init(keyStore);

            //初始化keystore
            KeyStore clientKeyStore = KeyStore.getInstance("BKS");
            clientKeyStore.load(RCTContext.getAssets().open("client.bks"), "mima".toCharArray());

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(clientKeyStore, "mima".toCharArray());

            sslContext.init(
                    keyManagerFactory.getKeyManagers(),
                    trustManagerFactory.getTrustManagers(),
                    new SecureRandom()
            );
            client.sslSocketFactory(sslContext.getSocketFactory());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
            //https --修改 --
    public RNFetchBlob(ReactApplicationContext reactContext) {

        super(reactContext);

        RCTContext = reactContext;
        mClient = initCustomOkHttpClient();
        mCookieHandler = new ForwardingCookieHandler(reactContext);
        mCookieJarContainer = (CookieJarContainer) mClient.cookieJar();
        mCookieJarContainer.setCookieJar(new JavaNetCookieJar(mCookieHandler));


        reactContext.addActivityEventListener(new ActivityEventListener() {
            @Override
            public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
                if(requestCode == GET_CONTENT_INTENT && resultCode == RESULT_OK) {
                    Uri d = data.getData();
                    promiseTable.get(GET_CONTENT_INTENT).resolve(d.toString());
                    promiseTable.remove(GET_CONTENT_INTENT);
                }
            }

            @Override
            public void onNewIntent(Intent intent) {

            }
        });
    }

    @Override
    public String getName() {
        return "RNFetchBlob";
    }

    @Override
    public Map<String, Object> getConstants() {
        return RNFetchBlobFS.getSystemfolders(this.getReactApplicationContext());
    }

    @ReactMethod
    public void createFile(final String path, final String content, final String encode, final Callback callback) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.createFile(path, content, encode, callback);
            }
        });

    }

    @ReactMethod
    public void actionViewIntent(String path, String mime, final Promise promise) {
        try {
            Intent intent= new Intent(Intent.ACTION_VIEW)
                    .setDataAndType(Uri.parse("file://" + path), mime);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            this.getReactApplicationContext().startActivity(intent);
            ActionViewVisible = true;

            final LifecycleEventListener listener = new LifecycleEventListener() {
                @Override
                public void onHostResume() {
                    if(ActionViewVisible)
                        promise.resolve(null);
                    RCTContext.removeLifecycleEventListener(this);
                }

                @Override
                public void onHostPause() {

                }

                @Override
                public void onHostDestroy() {

                }
            };
            RCTContext.addLifecycleEventListener(listener);
        } catch(Exception ex) {
            promise.reject(ex.getLocalizedMessage());
        }
    }

    @ReactMethod
    public void createFileASCII(final String path, final ReadableArray dataArray, final Callback callback) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.createFileASCII(path, dataArray, callback);
            }
        });

    }

    @ReactMethod
    public void writeArrayChunk(final String streamId, final ReadableArray dataArray, final Callback callback) {
        RNFetchBlobFS.writeArrayChunk(streamId, dataArray, callback);
    }

    @ReactMethod
    public void unlink(String path, Callback callback) {
        RNFetchBlobFS.unlink(path, callback);
    }

    @ReactMethod
    public void mkdir(String path, Callback callback) {
        RNFetchBlobFS.mkdir(path, callback);
    }

    @ReactMethod
    public void exists(String path, Callback callback) {
        RNFetchBlobFS.exists(path, callback);
    }

    @ReactMethod
    public void cp(final String path, final String dest, final Callback callback) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.cp(path, dest, callback);
            }
        });

    }

    @ReactMethod
    public void mv(String path, String dest, Callback callback) {
        RNFetchBlobFS.mv(path, dest, callback);
    }

    @ReactMethod
    public void ls(String path, Callback callback) {
        RNFetchBlobFS.ls(path, callback);
    }

    @ReactMethod
    public void writeStream(String path, String encode, boolean append, Callback callback) {
        new RNFetchBlobFS(this.getReactApplicationContext()).writeStream(path, encode, append, callback);
    }

    @ReactMethod
    public void writeChunk(String streamId, String data, Callback callback) {
        RNFetchBlobFS.writeChunk(streamId, data, callback);
    }

    @ReactMethod
    public void closeStream(String streamId, Callback callback) {
        RNFetchBlobFS.closeStream(streamId, callback);
    }

    @ReactMethod
    public void removeSession(ReadableArray paths, Callback callback) {
        RNFetchBlobFS.removeSession(paths, callback);
    }

    @ReactMethod
    public void readFile(final String path, final String encoding, final Promise promise) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.readFile(path, encoding, promise);
            }
        });
    }

    @ReactMethod
    public void writeFileArray(final String path, final ReadableArray data, final boolean append, final Promise promise) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.writeFile(path, data, append, promise);
            }
        });
    }

    @ReactMethod
    public void writeFile(final String path, final String encoding, final String data, final boolean append, final Promise promise) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.writeFile(path, encoding, data, append, promise);
            }
        });

    }

    @ReactMethod
    public void lstat(String path, Callback callback) {
        RNFetchBlobFS.lstat(path, callback);
    }

    @ReactMethod
    public void stat(String path, Callback callback) {
        RNFetchBlobFS.stat(path, callback);
    }

    @ReactMethod
    public void scanFile(final ReadableArray pairs, final Callback callback) {
        final ReactApplicationContext ctx = this.getReactApplicationContext();
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                int size = pairs.size();
                String [] p = new String[size];
                String [] m = new String[size];
                for(int i=0;i<size;i++) {
                    ReadableMap pair = pairs.getMap(i);
                    if(pair.hasKey("path")) {
                        p[i] = pair.getString("path");
                        if(pair.hasKey("mime"))
                            m[i] = pair.getString("mime");
                        else
                            m[i] = null;
                    }
                }
                new RNFetchBlobFS(ctx).scanFile(p, m, callback);
            }
        });

    }

    @ReactMethod
    /**
     * @param path Stream file path
     * @param encoding Stream encoding, should be one of `base64`, `ascii`, and `utf8`
     * @param bufferSize Stream buffer size, default to 4096 or 4095(base64).
     */
    public void readStream(final String path, final String encoding, final int bufferSize, final int tick, final String streamId) {
        final ReactApplicationContext ctx = this.getReactApplicationContext();
        fsThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS fs = new RNFetchBlobFS(ctx);
                fs.readStream(path, encoding, bufferSize, tick, streamId);
            }
        });
    }

    @ReactMethod
    public void cancelRequest(String taskId, Callback callback) {
        try {
            RNFetchBlobReq.cancelTask(taskId);
            callback.invoke(null, taskId);
        } catch (Exception ex) {
            callback.invoke(ex.getLocalizedMessage(), null);
        }
    }

    @ReactMethod
    public void slice(String src, String dest, int start, int end, Promise promise) {
        RNFetchBlobFS.slice(src, dest, start, end, "", promise);
    }

    @ReactMethod
    public void enableProgressReport(String taskId, int interval, int count) {
        RNFetchBlobProgressConfig config = new RNFetchBlobProgressConfig(true, interval, count, RNFetchBlobProgressConfig.ReportType.Download);
        RNFetchBlobReq.progressReport.put(taskId, config);
    }

    @ReactMethod
    public void df(final Callback callback) {
        fsThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                RNFetchBlobFS.df(callback);
            }
        });
    }


    @ReactMethod
    public void enableUploadProgressReport(String taskId, int interval, int count) {
        RNFetchBlobProgressConfig config = new RNFetchBlobProgressConfig(true, interval, count, RNFetchBlobProgressConfig.ReportType.Upload);
        RNFetchBlobReq.uploadProgressReport.put(taskId, config);
    }

    @ReactMethod
    public void fetchBlob(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, final Callback callback) {
        new RNFetchBlobReq(options, taskId, method, url, headers, body, null, mClient, callback).run();
}

    @ReactMethod
    public void fetchBlobForm(ReadableMap options, String taskId, String method, String url, ReadableMap headers, ReadableArray body, final Callback callback) {
        new RNFetchBlobReq(options, taskId, method, url, headers, null, body, mClient, callback).run();
    }

    @ReactMethod
    public void getContentIntent(String mime, Promise promise) {
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        if(mime != null)
            i.setType(mime);
        else
            i.setType("*/*");
        promiseTable.put(GET_CONTENT_INTENT, promise);
        this.getReactApplicationContext().startActivityForResult(i, GET_CONTENT_INTENT, null);

    }

    @ReactMethod
    public void addCompleteDownload (ReadableMap config, Promise promise) {
        DownloadManager dm = (DownloadManager) RNFetchBlob.RCTContext.getSystemService(RNFetchBlob.RCTContext.DOWNLOAD_SERVICE);
        String path = RNFetchBlobFS.normalizePath(config.getString("path"));
        if(path == null) {
            promise.reject("RNFetchblob.addCompleteDownload can not resolve URI:" + config.getString("path"), "RNFetchblob.addCompleteDownload can not resolve URI:" + path);
            return;
        }
        try {
            WritableMap stat = RNFetchBlobFS.statFile(path);
            dm.addCompletedDownload(
                    config.hasKey("title") ? config.getString("title") : "",
                    config.hasKey("description") ? config.getString("description") : "",
                    true,
                    config.hasKey("mime") ? config.getString("mime") : null,
                    path,
                    Long.valueOf(stat.getString("size")),
                    config.hasKey("showNotification") && config.getBoolean("showNotification")
            );
            promise.resolve(null);
        }
        catch(Exception ex) {
            promise.reject("RNFetchblob.addCompleteDownload failed", ex.getStackTrace().toString());
        }

    }

}

原理就是,import我们copy改动的类,不再使用.jar包里的类,option+return自动import包路径
具体查看管知汇,前期管理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容