1.Android5.0上WebView中Http和Https混合问题
从Android5.0以后,当一个安全的站点(https)去加载一个非安全的站点(http)时,需要配置Webview加载内容的混合模式,一共有如下三种模式:
- MIXED_CONTENT_NEVER_ALLOW:Webview不允许一个安全的站点(https)去加载非安全的站点内容(http),比如,https网页内容的图片是http链接。强烈建议App使用这种模式,因为这样更安全。
- MIXED_CONTENT_ALWAYS_ALLOW:在这种模式下,WebView是可以在一个安全的站点(Https)里加载非安全的站点内容(Http),这是WebView最不安全的操作模式,尽可能地不要使用这种模式。
- MIXED_CONTENT_COMPATIBILITY_MODE:在这种模式下,当涉及到混合式内容时,WebView会尝试去兼容最新Web浏览器的风格。一些不安全的内容(Http)能被加载到一个安全的站点上(Https),而其他类型的内容将会被阻塞。这些内容的类型是被允许加载还是被阻塞可能会随着版本的不同而改变,并没有明确的定义。这种模式主要用于在App里面不能控制内容的渲染,但是又希望在一个安全的环境下运行。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
2.无法打开系统相机和相册
一个完整示例
public class WebViewActivity extends AppCompatActivity {
@BindView(R.id.webView)
WebView webView;
private String mCM;
private final static int FCR = 1;
private ValueCallback<Uri> mUM;
private ValueCallback<Uri[]> mUMA;
/**
* 选择是否上传多个文件
*/
private boolean multipleFiles = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView.loadUrl("网址");
mWebView.setWebChromeClient(new MyWebChromeClient());
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (requestCode == FCR) {
if (null == mUMA) {
return;
}
if (intent == null || intent.getData() == null) {
if (mCM != null) {
results = new Uri[]{Uri.parse(mCM)};
}
} else {
String dataString = intent.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
} else {
if (multipleFiles) {
if (intent.getClipData() != null) {
final int numSelectedFiles = intent.getClipData().getItemCount();
results = new Uri[numSelectedFiles];
for (int i = 0; i < numSelectedFiles; i++) {
results[i] = intent.getClipData().getItemAt(i).getUri();
}
}
}
}
}
}
}
mUMA.onReceiveValue(results);
mUMA = null;
} else {
if (requestCode == FCR) {
if (null == mUM) {
return;
}
Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
mUM.onReceiveValue(result);
mUM = null;
}
}
}
public class MyWebChromeClient extends WebChromeClient {
/**
* For Android 4.1
*/
@SuppressLint("ObsoleteSdkInt")
@SuppressWarnings("unused")
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUM = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
if (multipleFiles && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
startActivityForResult(Intent.createChooser(i, "File Chooser"), FCR);
}
/**
* Android 5.0+
*/
@Override
@SuppressLint("NewApi")
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
requestPermission(AppConstants.PERMISSION_CAMERA, AppConstants.PERMISSION_CAMERA_CODE);
if (mUMA != null) {
mUMA.onReceiveValue(null);
}
mUMA = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(WebViewActivity.this.getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = FileUtils.createImageFile();
takePictureIntent.putExtra("PhotoPath", mCM);
} catch (IOException ex) {
Log.e(TAG, "Image file creation failed", ex);
}
if (photoFile != null) {
mCM = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
if (multipleFiles) {
contentSelectionIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "File Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, FCR);
return true;
}
}
}
3.显示net::ERR_CACHE_MISS
网页缓存导致
mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
网页缓存模式:
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据,
LOAD_DEFAULT:根据cache-control决定是否从网络上取数据,
LOAD_CACHE_NORMAL:API level 17中已经废弃, 从API level 11开始作用同- - LOAD_DEFAULT模式,
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据,
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
4.避免WebView的内存泄露问题
@Override
protected void onDestroy() {
if (webView != null) {
webView.clearCache(true);
webView.clearHistory();
ViewGroup view = (ViewGroup) getWindow().getDecorView();
view.removeAllViews();
webView.destroy();
webView = null;
}
super.onDestroy();
}
5.拨打电话
网页无法直接拨打电话,需要自己拦截电话标签"tel:",然后跳转系统拨号界面。
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 调用拨号程序
if (url.startsWith("tel:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
6.加载完成后白屏
记一次遇到白屏问题
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
webview布局是全占满,但父布局高度是自适应,在加载完成后白屏,就是出现内容闪一下之后白屏,在把父布局高度改为全充满后解决。