直接切入主題了
最近公司專案裡線上使用者反饋出一個bug,介入的第三方平臺中有個新增圖片的功能,當點選H5中的按鈕的時候,調不起本地檔案管理器,在很多手機上都出現這種情況
原因如下
刨除定製化的webview來說,原生webview是支援上傳檔案的。但是眾多版本的迭代擴充套件,api引數也不一樣。一般拿到上傳檔案的需求時,大家都會照搬android brower的程式碼(聰明),api如下:
// Android > 4.1.1 呼叫這個方法
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
LogUtils.d("openFileChooser 4.1.1 = ");
}
// 3.0 + 呼叫這個方法
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
LogUtils.d("openFileChooser 3.0 = ");
}
// Android < 3.0 呼叫這個方法
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
LogUtils.d("openFileChooser < 3.0 = ");
}複製程式碼
之前專案裡的程式碼當然也是這樣寫的,後來還懷疑過是否需要和前端聯調,後來查了一下發現了第一個坑:
5.0之後,系統提供了onShowFileChooser來讓我們實現選擇檔案的方法。
看了Sam的文章知道了這點:Sam
之後,本以為沒有問題了,測試的時候發現,坑又來了。
我們可以看出,在這個功能上,有很明顯的api升級痕跡,在試過眾多手機之後,發現,4.4.0到4.4.2的系統無法呼叫這個api,當然也無從上傳檔案了,是怎麼回事?
在這篇部落格找到了詳細的答案:穿衣助手技術部落格
原來google在4.4更改webkit核心為chromium之後,把這個api刪除了!一般升級api,都會保留對原有api的支援,@deprecated掉舊的api,加入新的api,但是google卻2個都沒做,開發團隊解釋,我們正在開發一個新的共有的api,這個api會更好,我們會在正式版本上推出。結果呢,在4.4.3版本,他又把這個api加回來了
於是怎麼解決呢,網路上大多都放棄或是沒有解決方案了,太過於麻煩,有這麼三個方式:
參考這個,不過並沒有給出具體的程式碼,只有思路,並且只有第三個思路是較靠譜的
第一種,H5直接使用新的video標籤通過獲取navigator的getUserMedia來獲取視訊流stream中擷取一張圖的方式來實現,不過這種方式在mobile上的支援比較晚。其中,android是從Android5.0才開始支援,ios未知。所以這種實現思路不做考慮。
第二種,H5直接用以前Html舊有的input標籤來實現。其中這種方式在ios支援上還不錯,在android上的支援則不怎麼令人滿意。因為它直接涉及到了webkit在android各個平臺不同的實現方式,有很大的風險性。但是,出於方便讓ios能直接呼叫的原因這個方式的可行性還是很大的。當然,問題並非不能解決,這個下面再講。
第三種,H5直接利用js和本地進行互動來實現。這種方式的採用很成功的例子就是微信公眾賬號的實現,它通過實現一套js庫來支援網頁的各種呼叫
關於第三種方式的解決方案如下:
用js呼叫的本地方法如下:
final class ModuleJavaScriptInterface {
ModuleJavaScriptInterface() {
}
@JavascriptInterface
public void finishWebview(String json) {
if (!TextUtils.isEmpty(json)) {
Toast.makeText(BrowserActivity.this, "json= " + json, Toast.LENGTH_SHORT).show();
}
}
@JavascriptInterface
public void uploadImage() {
Toast.makeText(BrowserActivity.this, "upload ", Toast.LENGTH_SHORT).show();
//開啟相簿
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), KITKAT_RESULTCODE);
/*
在onActivityResult中 KITKAT_RESULTCODE
1 上傳圖片
2 上傳成功後 將伺服器返回的URL 返回給 js : UploadedFileName
String UploadedFileName = "";
mWebView.loadUrl("javascript:CheckImage('" + UploadedFileName + "')");
*/
}
}複製程式碼
當然還有一個小坑
之前專案裡面已經處理好了,所以並沒有顯現出來,如下:
呼叫系統app選擇檔案的時候,若彈出選擇框,cancel掉選擇框之後,發現webview無響應了,無法重新整理,載入,點選,甚至退出這個activity也無法載入!後果很嚴重~
原因是當你選擇上傳檔案的時候,webview的ValueCallback物件(就是選擇圖片的回撥)會持有這個webview,在沒有收到回撥之前,你無法對這個webview做任何的操作!
知道原因之後,就很好解決了,如果cancel了,那麼直接呼叫該物件的onReceiveValue()方法,傳入null即可,webview就可以正常操作了
最後別忘了取消混淆的問題呦
最後給出剩下的程式碼事例:
public class SafeWebViewClient extends WebViewClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
activity.mWebLoadingProgressBar.setProgress(newProgress);
if (newProgress >= 90 && activity.mWebLoadingProgressBar.getVisibility() == View.VISIBLE) {
activity.mWebLoadingProgressBar.setVisibility(View.GONE);
}
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
activity.mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), activity.FILECHOOSER_RESULTCODE);
}
// For Android 3.0+
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
activity.mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
activity.startActivityForResult(Intent.createChooser(i, "File Browser"), activity.FILECHOOSER_RESULTCODE);
}
//For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
activity.mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
activity.startActivityForResult(Intent.createChooser(i, "File Chooser"), activity.FILECHOOSER_RESULTCODE);
}
//For Android 5.0
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
// make sure there is no existing message
if (activity.uploadMessage != null) {
activity.uploadMessage.onReceiveValue(null);
activity.uploadMessage = null;
}
activity.uploadMessage = filePathCallback;
Intent intent = fileChooserParams.createIntent();
try {
activity.startActivityForResult(intent, activity.REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e) {
activity.uploadMessage = null;
return false;
}
return true;
}
}複製程式碼
選中圖片之後走:
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage) return;
Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null) return;
uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
uploadMessage = null;
}複製程式碼
Thanks && END