1. 程式人生 > >Android WebView上傳圖片(base64)到H5(JS)

Android WebView上傳圖片(base64)到H5(JS)

最近專案需求是H5呼叫安卓的方法選擇圖片或者開啟照相機拍照,然後傳給H5顯示圖片,最後由H5上傳到伺服器。查了一下資料,大概有以下幾種方法:

  1. 利用WebChromeClient的openFileChooser(5.0+是onShowFileChooser)
  2. JS呼叫安卓端定義好的介面選擇圖片或者開啟照相機,獲取到圖片資訊(base64)之後,安卓端呼叫JS方法回撥給H5
  3. JS呼叫安卓端定義好的介面選擇圖片或者開啟照相機,安卓端上傳圖片給伺服器,伺服器返回圖片的URL,最後安卓端呼叫JS方法把圖片的URL傳給H5

先來比較一下這三種方法:

第一種方法,看上去應該很好用,畢竟是系統原生方法,但是隨著安卓系統版本的不斷更新,WebChromeClient的openFileChooser方法返回的引數都有所改變,到了5.0+更是換了個名字,這都不是重點,重點是出了4.4這個奇葩,居然不支援openFileChooser,所以這個方法要做適配和解決4.4這個奇葩才可行。嫌麻煩的我怎麼可能選這個方法呢(´Д`)解決方案可以參考以下兩個連結的內容:

http://stackoverflow.com/questions/5907369/file-upload-in-webview/ http://blog.csdn.net/mengzuixilou/article/details/48374971

第二種方法,是我最後採用了的方法,可能是因為我比較二,所以喜歡第二種方法吧。囧rz 主要的問題都是跟H5之間的互動,不過還是遇到部分機型傳遞不到資料的問題(圖片流太大),後面將會詳細講解我最後的方案。

第三種方法,簡單粗暴而且可行性很高,因為上傳圖片到伺服器的方法已經很成熟了,所以基本不必擔心傳不到圖片對應的URL給H5,而且傳給H5的資料量少。但是,有個缺點就是要展示的圖片不一定是要上傳到伺服器的,而是使用者點選了“上傳”按鈕才會上傳到伺服器。所以,這個方法看起來有點彆扭,最後還是沒有用到這個方案。

下面將詳細講解第二種方法 <( ̄ ﹌  ̄)@m

首先,設定WebView使其支援JS呼叫JAVA方法

private OperationPresenter operationPresenter = new OperationPresenter(this);
private WebView mWebView;

//設定WebView支援JS
mWebView.getSettings().setJavaScriptEnabled(true);
//新增JS可呼叫的例項物件,第二個引數為JS可用的物件名
mWebView.addJavascriptInterface(operationPresenter, "operation"
)

其中OperationPresenter程式碼如下

public class OperationPresenter {

    private IOperationView view;
    private String methodName;

    public OperationPresenter(IOperationView view) {
        this.view = view;
    }

    /**
     * 選擇圖片
     *
     * @param methodName JS方法名
     */
    @JavascriptInterface
    public void choosePhoto(String methodName) {
        this.methodName = methodName;
        view.onChoosePhoto();
    }

    /**
     * 開啟照相機
     *
     * @param methodName JS方法名
     */
    @JavascriptInterface
    public void openCamera(String methodName) {
        this.methodName = methodName;
        view.onOpenCamera();
    }

    /**
     * 部分低端機可能會因為字串太長而不能直接傳base64
     * 需要把base64封裝到JSON物件中
     *
     * @param base64JSONObject 包含base64的JSON物件
     */
    public void sendPhoto(WebView webView, JSONObject base64JSONObject) {
        webView.loadUrl("javascript:" + methodName + "(" + base64JSONObject + ")");
    }

}

H5只需要在這樣呼叫即可

//選擇圖片
window.opreation.choosePhoto('getPhoto')
//開啟照相機
window.opreation.openCamera('getPhoto')

getPhoto就是JS定義的方法,需要安卓端回撥使用的,定義如下

function getPhoto(response) {
    //因為response是JSON物件,所以直接通過key(img)獲取value
    response.img
    //......後面省略
}

封裝JSONObject並回調

//獲取Bitmap並用自己封裝的工具類解釋成base64
String baseData = ImgUtils.bitmap2Base64(bitmap);
if (!TextUtils.isEmpty(baseData)) {
    JSONObject jsonObject = new JSONObject();
    try {
        jsonObject.put("img", "data:image/png;base64," + baseData);
        operationPresenter.sendPhoto(mWebView, jsonObject);
    } catch (JSONException e) {
        //TODO
    }
}

大致流程如下:
JS呼叫了安卓的方法開啟相簿/照相機–>獲取圖片–>壓縮–>轉成base64–>封裝成JSONObject–>呼叫JS方法回傳圖片資料
至於一些WebView的優化和設定、圖片壓縮、base64的轉換等等可以參考網上的其他資料。

再說明一下為什麼不直接傳base64字串,而是將它封裝成JSON物件。因為當在部分效能比較差的手機上面(4.0真機、4.2模擬器),把base64這樣長的字串直接傳給JS的話會出現像下面這樣的問題

Console: Uncaught SyntaxError: Unexpected token ILLEGAL at null:1

居然是一個我看不懂的JS錯誤,搜尋了一下基本都沒有範對應的錯(PS:還是我不夠細心看問題呢)後來無意間發現了這篇文章
http://blog.csdn.net/waww116529/article/details/52850018
主要原因就是圖片流太大,不過作者應該是寫錯了一點,傳JSON物件的時候,應該不用再加單引號或者雙引號了,我試過加了是沒反應的。像下面這樣就可以了。

webView.loadUrl("javascript:" + methodName + "(" + base64JSONObject + ")");

有更多更好的WebView上傳圖片到H5的方法請留言發連結,小弟不勝感激!