android native与webview 传递流数据(h5、webview、android、byte[]、blog) - 初版

上期文章:https:///post/7259272190637539386,感谢我们支持

背景

在androidx-Webkit,咱们假如需要传递一张图片在webview与native,能够通过两种办法来完成

  • 文件blog转换成base64,再由接纳端持续处理
  • 发送端先将图片上传至服务器,再将图片地址传递至接纳端,接纳端再将图片下载下来

比较于将图片直接发送到文件服务,然后传个链接给客户端,文件blog转换成base64传给客户端具有显着优势

  • 不占用服务资源
  • 速度快
  • 不需要服务端开发成本

但是在真实落地时才发却发生了一个问题,android内存溢出了,最后通过排查是base64的字符串长度太长了,其实实质也是图片太大,导致输出的base64格局的字符串也相应的超过了客户端的字符串最大长度。莫非咱们要抛弃这个方案么?明显不可!

探究直接传流: demo源码 解析

既然是图片太大了导致的,那么就改动思路,测验将图片分片传递,然后由native去逐片保存,那么问题来了,再通过九九一天的测验往后,总算完成了这项不太艰巨但是能够直爽摸鱼的使命。

流处理服务-UploadBlobServer

这个类首要做了两件作业,一个是将base64复原回byte[]对象,另外一件作业就是将文件保存到本地。

  // base64复原回byte[]
  import android.util.Base64;
  byte[]  buffer = Base64.decode(bufferBase64,Base64.DEFAULT);
   // 保存流到指定文件-android中需要创立一个作业线程
   private class  DownThread {
        private String cacheFilePath = null;
        private InputStream inputStream;
        public DownThread(String cacheFilePath) {
            this.cacheFilePath = cacheFilePath;
        }
        private RandomAccessFile fileOutputStream = null;
        public void write(byte[] buffer,int seekStart) throws IOException {
            File cacheFile = new File(this.cacheFilePath);
            if (!cacheFile.exists()) {
                cacheFile.createNewFile();
            }
            fileOutputStream = new RandomAccessFile(cacheFile,"rwd");
            inputStream = new ByteArrayInputStream(buffer);
            fileOutputStream.seek(seekStart);
            int length = -1;
            while ((length = inputStream.read(buffer)) != -1) {
                fileOutputStream.write(buffer,0,length);
            }
        }
        public File close() throws IOException {
            inputStream.close();
            fileOutputStream.close();
            return  new File(cacheFilePath);
        }
    }
文件传递控制器UploadBlobContainer

为了方便咱们去办理文件传递的过程而封装的这个类,它的首要作用是控制多文件、分片下载的过程

