WebView的坑

webview在Android中主要用作展示URL的页面,但是其中有很多的坑,因为做一个签协议的功能遇到很多的问题,现在记录下来

1、在页面中下载文件

下载文件需要webview.setDownloadListener(),然后实现onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength)的方法
我使用的是唤起外部浏览器去下载,这种方法最省事儿,也可以使用内部的各种下载工具实现
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
    try {
        if (!TextUtils.isEmpty(url)) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.addCategory(Intent.CATEGORY_BROWSABLE);
            intent.setData(Uri.parse(url));
            startActivity(intent);
        }

    } catch (Exception e) {

    }
}

2、在webview中打开本地的图库或者相机

打开本地的图库和相机主要是要在网页内上传图片,之前没做过不知道怎么实现后来看网上其他人的实现方法,主要是继承WebChromeClient然后重写onShowFileChooser(
WebViewwebView,ValueCallback<Uri[]>filePathCallback, FileChooserParams fileChooserParams),当然还有低版本的几个方法openFileChooser()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
1. 首先需要定义两个全局变量(因为5.0(>=)之后的上传方式和5.0之前的上传方式不同,5.0之前是上传单个的uri,5.0之后上传的是uri数组)
private ValueCallback<Uri> uploadFile;//5.0以前
private ValueCallback<Uri[]> uploadFiles;//5.0以后
private Uri imageUri;//拍照的后的uri
2.继承WebChromeClient类,重写openFileChooser()和onShowFileChooser()方法
private class MyWebChromeClient extends WebChromeClient{
// For Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
Log.i("test", "openFileChooser 1");
uploadFile = uploadMsg;
openFileChooseProcess();
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsgs) {
Log.i("test", "openFileChooser 2");
uploadFile = uploadMsgs;
openFileChooseProcess();
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
Log.i("test", "openFileChooser 3");
uploadFile = uploadMsg;
openFileChooseProcess();
}
// For Android >= 5.0
public boolean onShowFileChooser(com.tencent.smtt.sdk.WebView webView,
ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
Log.i("test", "openFileChooser 4:" + filePathCallback.toString());
uploadFiles = filePathCallback;
openFileChooseProcess();
return true;
}
}
有一个重点的问题就是onShowFileChooser()方法的返回值,如果返回true代表已经唤起文件选择器(如果不上传图片,下次就能唤起了),如果返回false代表没有唤起文件选择器(如果没选择图片,之后还可以唤起),这里的true和false主要是考虑到申请权限的问题
private boolean getPermission(){
if(没有权限){//主要是照相和存储卡权限 >6.0需要考虑
获取权限
return false;
}else{
openFileChooser();
return true;
}
}
因此onShowFileChooser(XXX)方法需要修改一下
onShowFileChooser(xxx){
return getPermission();
}
3.设置唤起文件选择器,这个其实是利用Intent来唤起的,目前唤起相机和文件选择
private void openFileChooser(){
Properties properties = BaseConfig.getInstance().getConfig();
String storePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + properties.getProperty("imagepath");
File imageStorageDir = new File(storePath);
// Create the storage directory if it does not exist
if (!imageStorageDir.exists()) {
imageStorageDir.mkdirs();
}
File file = new File(imageStorageDir + File.separator + "IMG_" + String.valueOf(System.currentTimeMillis()) + ".jpg");
imageUri = Uri.fromFile(file);
List<Intent> cameraIntents = new ArrayList<Intent>();
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
PackageManager packageManager = getPackageManager();
List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
if (listCam == null) {
return false;
}
for (ResolveInfo res : listCam) {
String packageName = res.activityInfo.packageName;
Intent i = new Intent(captureIntent);
i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
i.setPackage(packageName);
i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
cameraIntents.add(i);
}
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
Intent chooserIntent = Intent.createChooser(i, "选择");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
}
4.最后一步通过获取到的uri进行上传
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case 0:
if (null != uploadFile) {
Uri result = data == null || resultCode != RESULT_OK ? null
: data.getData();
if(result == null && StringUtil.isNotNull(getRealFilePath(mContext,imageUri))){//主要用来判断是否有相机拍摄的照片
File file = new File(getRealFilePath(mContext,imageUri))
if(file.isFile())
result = imageUri;
}
uploadFile.onReceiveValue(result);
uploadFile = null;
}
if (null != uploadFiles) {
Uri result = data == null || resultCode != RESULT_OK ? null: data.getData();
if(result == null && StringUtil.isNotNull(getRealFilePath(mContext,imageUri))){
File file = new File(getRealFilePath(mContext,imageUri))
if(file.isFile())
result = imageUri;
}
uploadFiles.onReceiveValue(new Uri[]{result});
uploadFiles = null;
}
break;
default:
break;
}
} else if (resultCode == RESULT_CANCELED) {
if (null != uploadFile) {
uploadFile.onReceiveValue(null);
uploadFile = null;
}
}
}
/**
*@description 获取uri的真正路径
*/
public static String getRealFilePath(final Context context, final Uri uri) {
if (null == uri) return null;
try {
final String scheme = uri.getScheme();
String data = null;
if (scheme == null)
data = uri.getPath();
else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
return data;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
此时上传功能已经完成了,但是uploadFile即5.0以下的系统如果传了uri,uri没有文件的话可能会刷新出空图片(在华为4.4.2上见过这个问题),所以如果没有传文件就传null

3、在模拟器上测试Webview的功能

在模拟器的4.4.2的版本上测试WebView唤起图库和相机,一直不成功,一直以为是代码的问题,后来用真机测试完全没问题

4、低版本和高版本中适配HTTP和HTTPS

在低版本4.4.2上的webView还有一个奇怪的问题就是不图片不展示,或者不能监听到onDownloadStart(xxx),后来发现原因在于使用了HTTPS的文件路径引起的,如果全部
替换为HTTP就不会有问题了。

5、申请权限的问题(主要是onShowFileChooser(xx)方法的返回值true or false,高版本的问题)

申请6.0以上的权限目前用原生的或者用EasyPermission,一般用EasyPermission比较好,调用比较方便。主要想记录下onShowFileChooser(xxx)这个方法的使用问题

6、标签的问题(rotate)

标签适配要把HTML的标签适配写全,如果没把标签写全的话可能存在不适配的问题,不能只写rotate。

7、画布闪烁的问题

在4.4.2的华为荣耀3x畅玩版上(已root)存在签名画布闪烁的问题,没发现原因,但是不排除root的原因,因为在相同4.4.2版本华为荣耀6plus不存在这个问题
坚持原创技术分享,您的支持将鼓励我继续创作!