最近公司項目裏線上用戶反饋出一個bug,介入的第三方平臺中有個添加圖片的功能,當點擊H5中的按鈕的時候,調不起本地文件管理器,在不少手機上都出現這種狀況javascript
刨除定製化的webview來講,原生webview是支持上傳文件的。可是衆多版本的迭代擴展,api參數也不同。通常拿到上傳文件的需求時,你們都會照搬android brower的代碼(聰明),api以下:html
// 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的文章知道了這點:Samjava
以後,本覺得沒有問題了,測試的時候發現,坑又來了。android
咱們能夠看出,在這個功能上,有很明顯的api升級痕跡,在試過衆多手機以後,發現,4.4.0到4.4.2的系統沒法調用這個api,固然也無從上傳文件了,是怎麼回事?ios
在這篇博客找到了詳細的答案:穿衣助手技術博客web
原來google在4.4更改webkit內核爲chromium以後,把這個api刪除了!通常升級api,都會保留對原有api的支持,@deprecated掉舊的api,加入新的api,可是google卻2個都沒作,開發團隊解釋,咱們正在開發一個新的共有的api,這個api會更好,咱們會在正式版本上推出。結果呢,在4.4.3版本,他又把這個api加回來了json
因而怎麼解決呢,網絡上大多都放棄或是沒有解決方案了,太過於麻煩,有這麼三個方式:
參考這個,不過並無給出具體的代碼,只有思路,而且只有第三個思路是較靠譜的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