转载自:
作者:AWeiLoveAndroid
链接:www.jianshu.com/p/2b2e5d417…
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(1) 为什么Webview打开一个页面,播放一段音乐,退出Activity时音乐还在后台播放?
方案一
//销毁Webview
@Override
protected void onDestroy() {
if (mWebview != null) {
mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebview.clearHistory();
//一定要移除view,避免:Error: WebView.destroy() called while still attached!
((ViewGroup) mWebview.getParent()).removeView(mWebview);
mWebview.destroy();
mWebview = null;
}
super.onDestroy();
}
方案二
@Override
protected void onPause() {
h5_webview.onPause();
h5_webview.pauseTimers();
super.onPause();
}
@Override
protected void onResume() {
h5_webview.onResume();
h5_webview.resumeTimers();
super.onResume();
}
(2) 怎么用网页的标题来设置自己的标题栏?
WebChromeClient mWebChromeClient = new WebChromeClient() {
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
txtTitle.setText(title);
}
};
mWedView.setWebChromeClient(mWebChromeClient());
// 1.可能当前页面没有标题,获取到的是null,那么你可以在跳转到该Activity的时
//候自己带一个标题,或者有一个默认标题。
// 2.在一些机型上面,Webview.goBack()后,这个方法不一定会调用,所以标题还是
//之前页面的标题。那么你就需要用一个ArrayList来保持加载过的url,
//一个HashMap保存url及对应的title.然后就是用WebView.canGoBack()来做判断处理了。
(3) 为什么打包之后JS调用失败(或者WebView与JavaScript相互调用时,如果是debug没有配置混淆时,调用时没问题的,但是当设置混淆后发现无法正常调用了)?
解决方案:在proguard-rules.pro中添加混淆
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
-keep public class org.mq.study.webview.DemoJavaScriptInterface{
public <methods>;
}
#假如是内部类,混淆如下:
-keepattributes *JavascriptInterface*
-keep public class org.mq.study.webview.webview.DemoJavaScriptInterface$InnerClass{
public <methods>;
}
其中org.mq.study.webview.DemoJavaScriptInterface 是不需要混淆的类名
(4) 5.0 以后的WebView加载的链接为Https开头,但是链接里面的内容,比如图片为Http链接,这时候,图片就会加载不出来,怎么解决?
//原因分析:原因是Android 5.0上Webview默认不允许加载Http与Https混合内容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//两者都可以
webSetting.setMixedContentMode(webSetting.getMixedContentMode());
//mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
● MIXED_CONTENT_ALWAYS_ALLOW 允许从任何来源加载内容,即使起源是不安全的;
● MIXED_CONTENT_NEVER_ALLOW 不允许Https加载Http的内容,即不允许从安全的起源去加载一个不安全的
资源;
● MIXED_CONTENT_COMPLTIBILITY_MODE 当涉及到混合式内容时,WebView会尝试去兼容最新Web浏览器的
风格;
另外:在认证证书不被Android所接受的情况下,我们可以通过设置重写WebViewClient的onReceivedSslError方法在其中设置接受所有网站的证书来解决,具体代码如下:
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view,
SslErrorHandler handler, SslError error) {
//super.onReceivedSslError(view, handler, error);注意一定要去除这行代码,否则设置无效。
// handler.cancel();// Android默认的处理方式
handler.proceed();// 接受所有网站的证书
// handleMessage(Message msg);// 进行其他处理
}
});
(5) WebView调用手机系统相册来上传图片,开发过程中发现在很多机器上无法正常唤起系统相册来选择图片。怎么解决?
-
原因分析:因为Google攻城狮们对setWebChromeClient的回调方法openFileChooser做了多次修改,5.0以下openFileChooser有几种重载方法,在5.0以上将回调方法该为了onShowFileChooser。 -
解决方案:为了兼容各个版本,我们需要对openFileChooser()进行重载,同时针对5.0及以上重写onShowFileChooser()方法:
public class MainActivity extends AppCompatActivity {
private ValueCallback<Uri> uploadMessage;
private ValueCallback<Uri[]> uploadMessageAboveL;
private final static int FILE_CHOOSER_RESULT_CODE = 10000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webview = (WebView) findViewById(R.id.web_view);
assert webview != null;
WebSettings settings = webview.getSettings();
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setJavaScriptEnabled(true);
webview.setWebChromeClient(new WebChromeClient() {
// android 3.0以下:用的这个方法
public void openFileChooser(ValueCallback<Uri> valueCallback) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
// android 3.0以上,android4.0以下:用的这个方法
public void openFileChooser(ValueCallback valueCallback, String acceptType) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
//android 4.0 - android 4.3 安卓4.4.4也用的这个方法
public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType,
String capture) {
uploadMessage = valueCallback;
openImageChooserActivity();
}
//android4.4 无方法。。。
// Android 5.0及以上用的这个方法
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]>
filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
uploadMessageAboveL = filePathCallback;
openImageChooserActivity();
return true;
}
});
String targetUrl = "file:///android_asset/up.html";
webview.loadUrl(targetUrl);
}
private void openImageChooserActivity() {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "Image Chooser"),
FILE_CHOOSER_RESULT_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FILE_CHOOSER_RESULT_CODE) {
if (null == uploadMessage && null == uploadMessageAboveL) return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
if (uploadMessageAboveL != null) {
onActivityResultAboveL(requestCode, resultCode, data);
} else if (uploadMessage != null) {
uploadMessage.onReceiveValue(result);
uploadMessage = null;
}
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {
if (requestCode != FILE_CHOOSER_RESULT_CODE || uploadMessageAboveL == null)
return;
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (intent != null) {
String dataString = intent.getDataString();
ClipData clipData = intent.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null)
results = new Uri[]{Uri.parse(dataString)};
}
}
uploadMessageAboveL.onReceiveValue(results);
uploadMessageAboveL = null;
}
- 针对Android4.4,系统把openFileChooser方法去掉了,怎么解决?
详情请见 博客 blog.csdn.net/xiexie758/a…
(6) WebView调用手机系统相册来上传图片,处理好第六点说的方法,我们打好release包测试的时候却又发现还是没法选择图片了。怎么解决?
-
原因分析:查看WebChromeClient的源码,发现openFileChooser()是系统API,我们的release包是开启了混淆的,所以在打包的时候混淆了openFileChooser(),这就导致无法回调openFileChooser()了。 -
解决方案直接不混淆openFileChooser()就好了。
-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}
(7)怎么在 WebView 中长按保存图片?
1. 给 WebView添加监听
mWebview.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
}
});
2. 获取点击的图片地址
先获取类型,根据相应的类型来处理对应的数据。
//首先判断点击的类型
WebView.HitTestResult result = ((WebView) v).getHitTestResult();
int type = result.getType();
//获取具体信息,图片这里就是图片地址
String imgurl = result.getExtra();
type有这几种类型:
- WebView.HitTestResult.UNKNOWN_TYPE 未知类型
- WebView.HitTestResult.PHONE_TYPE 电话类型
- WebView.HitTestResult.EMAIL_TYPE 电子邮件类型
- WebView.HitTestResult.GEO_TYPE 地图类型
- WebView.HitTestResult.SRC_ANCHOR_TYPE 超链接类型
- WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE 带有链接的图片类型
- WebView.HitTestResult.IMAGE_TYPE 单纯的图片类型
- WebView.HitTestResult.EDIT_TEXT_TYPE 选中的文字类型
3. 操作图片
可以弹出保存图片,或者点击之后跳转到显示图片的页面。
mWebView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
WebView.HitTestResult result = ((WebView)v).getHitTestResult();
if (null == result)
return false;
int type = result.getType();
if (type == WebView.HitTestResult.UNKNOWN_TYPE)
return false;
// 这里可以拦截很多类型,我们只处理图片类型就可以了
switch (type) {
case WebView.HitTestResult.PHONE_TYPE: // 处理拨号
break;
case WebView.HitTestResult.EMAIL_TYPE: // 处理Email
break;
case WebView.HitTestResult.GEO_TYPE: // 地图类型
break;
case WebView.HitTestResult.SRC_ANCHOR_TYPE: // 超链接
break;
case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
break;
case WebView.HitTestResult.IMAGE_TYPE: // 处理长按图片的菜单项
// 获取图片的路径
String saveImgUrl = result.getExtra();
// 跳转到图片详情页,显示图片
Intent i = new Intent(MainActivity.this, ImageActivity.class);
i.putExtra("imgUrl", saveImgUrl);
startActivity(i);
break;
default:
break;
}
}
});
(8) WebView 开启硬件加速导致的问题?
WebView有很多问题,比如:不能打开pdf,播放视屏也只能打开硬件加速才能支持,在某些机型上会崩溃。 下面看一下硬件加速, 硬件加速 分为四个级别:
Application级别 <application android:hardwareAccelerated="true"...>
Activity级别 <activity android:hardwareAccelerated="true"...>
window级别(目前为止,Android还不支持在Window级别关闭硬件加速。) getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
View级别 view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
1. 白屏闪烁
-
原因分析:4.0以上的系统我们开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是,当WebView视图被整体遮住一块,然后突然恢复时(比如使用SlideMenu将WebView从侧边滑出来时),这个过渡期会出现白块同时界面闪烁。 -
解决方案:在过渡期前将WebView的硬件加速临时关闭,过渡期后再开启,代码如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
2. 字符重叠
- Android 4.0+ 版本中的EditText字符重叠问题: 做的软件,在一些机器上,打字的时候,EditText中的内容会出现重叠,而大部分机器没有,所以感觉不是代码的问题,一直没有头绪。
出现原因:JellyBean的硬件加速bug,在此我们关掉硬件加速即可。
解决方案:在EditText中加入一句:
android:layerType=”software”
3.图片无法显示
做的程序里有的时候会需要加载大图,但是硬件加速中 OpenGL对于内存是有限制的。如果遇到了这个限制,LogCat只会报一个Warning: Bitmap too large to be uploaded into a texture (587x7696, max=2048x2048)
这时我们就需要把硬件加速关闭了。 关闭了整个应用的硬件加速:
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:hardwareAccelerated="false"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
ListView和WebView等控件显得特别的卡,这说明硬件加速对于程序的性能提升是很明显的。所以我就改为对于Activity的关闭。
<activity
android:name="icyfox.webviewimagezoomertest.MainActivity"
android:label="@string/app_name"
android:hardwareAccelerated="false"
(9) ViewPager里非首屏WebView点击事件不响应是什么原因?
- 如果你的多个WebView是放在ViewPager里一个个加载出来的,那么就会遇到这样的问题。ViewPager首屏WebView的创建是在前台,点击时没有问题;而其他非首屏的WebView是在后台创建,滑动到它后点击页面会出现如下错误日志:
20955-20968/xx.xxx.xxx E/webcoreglue﹕ Should not happen: no rect-based-test nodes found
- 解决方案: 这个问题的办法是继承WebView类,在子类覆盖onTouchEvent方法,填入如下代码:
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY());
}
return super.onTouchEvent(ev);
}
(10)给GLSurfaceView设置为software或者hardware后,发现没有画面了是什么原因?
-
问题分析:GLSurfaceView和WebView默认LayerType都是NONE。** -
解决方案:给GLSurfaceView的LayerType设置为LAYER_TYPE_NONE就可以了。
(11)Android 9.0/P WebView 多进程使用的问题
发现是Android P发布的时候,对WebView相关的使用方式进行了变更:不允许多进程使用同一个目录的WebView,需要为不同进程的WebView设置不同目录。
可以看出来,当我们的targetSdkVersion为28及以上的时候,且需要在多进程模式下使用WebView的时候,就需要对进行相应的调整,以便正确的支持。 解决这个问题,就需要兼容Android P的机制:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 修復WebView的多進程加載的bug
initWebView();
}
private void initWebView() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
String processName = getProcessName();
WebView.setDataDirectorySuffix(processName);
}
}
}
(12)WebView的内存泄漏
- 1.不在xml中定义 Webview ,而是在需要的时候在Activity中创建,并且Context使用 getApplicationgContext()
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(params);
mLayout.addView(mWebView);
- 2.在 Activity 销毁( WebView )的时候,先让 WebView 加载null内容,然后移除 WebView,再销毁 WebView,最后置空。
@Override
protected void onDestroy() {
if (mWebView != null) {
mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebView.clearHistory();
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
其他: