android4.1.2 browser下载音乐文件中文名乱码问题解决

参考资料:
http://blog.csdn.net/qinysong/article/details/1179489
http://lavasoft.blog.51cto.com/62575/273608
http://wenku.baidu.com/view/3668f2d6195f312b3169a571.html
http://blog.csdn.net/lijinwei_123/article/details/6619414
http://blog.csdn.net/coollofty/article/details/8058859
字符编码基础知识
iso8859-1
ISO/IEC 8859-1,又称Latin-1或“西欧语言”,是国际标准化组织内ISO/IEC 8859的第一个8位字符集。
它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入192个字母及符号,藉以供使用变音符号的
拉丁字母语言使用。此字符集支援部分于欧洲使用的语言。
ASCⅡ码
它是美国信息交换标准委员会(American Standards Committee for Information Interchange)
的缩写,为美国英语通信所设计。它由128个字符组成,包括大小写字母、数字0-9、标点符号、
非打印字符(换行符、制表符等4个)以及控制字符(退格、响铃等)组成。
ASCⅡ扩展码
ASCⅡ是针对英语设计的,当处理带有音调标号(形如汉语的拼音)的欧洲文字时就会出现问题。
因此,创建出了一些包括255个字符的由ASCⅡ扩展的字符集。其中有一种通常被称为IBM字符集,
它把值为128-255之间的字符用于画图和画线,以及一些特殊的欧洲字符。
Unicode
Unicode是unicode.org制定的可以容纳世界上所有文字和符号的字符编码方案。
它为每种语言的每个字符设定了统一并且唯一的二进制编码,以满足跨语言跨平台进行文本转换和处理
的要求。Unicode用数字0-0x10FFFF来映射这些字符。
UCS(Universal Character Set)
UCS是ISO制定的ISO10646标准(或称ISO/IEC 10646)所定义的标准字符集(Universal Character Set)。
UCS-2(Unicode-16)用两个字节编码,UCS-4(Unicode-32)用4个字节编码。
Unicode与UCS的关系:
(1)ISO与unicode.org是两个不同的组织, 因此最初制定了不同的标准; 但自从unicode2.0开始,
unicode采用了与ISO 10646-1相同的字库和字码,ISO也承诺ISO10646将不会给超出0x10FFFF的
UCS-4编码赋值,使得两者保持一致.
(2)UCS 和 Unicode 只是分配整数给字符的编码表.
中国国标编码:
GB 13000: 完全等同于ISO 10646-1/Unicode 2.1, 今后也将随ISO 10646/Unicode的标准更改
                    而同步更改.
GB2312:  简体中文编码。
GBK:      对GB2312的扩充, 以容纳GB2312字符集范围以外的Unicode 2.1的统一汉字部分,
                并且增加了部分unicode中没有的字符.
GB18030-2000:基于GB 13000, 作为Unicode 3.0的GBK扩展版本, 覆盖了所有unicode编码,
                            地位等同于UTF-8,UTF-16.是一种unicode编码形式. 变长编码,
                            用单字节/双字节/4字节对字符编码.GB18030向下兼容GB2312/GBK.
