文档章节

使用WebView.load(data, text/html, utf-8)加载显示乱码问题分析

哦_呢称
 哦_呢称
发布于 2017/07/09 14:39
字数 1076
阅读 8
收藏 0

本文最先发布在CSDN博客,地址:http://blog.csdn.net/hwliu51/article/details/70216242。 欢迎转载,但请标明原文地址。

本文引用的源码为android 4.4.4版本

使用WebView的load(data,"text/html", "utf-8")加载含有中文的网页时,页面上的中文字符显示为乱码。网页没有问题,使用PC浏览器查看显示正常。 load方法源码

	public void loadData(String data, String mimeType, String encoding) {
        checkThread();
        if (DebugFlags.TRACE_API) Log.d(LOGTAG, "loadData");
        mProvider.loadData(data, mimeType, encoding);
    }

真正执行加载网页操作的是mProvider,而它是什么类型的对象呢?

	private WebViewProvider mProvider;
	...
	private void ensureProviderCreated() {
        checkThread();
        if (mProvider == null) {
            // As this can get called during the base class constructor chain, pass the minimum
            // number of dependencies here; the rest are deferred to init().
            //调用方法getFactory获取WebViewFactoryProvider对象,然后使用该对象的createWebView方法创建。
            mProvider = getFactory().createWebView(this, new PrivateAccess());
        }
    }

	//生成WebViewFactoryProvider方法
	private static synchronized WebViewFactoryProvider getFactory() {
        return WebViewFactory.getProvider();
    }

WebViewFactoryProvider是一个接口,那么就查看WebViewFactory类。WebViewFactory的getProvider方法调用getFactoryClass()获取到字节码,然后通过反射创建了WebViewChromiumFactoryProvider对象。

public final class WebViewFactory {
	//类路径
    private static final String CHROMIUM_WEBVIEW_FACTORY =
            "com.android.webview.chromium.WebViewChromiumFactoryProvider";

    ...

    private static class Preloader {
        static WebViewFactoryProvider sPreloadedProvider;
        //静态代码块,字节码加载进来便创建了WebViewFactoryProvider对象
        static {
            try {
	            //通过反射创建对象
                sPreloadedProvider = getFactoryClass().newInstance();
            } catch (Exception e) {
                Log.w(LOGTAG, "error preloading provider", e);
            }
        }
    }

    …

    static WebViewFactoryProvider getProvider() {
        synchronized (sProviderLock) {
            //存在对象,则返回
            if (sProviderInstance != null) return sProviderInstance;

            Class<WebViewFactoryProvider> providerClass;
            try {
	            //获取字节码对象
                providerClass = getFactoryClass();
            } catch (ClassNotFoundException e) {
                Log.e(LOGTAG, "error loading provider", e);
                throw new AndroidRuntimeException(e);
            }

            //对象存在且字节码相同
            if (Preloader.sPreloadedProvider != null &&
                Preloader.sPreloadedProvider.getClass() == providerClass) {
                //赋值
                sProviderInstance = Preloader.sPreloadedProvider;
                
                return sProviderInstance;
            }

            
            StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
            try {
	            //通过字节码创建对象
                sProviderInstance = providerClass.newInstance();
                if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
                return sProviderInstance;
            } catch (Exception e) {
                Log.e(LOGTAG, "error instantiating provider", e);
                throw new AndroidRuntimeException(e);
            } finally {
                StrictMode.setThreadPolicy(oldPolicy);
            }
        }
    }

	//获取字节码的方法
    private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException {
        return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY);
    }
}

WebViewFactory被加载进来,便创建WebViewChromiumFactoryProvider对象。源码在frameworks/webview/chromium/java/com/android/webview/chromium/WebViewChromiumFactoryProvider.java。 那就看看WebViewChromiumFactoryProvider.loadData方法代码:

public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
        WebViewChromium wvc = new WebViewChromium(this, webView, privateAccess);

        synchronized (mLock) {
            if (mWebViewsToStart != null) {
                mWebViewsToStart.add(new WeakReference<WebViewChromium>(wvc));
            }
        }
        ResourceProvider.registerResources(webView.getContext());
        return wvc;
    }

该方法创建了WebViewChromium对象,并返回了该对象。在ensureProviderCreated方法中创建了该对象,并将其赋值给WebView中的mProvider属性。也就是说,在WebView中网页相关的操作都是WebViewChromium真正在执行。

WebViewChromium的源码在:frameworks/webview/chromium/java/com/android/webview/chromium/WebViewChromium.java。 看看WebViewChromium的loadData方法:

public void loadData(String data, String mimeType, String encoding) {
        loadUrlOnUiThread(LoadUrlParams.createLoadDataParams(
                fixupData(data), fixupMimeType(mimeType), isBase64Encoded(encoding)));
    }

使用LoadUrlParams类的静态方法createLoadDataParams对传入的参数做了封装。 继续查看LoadUrlParams,源码在:external/chromium_org/content/public/android/java/src/org/chromium/content/browser/LoadUrlParams.java。

	public static LoadUrlParams createLoadDataParams(
        String data, String mimeType, boolean isBase64Encoded) {
        return createLoadDataParams(data, mimeType, isBase64Encoded, null);
    }

	public static LoadUrlParams createLoadDataParams(
            String data, String mimeType, boolean isBase64Encoded, String charset) {
        StringBuilder dataUrl = new StringBuilder("data:");
        //类型
        dataUrl.append(mimeType);
        //编码类型
        if (charset != null && !charset.isEmpty()) {
            dataUrl.append(";charset=" + charset);
        }
        //是否为base64编码
        if (isBase64Encoded) {
            dataUrl.append(";base64");
        }
        //分割符
        dataUrl.append(",");
        //网页
        dataUrl.append(data);

        LoadUrlParams params = new LoadUrlParams(dataUrl.toString());
        params.setLoadType(LoadUrlParams.LOAD_TYPE_DATA);
        params.setTransitionType(PageTransitionTypes.PAGE_TRANSITION_TYPED);
        return params;
    }

