当前位置: 首页 > >

WebView常见问题及解决方案

发布时间:

目前HTML5发展快速,很多native app都会适当地嵌入网页,以此来适应多变的需求变化。但是android的WebView默认支持的功能很弱,很多都需要自定义,才能实现我们想要的效果。并且WebView在不同的版本下,均有不同程度的bug。总结通常使用WebView需要注意的地方如下:






1.重写WebViewClient






? ? ?protected ProgressBar mProgressBar;


???? private RelativeLayout mLayoutNetError;????????? //显示网络错误


???? private Button mBtnRetry;???????????????????????? //重新刷新


?????????


???? private boolean mInited = false;????????? //是否网页初始化完成


???? private boolean mNetworkError = false;???? //是否网络错误的标记


???? protected String mEntryUrl;??????????????????? //刚进入时打开的url


????


???? private ValueCallback mUploadMessage;???? //上传文件时用

? ? ?

? ? ?/**

????? * Web View Client


????? *


????? * @author houjinyun


????? * @date 2013-11-18 下午12:44:28


????? */


???? public class CustomWebViewClient extends WebViewClient {


?????????


????????? private static final String TAG = "CustomWebViewClient";


?????????


? ? ? ? ??
//网页开始加载,在这里可以进行加载动画等等


????????? @Override


????????? public void?
onPageStarted(WebView view, String url, Bitmap favicon) {


?????????????? super.onPageStarted(view, url, favicon);


?????????????? mProgressBar.setVisibility(View.VISIBLE);


????????? }


?????????


? ? ? ? ??


????????? @Override


????????? public void?
onPageFinished(WebView view, String url) {


?????????????? super.onPageFinished(view, url);


?????????????? mProgressBar.setVisibility(View.GONE);


?????????????? if(!mInited && !mNetworkError) {


??????????????????? mInited = true;


?????????????? }


????????? }


?????????


????????? /**


?????????? * SSL接收过程发生错误


?????????? */


????????? @Override


????????? public void?
onReceivedSslError(WebView view,


??????????????????? SslErrorHandler handler, SslError error) {


?????????????? if(!mInited) {


??????????????????? mWebView.loadData("", "text/html", "utf_8");


??????????????????? mLayoutNetError.setVisibility(View.VISIBLE);???? //显示网络错误


??????????????????? mBtnRetry.setTag(mEntryUrl);????????? //记录URL


??????????????????? mNetworkError = true;


?????????????? } else {


? ? ? ? ? ? ? ? ? ??
//这样才能让WebView处理https请求,否则WebView将无法处理


???????????????????? handler.proceed();


?????????????? }


????????? }


?????????


????????? /**


?????????? * 接收过程发生错误


?????????? */


????????? @Override


????????? public void?
onReceivedError(WebView view, int errorCode,


??????????????????? String description, String failingUrl) {


?????????????? Log.d(TAG, description + "-- error code " + errorCode + " of " + failingUrl);


? ? ? ? ? ? ? ?
//网页加载出现错误,只处理如下几种情况。默认的WebView会直接显示出错的网络地址url,有可能暴露数据。在这种情况下,需要将网页内容置空,提示用户网络错误


?????????????? if(errorCode == WebViewClient.ERROR_CONNECT || errorCode == WebViewClient.ERROR_TIMEOUT || errorCode == WebViewClient.ERROR_HOST_LOOKUP) {


??????????????????? mWebView.loadData("", "text/html", "utf_8");


??????????????????? mLayoutNetError.setVisibility(View.VISIBLE);???? //显示网络错误


??????????????????? mBtnRetry.setTag(failingUrl);???? //记录URL


??????????????????? mNetworkError = true;


?????????????? }


????????? }


?????????


? ? ? ? ??
//return false表示WebView自己处理,return true表示我们自己来处理该url。像“tel:10086”等等类似的url,WebView是不会处理的,需要我们自己来处理。


? ? ? ? ??
//通常情况下,以http、https、file(表示本地网页)开头的url,都给WebView自己来加载,其他类型的url都通过系统应用程序来打开


????????? @Override


????????? public boolean?
shouldOverrideUrlLoading(WebView view, String url) {


?????????????? if(Uri.parse(url).getScheme().startsWith("file")) {


??????????????????? //如果是本地加载的话,直接用当前浏览器加载


??????????????????? return false;


?????????????? }


?????????????? if(Uri.parse(url).getScheme().startsWith("http")) {


??????????????????? return false;


?????????????? }
? ? ? ? ? ? ? ?if ("about:blank".equals(url)){

??????????????????? return false; ? ? ? ? ? ? ? ? ? ? ? ? //不需要处理空白页


? ? ? ? ? ? ? ?}




???????????????
//不能识别的,启动系统程序进行加载


? ? ? ? ? ? ? ?Intent intent = new Intent(Intent.ACTION_VIEW);


? ? ? ? ? ? ? ?try {


? ? ? ? ? ? ? ? ? ? intent.setData(Uri.parse(url));


? ? ? ? ? ? ? ? ? ?context.startActivity(intent);


? ? ? ? ? ? ? ?} catch (Exception e) {


? ? ? ? ? ? ? ? ? ? e.printStackTrace();


? ? ? ? ? ? ? ?}


?????????????? return true;


????????? }


?????????


? ? ?}








