美文网首页
Native API 之 NetworkingModule

Native API 之 NetworkingModule

作者: 卑鄙的鹿尤菌 | 来源:发表于2017-06-06 17:04 被阅读87次

    NetworkingModule为JS端提供了发送网络请求的接口。在Native层,React Native 依赖了okttp网络库。主要API 包括以下三个:

    • sendRequest:发送网络请求
    • abortRequest:中断网络请求
    • clearCookies :清除cookie

    其中最重要的的是sendRequest方法,下面我们进行详细分析。


    NetworkingModule创建时初始化了默认的OkHttpClient,并在初始化时设置了cookie管理和存储策略:

    public static OkHttpClient createClient() {
        // No timeouts by default
        OkHttpClient.Builder client = new OkHttpClient.Builder()
          .connectTimeout(0, TimeUnit.MILLISECONDS)
          .readTimeout(0, TimeUnit.MILLISECONDS)
          .writeTimeout(0, TimeUnit.MILLISECONDS)
          .cookieJar(new ReactCookieJarContainer());
    
        return enableTls12OnPreLollipop(client).build();
      }
    public void initialize() {
        // cookie管理和存储策略
        mCookieJarContainer.setCookieJar(new JavaNetCookieJar(mCookieHandler));
    }
    

    默认情况下使用webview的CookieManager来管理cookie。首先看一下暴露给JS端的网络请求方法sendRequest

      public void sendRequest(
          final ExecutorToken executorToken, //用于保证JS调用Native API时返回结果给对应的JS VM
          String method, 
          String url,
          final int requestId,
          ReadableArray headers,
          ReadableMap data,
          final String responseType,
          final boolean useIncrementalUpdates,
          int timeout) {
        Request.Builder requestBuilder = new Request.Builder().url(url);
    
        if (requestId != 0) {
          requestBuilder.tag(requestId);
        }
    
        final RCTDeviceEventEmitter eventEmitter = getEventEmitter(executorToken);
        OkHttpClient.Builder clientBuilder = mClient.newBuilder();
    
        // 如果JS端监听了下载进度,则client加入ProgressResponseBody对返回结果拦截
        if (useIncrementalUpdates) {
          clientBuilder.addNetworkInterceptor(new Interceptor() {
            @Override
            public Response intercept(Interceptor.Chain chain) throws IOException {
              Response originalResponse = chain.proceed(chain.request());
              ProgressResponseBody responseBody = new ProgressResponseBody(
                originalResponse.body(),
                new ProgressListener() {
                  long last = System.nanoTime();
    
                  @Override
                  public void onProgress(long bytesWritten, long contentLength, boolean done) {
                    long now = System.nanoTime();
                    if (!done && !shouldDispatch(now, last)) {
                      return;
                    }
                    if (responseType.equals("text")) {
                      // For 'text' responses we continuously send response data with progress info to
                      // JS below, so no need to do anything here.
                      return;
                    }
                    ResponseUtil.onDataReceivedProgress(
                      eventEmitter,
                      requestId,
                      bytesWritten,
                      contentLength);
                    last = now;
                  }
                });
              return originalResponse.newBuilder().body(responseBody).build();
            }
          });
        }
    
        // 以调用时的timeout为准
        if (timeout != mClient.connectTimeoutMillis()) {
          clientBuilder.readTimeout(timeout, TimeUnit.MILLISECONDS);
        }
        OkHttpClient client = clientBuilder.build();
        // 构建Header
        Headers requestHeaders = extractHeaders(headers, data);
        if (requestHeaders == null) {
          ResponseUtil.onRequestError(eventEmitter, requestId, "Unrecognized headers format", null);
          return;
        }
        String contentType = requestHeaders.get(CONTENT_TYPE_HEADER_NAME);
        String contentEncoding = requestHeaders.get(CONTENT_ENCODING_HEADER_NAME);
        requestBuilder.headers(requestHeaders);
         // 构建body
        if (data == null) {
          requestBuilder.method(method, RequestBodyUtil.getEmptyBody(method));
        } else if (data.hasKey(REQUEST_BODY_KEY_STRING)) {
          if (contentType == null) {
            ResponseUtil.onRequestError(
              eventEmitter,
              requestId,
              "Payload is set but no content-type header specified",
              null);
            return;
          }
          String body = data.getString(REQUEST_BODY_KEY_STRING);
          MediaType contentMediaType = MediaType.parse(contentType);
          if (RequestBodyUtil.isGzipEncoding(contentEncoding)) {
            RequestBody requestBody = RequestBodyUtil.createGzip(contentMediaType, body);
            if (requestBody == null) {
              ResponseUtil.onRequestError(eventEmitter, requestId, "Failed to gzip request body", null);
              return;
            }
            requestBuilder.method(method, requestBody);
          } else {
            requestBuilder.method(method, RequestBody.create(contentMediaType, body));
          }
        } else if (data.hasKey(REQUEST_BODY_KEY_BASE64)) {
          if (contentType == null) {
            ResponseUtil.onRequestError(
              eventEmitter,
              requestId,
              "Payload is set but no content-type header specified",
              null);
            return;
          }
          String base64String = data.getString(REQUEST_BODY_KEY_BASE64);
          MediaType contentMediaType = MediaType.parse(contentType);
          requestBuilder.method(
            method,
            RequestBody.create(contentMediaType, ByteString.decodeBase64(base64String)));
        } else if (data.hasKey(REQUEST_BODY_KEY_URI)) {
          if (contentType == null) {
            ResponseUtil.onRequestError(
              eventEmitter,
              requestId,
              "Payload is set but no content-type header specified",
              null);
            return;
          }
          String uri = data.getString(REQUEST_BODY_KEY_URI);
          InputStream fileInputStream =
              RequestBodyUtil.getFileInputStream(getReactApplicationContext(), uri);
          if (fileInputStream == null) {
            ResponseUtil.onRequestError(
              eventEmitter,
              requestId,
              "Could not retrieve file for uri " + uri,
              null);
            return;
          }
          requestBuilder.method(
              method,
              RequestBodyUtil.create(MediaType.parse(contentType), fileInputStream));
        } else if (data.hasKey(REQUEST_BODY_KEY_FORMDATA)) {
          if (contentType == null) {
            contentType = "multipart/form-data";
          }
          ReadableArray parts = data.getArray(REQUEST_BODY_KEY_FORMDATA);
          MultipartBody.Builder multipartBuilder =
              constructMultipartBody(executorToken, parts, contentType, requestId);
          if (multipartBuilder == null) {
            return;
          }
    
          requestBuilder.method(
            method,
            RequestBodyUtil.createProgressRequest(
              multipartBuilder.build(),
              new ProgressListener() {
            long last = System.nanoTime();
    
            @Override
            public void onProgress(long bytesWritten, long contentLength, boolean done) {
              long now = System.nanoTime();
              if (done || shouldDispatch(now, last)) {
                ResponseUtil.onDataSend(eventEmitter, requestId, bytesWritten, contentLength);
                last = now;
              }
            }
          }));
        } else {
          // Nothing in data payload, at least nothing we could understand anyway.
          requestBuilder.method(method, RequestBodyUtil.getEmptyBody(method));
        }
        // 开始请求
        addRequest(requestId);
        client.newCall(requestBuilder.build()).enqueue(
            new Callback() {
              @Override
              public void onFailure(Call call, IOException e) {
                if (mShuttingDown) {
                  return;
                }
                removeRequest(requestId);
                ResponseUtil.onRequestError(eventEmitter, requestId, e.getMessage(), e);
              }
    
              @Override
              public void onResponse(Call call, Response response) throws IOException {
                if (mShuttingDown) {
                  return;
                }
                removeRequest(requestId);
                // Before we touch the body send headers to JS
                ResponseUtil.onResponseReceived(
                  eventEmitter,
                  requestId,
                  response.code(),
                  translateHeaders(response.headers()),
                  response.request().url().toString());
    
                ResponseBody responseBody = response.body();
                try {
                  // If JS wants progress updates during the download, and it requested a text response,
                  // periodically send response data updates to JS.
                  if (useIncrementalUpdates && responseType.equals("text")) {
                    readWithProgress(eventEmitter, requestId, responseBody);
                    ResponseUtil.onRequestSuccess(eventEmitter, requestId);
                    return;
                  }
    
                  // Otherwise send the data in one big chunk, in the format that JS requested.
                  String responseString = "";
                  if (responseType.equals("text")) {
                    responseString = responseBody.string();
                  } else if (responseType.equals("base64")) {
                    responseString = Base64.encodeToString(responseBody.bytes(), Base64.NO_WRAP);
                  }
                  ResponseUtil.onDataReceived(eventEmitter, requestId, responseString);
                  ResponseUtil.onRequestSuccess(eventEmitter, requestId);
                } catch (IOException e) {
                  ResponseUtil.onRequestError(eventEmitter, requestId, e.getMessage(), e);
                }
              }
            });
      }
    

    请求结果返回后,Native通过RCTDeviceEventEmitter将结果传递给JS端:

    public static void onDataReceived(
        RCTDeviceEventEmitter eventEmitter,
        int requestId,
        String data) {
        WritableArray args = Arguments.createArray();
        args.pushInt(requestId);
        args.pushString(data);
    
        eventEmitter.emit("didReceiveNetworkData", args);
      }
    

    在JS端进行监听即可获取到请求结果。同样,JS为了简化业务代码,使用RCTNetworking进行了进一步封装。

    相关文章

      网友评论

          本文标题:Native API 之 NetworkingModule

          本文链接:https://www.haomeiwen.com/subject/aakgfxtx.html