GB18030是中国所有非手持/嵌入式计算机系统的强制实施标准.
UTF: Unicode/UCS Transformation Format (Unicode/UCS转换格式)
现在流行的UTF有2种:UTF-8和UTF-16
UTF是一种编码方式,它的出现是因为unicode不适宜在某些场合直接传输和处理.
unicode出现以前的系统大多使用ASCII来处理字符,所以Unicode在诞生之日,
就必须考虑一个严峻的问题:和ASCII字符集之间的不兼容问题。我们知道,ASCII字符是单个字节的,
比如“A”的ASCII是65。而Unicode是双字节的。“A”的Unicode是0065,这就造成了一个非常大的问题:
以前处理ASCII的那套机制不能被用来处理Unicode了。
大多数使用 ASCII 文件的 UNIX 下的工具, 如果不进行重大修改是无法读取 16 位的字符的.
基于这些原因, 在文件名, 文本文件, 环境变量等地方, UCS-2 不适合作为 Unicode 的外部编码.
另一个更加严重的问题是,C语言使用'\0'作为字符串结尾,而Unicode里恰恰有很多字符都有一个字节
为0,这样一来,C语言的字符串函数将无法正常处理Unicode,除非把世界上所有用C写的程序以及他们
所用的函数库全部换掉。采用UTF-8编码对unicode的直接编码作些变换可以避免上述unicode编码
遇到的难题,并带来一些优点.
UTF-8
UTF-8编码对unicode的直接编码作些变换,8bit编码, ASCII不作变换, 其他字符做变长编码, 每个字符
1-3 byte. 通常作为外码.有以下优点:
(1)与CPU字节顺序无关, 可以在不同平台之间交流
(2)容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误
(如GB码错一个字节就会整行乱码)。
UTF-8 有以下特性:
(1)UCS 字符 U+0000 到 U+007F (ASCII) 被编码为字节 0x00 到 0x7F (ASCII 兼容).
这意味着只包含7位ASCII字符的文件在ASCII和UTF-8两种编码方式下是一样的.
(2)所有 >U+007F 的 UCS 字符被编码为一个多个字节的串, 每个字节都有标记位集. 因此,
ASCII 字节 (0x00-0x7F)不可能作为任何其他字符的一部分.
(3)表示非 ASCII 字符的多字节串的第一个字节总是在 0xC0 到 0xFD 的范围里, 并指出这个字符
包含多少个字节.多字节串的其余字节都在 0x80 到 0xBF 范围里.这使得重新同步非常容易,
并使编码无国界, 且很少受丢失字节的影响.可以编入所有可能的231个UCS代码.UTF-8 编码字符
理论上可以最多到 6 个字节长, 然而 16 位 BMP 字符最多只用到3字节长.Bigendian UCS-4
字节串的排列顺序是预定的.字节 0xFE 和 0xFF 在 UTF-8 编码中从未用到.
UTF-16
16bit编码, 是变长码, 大致相当于20位编码, 值在0到0x10FFFF之间, 基本上就是unicode编码的实现.
它是变长码, 与CPU字序有关。但因为最省空间, 常作为网络传输的外码.
UTF-16是unicode的preferred encoding.
UTF-32
32bit编码,仅使用了unicode范围(0到0x10FFFF)的32位编码, 相当于UCS-4的子集.
UTF与unicode的关系:
UTF-8对unicode直接编码做了一些变换.
UTF-16基本上就是unicode编码的实现.是unicode的preferred encoding.
java 中的字符编码问题
在Java中,字符串用统一的Unicode编码,每个字符占用两个字节.
这里Java文件的编码可能有多种多样,但Java编译器会自动将这些编码按照Java文件的编码格式正确读取后
产生class文件,这里的class文件编码是Unicode编码(具体说是UTF-16编码)。
因此,在Java代码中定义一个字符串:
String s="汉字";
不管在编译前java文件使用何种编码,在编译后成class后,他们都是一样的----Unicode编码表示。
JVM加载class文件读取时候使用Unicode编码方式正确读取class文件,那么原来定义的String s="汉字";
在内存中的存储形式是Unicode编码。
java.lang.String类中于编码相关的两个函数:
1)将字符串用指定的编码集合解析成字节数组,完成Unicode--->charsetName转换
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException 
说明:由于JVM中存储的String都是unicode编码格式,所以这个函数的作用就是得到以charsetName编码的
String内容的byte数组。
2)将以指定的编码集合构造的Byte数组转换成unicode形式存储的字符串,完成charsetName--->Unicode转换
public String(byte[] bytes, String charsetName) throws UnsupportedEncodingException
说明:由于JVM中存储的String都是unicode编码格式,所以这个函数返回的String也一定是以unicode编码格式
存储在JVM内存中的,所以这个函数的作用就是将bytes指定的字节数组的内容以unicode编码格式
存放在内存中,由于字节数组bytes的编码格式可能是多种多样的(UTF-8,GBK,gb2312,iso-8859-1)。
所以JVM需要知道bytes的编码格式(charsetName指定),才能将bytes字节数组正确的转换成unicode格式。
利用函数getBytes()我们可以得到任意字符(串)的任意格式的编码表示。

例如:

char c ='致';
      int i =c;
      System.out.println("  ‘致’的 unicode编码格式的16进制表示 ="+Integer.toHexString(i));
        
      String s = "致青春.mp3";
      byte[] bytesgb2312 =null;
      byte[] bytesGBK = null;
      byte[] bytesutf8 = null; 
      
       try
       {
         bytesgb2312 =s.getBytes("gb2312");
	 bytesGBK = s.getBytes("GBK");
	 bytesutf8 =s.getBytes("UTF-8");
      }catch(java.io.UnsupportedEncodingException e)
	{
         e.printStackTrace();
	}
     System.out.println("--------------\n gb2312 bytes:"+s);
     System.out.println("bytes is:     " + arrayToString(bytesgb2312));
     System.out.println("hex format is:"+ encodeHex(bytesgb2312));
		
     System.out.println("--------------\n GBK bytes:"+s);
     System.out.println("bytes is:" + arrayToString(bytesGBK));
     System.out.println("hex format is:" + encodeHex(bytesGBK));
		
     System.out.println("--------------\n UTF-8 bytes:"+s);
     System.out.println("bytes is:" + arrayToString(bytesutf8));
     System.out.println("hex format is:" + encodeHex(bytesutf8));