public class UploadBlobContainer {
    private static HashMap<String, UploadBlobServer> updateBlobHashMap =new HashMap<>();
    private static UploadBlobListener<UploadBlobBean> uploadBlobListener;
    public static void setUploadBlobListener(UploadBlobListener<UploadBlobBean> uploadBlobListener) {
        UploadBlobContainer.uploadBlobListener = uploadBlobListener;
    }
    /**
     * 获取仅有类
     * @param uploadBlobBean
     * @return
     */
    public static UploadBlobServer getUpdateBlob(UploadBlobBean uploadBlobBean){
        String fileName = uploadBlobBean.getFileName();
        if(updateBlobHashMap.containsKey(fileName)){
            return updateBlobHashMap.get(fileName);
        }
        // 初始化 流传输类
        UploadBlobServer updateBlob=new UploadBlobServer();
        // 事件一致输出
        updateBlob.setUploadBlobListener(new UploadBlobListener<String>() {
            @Override
            public void start(String fileName) {
                 if(UploadBlobContainer.uploadBlobListener!=null){
                     UploadBlobContainer.uploadBlobListener.start(uploadBlobBean);
                 }
            }
            @Override
            public void write(String fileName) {
                if(UploadBlobContainer.uploadBlobListener!=null){
                    UploadBlobContainer.uploadBlobListener.write(uploadBlobBean);
                }
            }
            @Override
            public void end(String fileName) {
                if(UploadBlobContainer.uploadBlobListener!=null){
                    UploadBlobContainer.uploadBlobListener.end(uploadBlobBean);
                }
                // 完成后从输出站内退出
                updateBlobHashMap.remove(fileName);
            }
        });
        updateBlobHashMap.put(fileName,updateBlob);
        return updateBlob;
    }
    public static  void upload(UploadBlobBean uploadBlobBean) {
        upload(uploadBlobBean,null);
    }
    public static  void upload(UploadBlobBean uploadBlobBean,UploadBlobListener<UploadBlobBean> uploadBlobListener) {
        setUploadBlobListener(uploadBlobListener);
        UploadBlobServer uploadBlobServer = getUpdateBlob(uploadBlobBean);
        switch (uploadBlobServer.status){
            case PADDING:
                uploadBlobServer.init(uploadBlobBean.getFileName(),uploadBlobBean.getContentLength());
            case WRITE:
                try {
                    uploadBlobServer.uploadFile(uploadBlobBean.getBody(),uploadBlobBean.getFileName());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}
js的完成

最首要的功用是

  • 转化流为byte[]格局
  • byte[]分片
  • byte[]转base64
// 转化流为byte[]格局
function getUint8ArrayBlob (blobStr) {
  const byteString = atob(blobStr.split(',')[1])
  const ab = new ArrayBuffer(byteString.length)
  const blob = new Uint8Array(ab)
  for (let i = 0; i < byteString.length; i++) {
    blob[i] = byteString.charCodeAt(i)
  }
  return blob
}
// byte[]分片
function sliceUint8Array (byteArray) {
  const blocks = []
  let length = byteArray.length
  console.log('getFile-h5-length', length + '::=>' + length)
  while (length) {
    const startIdnex = byteArray.length - length
    const end = startIdnex + (length >= 65535 ? 65535 : length)
    const byteArrayBlock = byteArray.slice(startIdnex, end)
    length -= byteArrayBlock.length
    blocks.push(byteArrayBlock)
    console.log('getFile-h5-length', startIdnex + '::' + end)
  }
  return blocks
}

byte[]转化为base64格局,这儿这么处理阐明一下,为何不直接转成base64,因为测验过直接转化后,并不能复原,所以String.fromCharCode()将其再次处理后才能正常复原,有谁知道为什么么?

// byte[]转化为base64格局
function arrayBufferToBase64 (buffer) {
  let binary = ''
  const bytes = new Uint8Array(buffer)
  const len = bytes.byteLength
  for (let i = 0; i < len; i++) {
    binary += String.fromCharCode(bytes[i])
  }
  // window.btoa():将ascii字符串或二进制数据转换成一个base64编码过的字符串,该办法不能直接作用于Unicode字符串.
  return window.btoa(binary)
}

又能愉快的传图了

look、look一下子

题外话 轻松加愉快的androidx-Webkit

在androidx-Webkit之中,咱们就不用为传递文件的功用大为光火,因为androidx-Webkit现已支持了字节流传递的办法,直接秒杀上面的所有方案,以下是我转移的简绘Android同学的文章代码,文章链接在底端,希望我们同样支持一下。

native:

// App (in Java)
WebMessageListener myListener = new WebMessageListener() {
 @Override
 public void onPostMessage(WebView view, WebMessageCompat message, Uri sourceOrigin,
     boolean isMainFrame, JavaScriptReplyProxy replyProxy) {
  // Communication is setup, send file data to web.
  if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER)) {
   // Suppose readFileData method is to read content from file.
   byte[] fileData = readFileData("myFile.dat");
   replyProxy.postMessage(fileData);
  }
 }
}

js:

// Web page (in JavaScript)
myObject.onmessage = function(event) {
 if (event.data instanceof ArrayBuffer) {
  const data = event.data; // Received file content from app.
  const dataView = new DataView(data);
  // Consume file content by using JavaScript DataView to access ArrayBuffer.
 }
}
myObject.postMessage("Setup!");

补充native于webview交互:/post/708674…

参考文章

/post/725976…

项目地址:

Android端:github.com/runner-up-j…

h5端:github.com/runner-up-j…