2.WebChromeClient




? ? ?/**

????? * Web Chrome Client


????? *


????? * @author houjinyun


????? *


????? */


???? public class CustomWebChromeClient extends WebChromeClient{


????????? private static final String TAG = "WebChromeClient";


????????? /**


?????????? * 控制台消息输出


?????????? */


????????? @Override


????????? public void?
onConsoleMessage(java.lang.String message, int lineNumber, java.lang.String sourceId){


?????????????? Log.d(TAG, String.format("%s -- From line %s of %s", message, lineNumber, sourceId));


????????? }


????????? /**


?????????? * 控制台消息输出


?????????? */


????????? @Override


????????? public boolean?
onConsoleMessage(ConsoleMessage cm) {


?????????????? if (cm.messageLevel() == ConsoleMessage.MessageLevel.DEBUG){


??????????????????? Log.d(TAG, String.format("%s -- From line %s of %s", cm.message(), cm.lineNumber(), cm.sourceId()));


?????????????? }else if (cm.messageLevel() == ConsoleMessage.MessageLevel.LOG


???????????????????????? || cm.messageLevel() == ConsoleMessage.MessageLevel.TIP){


??????????????????? Log.i(TAG, String.format("%s -- From line %s of %s", cm.message(), cm.lineNumber(), cm.sourceId()));


?????????????? }else if (cm.messageLevel() == ConsoleMessage.MessageLevel.WARNING){


??????????????????? Log.w(TAG, String.format("%s -- From line %s of %s", cm.message(), cm.lineNumber(), cm.sourceId()));


?????????????? }else if (cm.messageLevel() == ConsoleMessage.MessageLevel.ERROR){


??????????????????? Log.e(TAG, String.format("%s -- From line %s of %s", cm.message(), cm.lineNumber(), cm.sourceId()));


?????????????? }


?????????????? return true;


????????? }


????????? @Override


????????? public boolean?
onJsAlert(WebView view, String url, String message, final JsResult result){


?????????????? AlertDialog.Builder builder = new AlertDialog.Builder(BaseWebActivity.this);


?????????????? builder.setMessage(message);


?????????????? builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {????????????????????????


??????????????????? @Override


??????????????????? public void onClick(DialogInterface dialog, int which) {


???????????????????????? result.confirm();


??????????????????? }


?????????????? });


?????????????? builder.setCancelable(false).create().show();


?????????????? return true;


????????? }


????


????????? /**


?????????? * 页面加载进度


?????????? */


????????? @Override


????????? public void?
onProgressChanged(android.webkit.WebView view, int newProgress){


?????????????? Log.d(TAG, String.format("正在加载...(%s)", newProgress));


????????? }






????????? // For Android 3.0+


????????? public void openFileChooser(ValueCallback uploadMsg, String acceptType) {


?????????????? this.openFileChooser(uploadMsg, acceptType, null);


????????? }