由以上源码可知:WebView的loadData方法传入的参数encoding并没有被封装到LoadUrlParams中,所以导致中文显示乱码。 调用createLoadDataParams( String data, String mimeType, boolean isBase64Encoded, String charset)方法肯定是能够将编码类型封装到LoadUrlParams。往回看看,哪里调用了该方法。WebViewChromium中的方法loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)有调用到。

	public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding,
            String historyUrl) {
        data = fixupData(data);
        mimeType = fixupMimeType(mimeType);
        LoadUrlParams loadUrlParams;
        baseUrl = fixupBase(baseUrl);
        historyUrl = fixupHistory(historyUrl);

        if (baseUrl.startsWith("data:")) {
            // For backwards compatibility with WebViewClassic, we use the value of |encoding|
            // as the charset, as long as it's not "base64".
            boolean isBase64 = isBase64Encoded(encoding);
            //如果是base64编码:传入的编码类型为null;不是则传入设置的编码类型
            loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
                    data, mimeType, isBase64, baseUrl, historyUrl, isBase64 ? null : encoding);
        } else {
            // When loading data with a non-data: base URL, the classic WebView would effectively
            // "dump" that string of data into the WebView without going through regular URL
            // loading steps such as decoding URL-encoded entities. We achieve this same behavior by
            // base64 encoding the data that is passed here and then loading that as a data: URL.
            try {
	            //设置为utf-8编码
                loadUrlParams = LoadUrlParams.createLoadDataParamsWithBaseUrl(
                        Base64.encodeToString(data.getBytes("utf-8"), Base64.DEFAULT), mimeType,
                        true, baseUrl, historyUrl, "utf-8");
            } catch (java.io.UnsupportedEncodingException e) {
                Log.wtf(TAG, "Unable to load data string " + data, e);
                return;
            }
        }
        loadUrlOnUiThread(loadUrlParams);
    }

WebView中的loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) 方法调用了该方法,所以使用该方法能够解决乱码问题。 使用这种方式便可以解决中文乱码。

loadDataWithBaseURL(null, "html", "text/html", "UTF-8", null);

© 著作权归作者所有

共有 人打赏支持
哦_呢称
粉丝 1
博文 11
码字总数 13032
作品 0
私信 提问
WebView当前APP加载网页内容

1、loadUrl(String url);直接加载网页、图片并显示 本地资源: mWebview.loadUrl("file:///android_asset/x.html"); 远程资源: mWebview.loadUrl(“http://www.baidu.com"); 2、loadData (d......

Jaume
2016/09/07
32
0
Android WebView出现的乱码问题

1、webview加载网页<html>源码乱码问题 一、webView.loadUrl(); 直接显示网页内容(单独显示网络图片),一般不会出现乱码。 二、webView.loadData(data, "text/html", "UTF-8"); loadData主要...

当空皓月
2015/01/05
0
5
servlet 中文乱码问题及解决方案

一、常识了解 1.GBK包含GB2312,即如果通过GB2312编码后可以通过GBK解码,反之可能不成立; 2.java.nio.charset.Charset.defaultCharset() 获得平台默认字符编码; 3.getBytes() 是通过平台默...

三番水
2013/05/31
0
0
JQuery中Ajax的Post提交中文乱码的解决方案(转)

引言: 在JQuery的Ajax POST请求中,进行请求,其中的中文在后台,显示为乱码,该如何解决呢? 问题的引入: var regid = $('#oregion').combobox('getValue'); //var sname = $('#sname')....

Atom_me
2015/06/02
217
2
response.setHeader()下载中文文件名乱码问题

HTTP消息头 (1)通用信息头 即能用于请求消息中,也能用于响应信息中,但与被传输的实体内容没有关系的信息头,如Data,Pragma 主要: Cache-Control , Connection , Data , Pragma , Trailer , ...

Dicky
2011/10/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

颜色模型与颜色应用---光的特性

电磁频谱 颜色的心理学特征

中国龙-扬科
21分钟前
2
0
android音频及强噪相关

Android AudioRecord和AudioTrack介绍(Android音频收集和播放 麦克风降噪) https://blog.csdn.net/tanningzhong/article/details/72844559 音频采集(AudioRecorder) https://www.jianshu.......

whoisliang
27分钟前
2
0
redis-持久化

RDB rdb持久化是把当前进程数据生成快照保存到磁盘的过程。触发RDB持久化过程分为手动触发和自动触发。 触发机制 bgsave执行流程 rdb优缺点 AOF 记录每次写命令,重启时再重新执行aof文件中的...

grace_233
32分钟前
3
0
电话激活Windows 中文操作系统步骤

已购买微软MAK批量授权,系统又在企业内网中,无法通过连接Internet进行激活,还可以通过电话完成激活。 前期准备 请提前准备好产品密钥,Product Key格式如下:XXXXX-XXXXX-XXXXX-XXXXX-XXX...

tonyfox
34分钟前
4
0
Apache用户认证,域名跳转,访问日志

Apache用户认证 当设置了用户认证后,用户访问网站时,需要输入用户名和密码才能访问。 可以全局设置,也可以为某几个虚拟主机单独配置。 下面以全局配置进行操作示例。 编辑httpd.conf进行配...

野雪球
38分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部