// bytes以16进制格式打印成字符串	
public static final String encodeHex (byte[] bytes)
	  {
	  StringBuffer buff =
	  new StringBuffer(bytes.length * 2);
	  String b;
	  for (int i=0; i< bytes.length ; i++)
	  {
	  b = Integer.toHexString(bytes[i]); 
	  // byte是两个字节的,而上面的Integer.toHexString会把字节扩展为4个字节
	  buff.append(b.length() > 2 ? b.substring(6,8) : b); 
	  buff.append(" ");
	  }
	  return buff.toString();
	  }
//bytes以字符串格式打印
 public static final String arrayToString (byte[] bytes)
	  {
	  StringBuffer buff = new StringBuffer();
	  for (int i=0; i< bytes.length ; i++)
	  {
	  buff.append(bytes[i] + " ");
	  }
	  return buff.toString();
	  }
JNI函数支持字符串在unicode和utf-8两种编码之间转换。
JNI中与与字符编码相关的两个函数:
const char* GetStringUTFChars(jstring string, jboolean* isCopy)可以将一个jstring 指针(指向jvm 内部的unicode字符序列)转换成一个UTF-8格式的c字符串。
jstring NewStringUTF(const char* bytes)可以在本地方法中创建一个新的java.lang.String字符串对象。这个新创建的字符串拥有与给定的UTF-8字符串(参数bytes指定的UTF-8字符串)内容相同的unicode编码字符串。
即新创建的字符串以unicode编码方式存储在jvm 内存中。
android中这两个函数定义在jni.h中。
android4.1.2Browser.apk下载音乐文件中文名字乱码问题
下载的音乐文件名包含在WebUrlLoaderClient::downloadFile()函数调用
m_response->getHeader("content-disposition", &contentDisposition);
得到的contentDisposition中。contentDisposition的类型是std::string.
contentDisposition中存储的内容最终是网络上传过来的。
WebUrlLoaderClient::downloadFile()函数会接着调用
m_webFrame->downloadStart将contentDisposition传给WebFrame.
WebFrame定义在WebCoreFrameBridge.cpp中。
WebFrame::downloadStart()调用
jstring jContentDisposition = stdStringToJstring(env, contentDisposition, true);
将contentDisposition转换成jstring,通过调用
env->CallVoidMethod(javaFrame.get(), mJavaFrame->mDownloadStart, jUrl, jUserAgent, jContentDisposition, jMimetype, contentLength);
将jContentDisposition传给了framework层的BrowserFrame。
中文文件名乱码问题出现在这步:
jstring jContentDisposition = stdStringToJstring(env, contentDisposition, true);
我们看stdStringToJstring函数的具体实现:
jstring stdStringToJstring(JNIEnv* env, const std::string& str, bool validOnZeroLength)
{
    return !str.empty() || validOnZeroLength ? env->NewStringUTF(str.c_str()) : 0;
}
这个函数定义在WebCoreJni.cpp中。
可以看到这里只是调用JNI函数NewStringUTF创建java的String对象。
根据前面对NewStringUTF函数的介绍,我们知道,这个函数认为给定的字符串是UTF-8编码格式的,并执行UTF-8编码到unicode编码的转换,转换后的unicode字符串存储在
JVM内存中。
如果contentDisposition不是UTF-8编码格式的,这步自然就有问题。
所以如果网络上接收下来的contentDisposition是GBK编码格式的,我们就需要先将contentDisposition转成UTF-8编码格式的,然后在通过stdStringToJstring转成
java的unicode编码的String.
android 平台native层GBK2UTF8的转换相对麻烦,没有现成可利用的函数。
网上有强人自己写的转换函数,再添加个函数即可。
gbk2utf8源码下载
browser.apk
DownloadHandler::onDownloadStartNoStream()先调用
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
接着调用request::setDestinationFromBase()
设置mDestinationUri。
request类定义在frameworks/base/core/java/android/app的DownloadManager类中,
DownloadHandler::onDownloadStartNoStream()接着调用
DownloadManager::enqueue()。这个函数调用request.toContentValues(mPackageName);
将要下载信息存入ContentValues。
再调用mResolver.insert将这个ContentValues通过ContentResolver插入。
通过ContentResolver来使用ContentProvider.
mResover.instert实际调用的是DownloadProvider.insert().
  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值