????????? // For Android < 3.0


????????? public void openFileChooser(ValueCallback uploadMsg) {


?????????????? this.openFileChooser(uploadMsg, null, null);


????????? }






? ? ? ? ??
//针对网页里的, WebView默认是不会弹出选择文件对话框的,需要重写该方法,自己来弹出选择文件对话框。


? ? ? ? ? //注意SDK不同的版本会有不同的方法,需要统一处理


????????? // For Android > 4.1.1


????????? public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) {


?????????????? Log.d("BankkaActivity", "acceptType=" + acceptType + ",capture="+capture);


?????????????? mUploadMessage = uploadMsg;


?????????????? if ("camera".equalsIgnoreCase(capture) && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){//有存贮卡的话,才走拍照


??????????????????? //拍照


??????????????????? takePicture4InputFile();


?????????????? }else{


??????????????????? if (capture == null && (acceptType == null || acceptType.startsWith("image/")) && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){


???????????????????????? showMenu4InputFile(acceptType);


??????????????????? }else{


???????????????????????? //文件选择


???????????????????????? chooseFile4InputFile(acceptType);


??????????????????? }


?????????????? }


????????? }


?????????


? ? ?}








3.openFileChooser()


? ? ?
针对网页里来进行文件上传时,必须重写该方法。见第3点里的重写方法。






? ? ?/**


???? * 选择文件为客户端的Input File功能


???? */


??? private void chooseFile4InputFile(String acceptType){


???????? Intent intent = new Intent(Intent.ACTION_GET_CONTENT); ? ??


????????? intent.addCategory(Intent.CATEGORY_OPENABLE); ? ??
//能够返回一个Uri结果


????????? if (CommonMethod.isEmpty(acceptType)){//接收类型


?????????????? acceptType = "*/*"; ? ??
//选择的文件类型,例如:image/*表示图片


????????? }


????????? intent.setType(acceptType);


????????? startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_CHOOSERFILE);


??? }


??? /**


???? * 拍照为客户端的Input File功能


???? */


??? private void takePicture4InputFile(){


???????? Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);


????????? startActivityForResult(takePictureIntent, FILECHOOSER_TAKEPICTURE);


??? }


??? /**


???? * 显示选择菜单为Input File功能


???? * @param acceptType


???? */


??? private void showMenu4InputFile(final String acceptType){


???????? String[] menus = {"拍照", "相册"};


????????? Dialog dialog = new AlertDialog.Builder(this).setItems(menus, new DialogInterface.OnClickListener() {


?????????????? @Override


?????????????? public void onClick(DialogInterface dialog, int which) {


??????????????????? switch(which){


???????????????????????? case 0:


????????????????????????????? takePicture4InputFile();


????????????????????????????? break;


???????????????????????? case 1:


???????????????????????? default:


????????????????????????????? chooseFile4InputFile(acceptType);


????????????????????????????? break;


??????????????????? }


?????????????? }


????????? }).create();






? ? ? ? ??
//这里很重要,如果弹出对话框,用户选择一个图片或者进行拍照,但是进行到一半的时候,用户cancel了,这个时候再去点击“选择文件”按钮时,网页会失去响应。


? ? ? ? ??
//原因是:点击“选择文件”按钮时,网页会缓存一个ValueCallback
对象,必须触发了该对象的onReceiveValue()方法,WebView才会释放,进而才能再一次的选择文件。


????????? //当弹层被取消时,返回未选择文件


????????? dialog.setOnCancelListener(new OnCancelListener(){


?????????????? @Override


?????????????? public void onCancel(DialogInterface dialog) {


??????????????????? mUploadMessage.onReceiveValue(null);


??????????????????? mUploadMessage = null;


?????????????? }


????????? });


????????? dialog.show();


??? }


? ? ?


