androidwebview文件上传
⑴ 把HTML5的手机网站嵌入安卓APP中,发现<input type='file' />的上传按钮功能失效,点击没有任何反应
安卓APP的WebView默认屏蔽了该控件的使用,有些安卓APP之所以能支持文件选择和上传,主要可能是有可能采取了如下措施之一:
(1)可能在该APP中重写了相关方法(http://stackoverflow.com/questions/5907369/file-upload-in-webview)
(2)提供了JS Bridge来供web页面调用
所以,单从前端技术上是解决不了这个问题的。
这个问题之所以不太好排查,一方面跟手机端的图片上传功能我不太常用到有很大关系,另一方面也因为见到一些其它的APP容器能支持该功能,所以思维在习惯上已经形成定势,误以为这是WebView默认就支持的。
末了,顺便提醒一句,下次进行类似功能的需求评审,别忘了先了解下相应的APP容器是否支持该功能,如果没有,则需要找到相应的客户端开发同学评估一下工时,以免造成影响项目进度的风险
⑵ Android webview调取安卓原生相机和相册上传图片
适配安卓10方式:
返回URI即可
手机上加载webview,网页上上传图片调用原生相机和相册上传图片
先设置好webview的加载以及websetting,这里就不多说了。
主要是setWebChromeClient方法的实现
这个方法分几个版本的适配3.0以下的设备,3.0到4.1的设备,4.1到5.0的设备,以及5.0以上的设备,每个方法参数不一样适配时请注意。5.0以下的参数都是 ValueCallback<Uri> ,以上的设备需要 ValueCallback<Uri[]> ,回传数据的时候也是对应的不要弄错了。
最后的效果如下:
⑶ Android 5.x 免 Root 升级系统 WebView
Android 的系统碎片化问题可以说是 Android 系统最大的硬伤了,自这个系统诞生以来十几年过去了,依然没能很好的解决,碎片化问题也是每个 Android 开发工程师心中的隐痛😂,每次处理系统碎片化带来的问题时,血压也能分分钟飙升到 200+,为了减轻其他同仁的痛苦以及此后再遇到类似问题能少踩几个坑,就之前的爬坑经历做个记录吧。
有关 WebieView 的重要性和其使用不是本文的重点,但是有几个相关的属性我们必须了解:
在Android4.4(API level 19)系统以前,Android使用了原生自带的Android Webkit内核,这个内核对HTML5的支持不是很好,现在使用4.4以下机子的也不多了,就不对这个内核做过多介绍了,有兴趣可以看下 这辩丛闷篇文章 。
从携弯Android4.4系统开始,Chromium内核取代了Webkit内核,正式地接管了WebView的渲染工作。Chromium是一个开源的浏览器内核项目,基于Chromium开源项目修改实现的浏览器非常多,包括最着名的Chrome浏览器,以及一众国内浏览器(360浏览器、QQ浏览器等)。其中Chromium在Android上面的实现是 Android System WebView ^1 。
从Android5.0系统开始,WebView移植成了一个独立的apk,可以不依赖系统而独立存在和更新,我们可以在 系统->设置->Android System WebView 看到WebView的当前版本。
从Android7.0系统开始,如果系统安装了Chrome (version>51),那么Chrome将会直接为应用的WebView提供渲染,WebView版本会随着Chrome的更新而更新,用户也可以选择WebView的服务提供方(在开发者选项->WebView Implementation里),WebView可以脱离应用,在一个独立的沙盒进程中渲染页面(需要在开发者选项里打开) ^2 。
从Android8.0系统开始,默认开启WebView多进程模式,即WebView运行在独立的沙盒进程中 ^3 。―― 节选自 如何设计一个优雅健壮的Android WebView?(上)
WebView 的包名在 AOSP 中的值是 com.android.webview ,该值是在 AOSP 构建过程中编译的版本,也就是说它是和系统一起被编译出来的,由于大部分的第三方手机制造商都有自己的定制 ROM,所以包名也是不尽相同,比如 MIUI ROM 中它已经被改为 com.mi.webkit.core 。从 WebView 的版本历史中可以看到自 Android 5.0 开始 WebView 移植成了一个独立的 apk,可以不依赖系统而独立存在和更新,这时候起 WebView 的包名就正式改为 com.google.android.webview 了。
所以这就是郑核为什么网上一堆人问为啥升级了一下系统 WebView ,App 内使用到 WebView 的地方或者是内置浏览器一碰就报 PackageManager$NameNotFoundException: com.google.android.webview 或者 PackageManager$NameNotFoundException: com.android.webview 之类的错误,这些问题在 Android 5.0 的机器上非常常见,因为你升级了 WebView 之后 TMD 包名都变了🤣,而 ROM 定制商一般在版本衔接时都很保守,所以即使系统升到了 Android 5.0 ,解决方案未必就是最新的,内置的 WebView 依然可能是硬编码进 ROM 的,所以系统环境引用的包名可能依旧是 com.android.webview ,你升级到 com.google.android.webview 它当然不认识了😂。
通过上面一通废话,你应该知道了,替换的坑就在如果你换上去的 WebView 包名和原内置的 WebView 包名不一致,就无法使用,所以就需要找一个包名一致的高版本 APK 了,还有一种方法是在系统目录某个配置文件里改个什么值,也就是包名引用,这样你就能换成任何包名的 APK 了,这个暂时没仔细研究,后续有结果了再更新。
APKMirror 是一个 APK 镜像站点,在这里可以找到很多 APK 的 release 版本以及历史版本,尤其 Google 全家桶系列的 APK 非常全,我们在这里根据需求直接搜索包名就可以了,我这里需要 com.android.webview ,检索到如下结果,可以看到这些都是从第三方 ROM 里提取出来的。
因为 Google 官方早在 WebView 40 的时候就已经将包名换成 com.google.android.webview 了,最新的是 72.xxx ,我翻了 15 页才找到最早改名并独立出来的那个版本😂。
adb connect 192.168.18.235
adb shell
su
mount -o rw,remount /system
这里原目录下的文件分别有 /webview/webview.apk 和 /webview/lib/arm/libwebviewchromium.so ,备份原目录:
cd /system/app
mv webview/ webview-b/
mkdir -p webview/lib/arm/
这里很简单,文件后缀 .apk 直接改成 .zip 然后解压缩,复制出 libwebviewchromium.so 即可
先上传文件到设备 /sdcard ,然后执行如下命令移动过去,和原路径以及原文件名保持一致即可。
mv /sdcard/xxx.apk /system/app/webview/webview.apk
mv /sdcard/xxx.so /system/app/webview/lib/arm/libwebviewchromium.so
cd /system/app/
chmod 777 webview/*
adb reboot
如上一顿操作,其实也没什么难度,主要的坑就是包名一致的问题,还有一些系统目录访问权限之类的问题,之前网上搜了好多,都说不 root 没法换,或者换了会出问题,root 权限其实就是为了访问和写入系统目录,通过重新挂载就解决了,换了会崩掉的问题其实就是历史遗留问题,从 4.x 过度到 5.0 WebView 独立了,所以包名变了,或者是 ROM 定制方不按套路来导致换上去的 WebView 不被系统识别,只要找到合适的包就解决了。
⑷ android的本地视频,如何远程可以观看
android 端用WEBVIEW可以做 ,WEBVIEW可以加载你服务器上的网站,里面上传视频,客户端可以直接播放网络视频,当然也可以下载下来播放,就是用输入输出流做。可以选择用HTTP协议访问网站资源,也可以用socket
⑸ android 怎么在webview上添加照片
webview上添加照片的话,是需要使用js,生成一个html界面,里面写上图片就可以
⑹ webView文件上传取消弹窗后再次点击不响应点击事件问题
移动端H5页面调用手机相册或者照相机后,再次点击没反应问题,这是需要安卓方面修改的,前端不需要修改。
实现方法是
webView_customer.setWebChromeClient(new WebChromeClient() {
// For Android 3.0+
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
ArrayList list = new ArrayList();
list.add("拍照");
list.add("从相册选择");
showTakePicOrPhotoesDialog(Activity_Customer_E.this, "选择", list, new DialogUtils.OnClickPicDialogItemListener() {
@Override
public void onClickItem(int which) {
if(0==which){
Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//下面这句指定调用相机拍照后的照片存储的路径
try {
File file = 销春createImageFile();
takePicUri = Uri.fromFile(file);
takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, takePicUri);
startActivityForResult(takeIntent, FILECHOOSER_RESULTCODE);
} catch (IOException e) {
e.printStackTrace();
}
}else if(1==which){
Intent pickIntent = new Intent(Intent.ACTION_PICK, null); pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(pickIntent, FILECHOOSER_RESULTCODE);
}
}
});
}
// For Android < 3.0
public void openFileChooser(ValueCallback uploadMsg) {
openFileChooser(uploadMsg, "");
}
/亏绝耐/ For Android > 4.1.1
public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) {
openFileChooser(uploadMsg, acceptType);
}
// For Android 5.0+
public boolean onShowFileChooser (WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
mUploadCallbackAboveL = filePathCallback;
ArrayList list = new ArrayList();
list.add("拍照");
list.add("从相册选择");
showTakePicOrPhotoesDialog(Activity_Customer_E.this, "选择", list, new DialogUtils.OnClickPicDialogItemListener() {
@Override
public void onClickItem(int which) {
if(0==which){
Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//下宏判面这句指定调用相机拍照后的照片存储的路径
try {
File file = createImageFile();
takePicUri = Uri.fromFile(file);
takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, takePicUri);
startActivityForResult(takeIntent, FILECHOOSER_RESULTCODE);
} catch (IOException e) {
e.printStackTrace();
}
}else if(1==which){
Intent pickIntent = new Intent(Intent.ACTION_PICK, null);
pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
startActivityForResult(pickIntent, FILECHOOSER_RESULTCODE);
}
}
});
return true;
}
});
OnActivityResult回调方法:这里有个特别注意的地方,当打开拍照按钮时,Intent data这个数据是空,所以不能正常上传,打开拍照的Intent当时传了一个Uri,在OnActivityResult需要判断data是不是空,是空的话,就把传的那个Uri赋值给mUploadMessage.onReceiveValue,如果不为空就传data.getData();具体如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode==FILECHOOSER_RESULTCODE){
Uri result;
if (null == mUploadMessage && null == mUploadCallbackAboveL){
return;
}
//如果拍完照点击取消按钮就:if(resultCode ==RESULT_CANCELED){ result = Uri.EMPTY;}else{if(data ==null&& resultCode !=RESULT_OK){ result = Uri.EMPTY; }else if(data ==null){ result =takePicUri; }else{ result = data.getData(); }}
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
if(mUploadCallbackAboveL!=null) {mUploadCallbackAboveL.onReceiveValue(newUri[]{result});mUploadCallbackAboveL=null;}
}
}
这里有一个问题,点击“取消”或返回按钮,无法重复回调onShowFileChooser或openFileChooser方法。解决办法:
定义一个方法:
/**
*防止点击dialog的取消按钮之后,就不再次响应点击事件了
*/
public static void cancelCallback(){
if(mUploadCallbackAboveL!=null){
mUploadCallbackAboveL.onReceiveValue(null);
}
if(mUploadMessage!=null){
mUploadMessage.onReceiveValue(null);
}
}
在弹窗(dialog)的取消按钮监听的方法中调用上面的cancellCallBack方法。比如我的:
/**
*展示拍照或者从相册选择的Dialog
* @param context
* @param title
* @param names
* @param onClickPicDialogItemListener
*/
public static void showTakePicOrPhotoesDialog(Context context, String title, final ArrayList names, final DialogUtils.OnClickPicDialogItemListener onClickPicDialogItemListener){
AlertDialog.Builder builder=new AlertDialog.Builder(context);
builder.setTitle(title);//设置标题
String[] itemList=(String[]) names.toArray(new String[names.size()]);
builder.setItems(itemList, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onClickPicDialogItemListener.onClickItem(which);
}
});
AlertDialog dialog=builder.create();//获取dialog
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancelCallback();
}
});
dialog.show();//显示对话框
}
⑺ android webview 怎样加载本地css
第一步:将资源拷贝到assets目录
第二步:给需要注入的资源文件在url上做一个标志,当然,你也可以不做,只要你在安卓端可以判断出来即可。
<link rel="stylesheet" href="[inject]public/css/bootstrap.min.css" type="text/css" media="screen" title="no title" charset="utf-8">
<link rel="stylesheet" href="[inject]public/css/bootstrap-theme.min.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script type="text/javascript" src="[inject]public/js/zipto/1.1.6/zepto.min.js"></script>
<script type="text/javascript" src="[inject]public/js/moles/md5.min.js"></script>
第三步:拦截将要注入的文件,读取本地文件即可。
webview.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
//System.out.println("WebResourceResponse::"+url);
if(url.contains("[inject]")){
String localPath = url.replaceFirst("^http.*inject\\]","");
System.out.println("LocalPath::"+localPath);
try {
InputStream is = getApplicationContext().getAssets().open(localPath);
return new WebResourceResponse("text/javascript", "UTF-8", is);
} catch (Exception e) {
e.printStackTrace();
return super.shouldInterceptRequest(view, url);
}
} else {
return super.shouldInterceptRequest(view, url);
}
}
});
⑻ webview触摸屏上传程序失败
网络问念慎题。Android原生的WebView并不支持上传文件,但webview触摸屏上传程序是使用vantui进行和纯上传的,上传程序失败大多数的原唤高咐因数网络的问题,上传程序对于网络的要求很高。
⑼ Android webview 上传图片为什么 application/octet-stream
首先得找出问题的原因,你不妨做以下测试,看下问题是处在那端
用其他的软件测试Server看是不是服务器配置的事
你写的Webview,Mainfest赋权是否正确
⑽ WebView中的文件选择
参考:
Android使用WebView加载网页选择文件上传