在onActivityResult()方法里进行回调处理:
if (requestCode == FILECHOOSER_TAKEPICTURE && this.mUploadMessage != null){

?????????????? //选择文件时的拍照功能


?????????????? Uri uri = null;


?????????????? if (resultCode == Activity.RESULT_OK && Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){


??????????????????? FileOutputStream fos = null;


??????????????????? try{


???????????????????????? File file = Environment.getExternalStorageDirectory();


???????????????????????? file = new File(file, "51zhangdan/temp");


???????????????????????? if (!file.exists()){


????????????????????????????? file.mkdirs();


???????????????????????? }


???????????????????????? file = new File(file, "filechoosertmp.png");


???????????????????????? Bitmap bitmap = (Bitmap)intent.getExtras().get("data");


???????????????????????? fos = new FileOutputStream(file);


???????????????????????? bitmap.compress(CompressFormat.PNG, 100, fos);


???????????????????????? fos.close();


???????????????????????? fos = null;


???????????????????????? uri = Uri.fromFile(file);


??????????????????? }catch(Exception e){


???????????????????????? Log.e(TAG, String.format("处理TakePicture图片数据出错.%s", e.getMessage()));


??????????????????? }finally{


???????????????????????? if (fos != null){


????????????????????????????? try {


?????????????????????????????????? fos.close();


????????????????????????????? } catch (IOException e) {


?????????????????????????????????? Log.e(TAG, String.format("关闭TakePicture图片数据出错.%s", e.getMessage()));


????????????????????????????? }


???????????????????????? }


??????????????????? }


???????????????????


?????????????? }


? ? ? ? ? ? ? ?//这里进行回调,WebView才能获取到你选择了哪个文件




?????????????? this.mUploadMessage.onReceiveValue(uri); ? ??


?????????????? this.mUploadMessage = null;


? ? ? ? ? }








4.DownloadListener


? ? ?
默认情况下,WebView是不能处理下载链接的,一个download url也是以http、https之类开头的,在shouldOverrideUrlLoading()里是不能根据url来判断它是否是一个下载链接。






//该接口可以监听到WebView里打开的下载链接,重写该接口,我们可以得到下载地址的url,进而自己来处理下载。


public class CustomDownloadListener implements android.webkit.DownloadListener {





????????? private static final String TAG = "CustomDownloadListener";


?????????


????????? @Override


????????? public void onDownloadStart(String url, String userAgent,


??????????????????? String contentDisposition, String mimetype, long contentLength) {


?????????????? Log.d(TAG, "download:");


?????????????? Log.d(TAG, url+ "");


?????????????? Log.d(TAG, userAgent + "");


?????????????? Log.d(TAG, contentDisposition + "");


?????????????? Log.d(TAG, mimetype + "");


?????????????? Log.d(TAG, "contentLength: " + contentLength);


?????????????? CommonMethod.launchBrowser(BaseWebActivity.this, url);


????????? }


?????????


???? }


mWebView.setDownloadListener(new CustomDownloadListener());








5.proguard混淆问题


? ? ?混淆时,该WebView里的自定义方法可能会被混淆掉,出现莫名其妙的问题,导致我们的设置都不起作用。例如:

-keep class com.example.app.activities.BaseWebActivity$*{*;}


-
keepclassmembers?
class com.
example
.app.activities.BaseWebActivity$*{*;}





-keep public class android.net.http.SslError
-keep public class android.webkit.WebViewClient

-dontwarn?android.webkit.WebView
-dontwarn?android.net.http.SslError


-
dontwarn?
android.webkit.WebViewClient








#如果有与javascript进行交互的对象,则需要如下处理方式

-keepclassmembers?class com.example.app.view.ComJSInterface {
?? public *;


}








6.onDestroy()时问题



? ? ?在界面退出时,由于WebView自身的bug,可能会出现一些异常,可以做如下处理:

? ? ?@Override

???? protected void onDestroy() {


????????? super.onDestroy();


????????? if(mWebView != null) {


?????????????? ((ViewGroup)findViewById(R.id.RelativeLayout_Content)).removeView(mWebView);


?????????????? mWebView.removeAllViews();


?????????????? mWebView.destroy();


?????????????? mWebView = null;


????????? }


? ? ?}














友情链接: