首页 > 编程技术 > android

Android中POST请求中的UTF-8编码问题

发布时间:2016-9-20 19:59

下面本文章来给各位同学介绍一个关于Android中POST请求中的UTF-8编码问题解决办法,如果你碰到不防进入参考。

今天遇到这样一个bug:客户端POST到服务器的一段数据导致服务器端发生未知异常。服务器端确认是编码转换错误。于是截取网络数据包进行分析,发现客户端POST的json数据中包含下面一段(hex形式):

... 61 64 20 b7 20 52 69 63 ...

问题就出在这个b7上。查阅Unicode代码表后发现,U+00b7是MIDDLE DOT,它的UTF-8表现形式应该是c2 b7,但为何客户端发送的数据中它变成了b7?

由于系统使用了ormlite、gson和async-http几个库,于是逐一排查。最后发现原来是向服务器发送数据时没有指定文字编码,导致async-http(实际是apache common http client)将数据以ISO-8559-1格式发送,U+00b7被编码成b7,然后服务器试图使用UTF-8解码时发生错误。

出错的代码片段如下:

 代码如下 复制代码

Gson gson = new Gson();
String json = gson.toJson(data);
StringEntity entity = new StringEntity(json);
httpClient.post(context, url, entity, "application/json", new TextHttpResponseHandler() ... );

第三行new StringEntity(json)时没有指定编码导致错误。改正后如下:

 代码如下 复制代码

Gson gson = new Gson();
String json = gson.toJson(data);
StringEntity entity = new StringEntity(json, "utf-8");
httpClient.post(context, url, entity, "application/json;charset=utf-8", new TextHttpResponseHandler() ... );

 

经过这么久在android客户端和服务器端的开发,感觉还是积累了不少东西想要和大家分享一下,但是好想单独拎一个点出来又不太值得,所以就汇集到一起写成系列吧。

一. 关于用户数据存储

首先在注册的协议里,定义如下公共传输字段:

version: 这个其实是xml中配置的versionCode。versionName个人认为没有什么必要,所以就不传了。

channel:  用户渠道,这个和xml中的UMENG_CHANNEL 是共用的,因为一直在使用umeng,所以这样定义反而清楚一些。

device_id: 设备ID

os: 操作系统类型,这里默认传入android

os_version: 操作系统版本

对于小数据量,用户数据存储在mysql中是相对较好的选择,这里直接以django的model为例:

 代码如下 复制代码
from django.db import models
class User(models.Model):
    device_id = models.CharField(max_length=255, null=True, blank=True)
    version = models.IntegerField()
    channel = models.CharField(max_length=64, null=True, blank=True)
    os = models.CharField(max_length=64, null=True, blank=True)
    os_version = models.CharField(max_length, null=True, blank=True)
    create_time = models.DateTimeField(default=get_cur_time)
    login_time = models.DateTimeField(default=get_cur_time)


如果需要用到如facebook之类的联合登录,对于小规模的服务,我个人倾向于不破坏User的定义,因为毕竟这种联合登录什么时候会加入很难预知:

 代码如下 复制代码
class FBUser(models.Model):
    # facebook 用户ID
    userid = models.CharField(max_length=32, unique=True)
    # User.id,这里不用外键,是为了以后拆分表或者换数据存储留下后路
    native_id = models.IntegerField(default=0)
 
    def __unicode__(self):
        return '%s->%s' % (self.userid, self.native_id)


其他的业务相关的字段定义就由业务自己决定了。

我不是很建议在未来可能数据量很大的表里使用 外键,因为很可能以后要设计到分库分表、或者迁移数据到redis、mongodb之类的,这在我之前的博文里面就有提到过,大家有兴趣可以看一下。

 

二. 关于通信协议的选择

其实这块还真的有不少东西可以说的。

HTTP

最简单的肯定是用http协议,但是http协议在手机应用上其实只能满足传统一收一发的需求,即使是使用long poll之类技术,经过我测试,当在3G网络下时,运营商经常会强制返回http请求为502错误。

WebSocket

当然,如果对http还是心有所属,可以使用 websocket,经过测试 websocket还是比较好用的,cocos2d-x 有专门提供websocket的封装,android下也有专门的websocket的库: AndroidAsync

python也提供了很多websocket的server和client。比如server端有 gevent-websocket、以及在其基础上开发的flask plugin: flask-sockets。当然django也是可以直接使用gevent-websocket的,django还有一个不基于gevent的版本:django-websocket。client端有 websocket-client。

基于gevent的server之前测试过,可以正常的收到断掉链接的消息,逻辑处理也都比较正常。最终要的一点是,他可以和现有http服务器无缝结合,不需要做跨进程在两个server间通讯。

SocketIO

这个我也测试过,但是实在不建议大家在项目中使用,他做了太多的兼容的事情了,而我们客户端又不是浏览器,根本不需要考虑那么多事情,只要用一种协议就可以了。

还有一点就是,我试了下python的socketio server似乎有点问题,在客户端主动close链接时,服务器端并没有收到事件,而只有客户端发送disconnect命令才会触发服务器端的相关事件。这个事情在网上查了一下,貌似是官方故意做的处理,防止浏览器在刷新时触发一些奇怪的事情,但是这样处理对于我是无法忍受的。

不过还是把相关的链接发给大家,大家可以试一下。android客户端:还是  AndroidAsync。 python server端:Flask-SocketIO,django-socketio,python client端:socketIO-client。

原生socket+自定义协议

这种方式灵活度肯定是最高的,但是相应的开发难度肯定也会增大。协议可以使用json或者google 的 protobuf。这个可能一两句话还说不清楚,下一篇我们专门花篇幅聊一下。

在android开发过程中,APP需要用到摄像头录制视频音效、播放视频的功能,并且通过第三方线程调用Handler动态的addview和removeview添加和删除播放视频的组件——MediaPlayer。

一、产生异常原因

每次在点播放按钮的时候,打开MediaPlayer的SurfaceView进行播放,再次点击则删除波翻组件,停止播放,这个过程中会遇到The surface has been released 错误,这个的原因是因为:在播放this.mediaPlayer.start()之前SurfaceView没有来的及调用onCreate()或者onChange()方法,导致holder没有成功加载,所以在start播放的时候抛出播放异常。

二、解决办法

很简单的办法就是:
1.设置一个boolean标志位isSurfaveCreated,在执行onCreate()或者onChange()之后,将isSurfaveCreated=true。
2.在停止播放之后isSurfaveCreated=false。
3.在MediaPlayer.setDisplay()之前使用while循环以及Thread.sleep(10)来循环检测isSurfaveCreated,只有isSurfaveCreated为true的时候,才继续执行,具体代码为:

 代码如下 复制代码
while (! this.isSurfaveCreated) {
    try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

当然,这个办法自己感觉是一个非主流的办法,但是确实可以很好的避免这个问题,除此之外,大家自己考虑代码的安全稳定性等等因素,Enjoy~

三星I879的刷机教程也分享一下了,这个刷机教程是采用卡刷的方式,这个卡刷教程主要就是来刷第三方包的,因为之前也有人在刷第三方包,可是就是不知道怎么进行详细的刷机操作,下面就来给大家分享一下详细的卡刷步骤了,如果你也想刷机的话可以一起来看看详细的刷机步骤吧:
一:三星I879刷机前的准备工作:
1:下载rom刷机包,点,这里提供的有相关的卡刷包,如果你的手里已经有rom卡刷包了也可以,下载其它地方的rom包也可以,只要是支持卡刷的就行,因为所有的卡刷包的刷机方法是一样的。
2:确保手机能用usb数据线正常的连接电脑,连接电脑是为了把上面下载的rom刷机包复制到手机的sd卡里
3:因为是卡刷,所以手机里必须先要刷入第三方的recovery才可以,如果你的手机里还没有刷入第三方的recovery的话,点击这里查看详细的刷入recovery的教程>>>> 
二:三星I879卡刷刷机的操作:
1:手机用usb数据线连接上电脑之后,把上面下载下来的zip格式的rom刷机包复制到手机的sd卡的根目录下方便找到。
2:手机先关机,然后手机在关机状态下按住手机的音量上键 + Home键 + 电源键(两键一起按),一会就会进入recovery界面了。
 
 
3:进入recovery界面之后先进行双清,(按音量键表示选择,按开机键表示确认),依次执行
 
三星I879卡刷刷机教程(图文)

三星I879卡刷刷机教程(图文)
 
wipe data/factory reset——Yes——delete all user data
 
三星I879卡刷刷机教程(图文)

三星I879卡刷刷机教程(图文)
 
wipe cache partition——Yes-Wipe Cache
4:双清之后按音量键选择install zip from sdcard,然后再选择choose zip from sdcard,然后找到刚才放到手机sd卡里zip格式的rom刷机包 XXXX.zip,然后按音量键选中,然后按电源键确认,接着选中 Yes-install XXXXX.zip并确认开始刷机...
 
三星I879卡刷刷机教程(图文)

三星I879卡刷刷机教程(图文)

三星I879卡刷刷机教程(图文)

三星I879卡刷刷机教程(图文)
 
5:等待刷机完成,刷完之后返回到recovery主界面,然后选择reboot system now 重启手机就可以了。
 
三星I879卡刷刷机教程(图文)
 
6:刷机完成。

 

本文明来给各位同学介绍一下关于cordova cteate app 时下载失败无法成功创建的解决方法,有碰到此问题的同学可进入参考。

使用如下语句创建一个cordova应用:

 代码如下 复制代码
cordova -d create lzwmeapp com.lzw.lzwmeapp lzwmeapp

但是无法成功创建,提示错误如下:

 代码如下 复制代码
E:lzwme_app_android>cordova -d create lzwmeapp
Creating a new cordova project with name “HelloCordova” and id “io.cordova.hello
cordova” at location “E:lzwme_app_androidlzwmeapp”
Using stock cordova hello-world application.
Requesting {“uri”:”https://git-wip-us.apache.org/repos/asf?p=cordova-app-hello-w
orld.git;a=snapshot;h=3.4.0;sf=tgz”}…
Downloading cordova library for www…
Error: connect ETIMEDOUT
at errnoException (net.js:904:11)
at Object.afterConnect [as oncomplete] (net.js:895:19)

明显这是下载 cordova hello world 文件失败。主要原因为网址 https://git-wip-us.apache.org/ 速度太慢,容易超时。
解决方法:
我们可以变更这个文件的下载地址,方法如下:
打开 cordova 目录下的platforms.js文件,修改其中的url配置地址。这里可以修改为git附件包下载地址,具体到官方github查找:
https://github.com/apache/
如这里主要修改了如下部分代码:

 代码如下 复制代码
‘android’ : {
parser : ‘./src/metadata/android_parser’,
//url : ‘https://git-wip-us.apache.org/repos/asf?p=cordova-android.git’,
url : ‘https://github.com/apache/cordova-android/archive/3.4.0.tar.gz?’,
version: ’3.4.0′
},
‘www’:{
hostos : [],
//url : ‘https://git-wip-us.apache.org/repos/asf?p=cordova-app-hello-world.git’,
url : ‘https://github.com/apache/cordova-app-hello-world/archive/3.4.0.tar.gz?’,
version: ’3.4.0′
}

其他部分的url也可参照修改。
当然,你也可以手动下载对应的压缩包,放到本地服务器,然后修改为相应文件的下载地址。

标签:[!--infotagslink--]

您可能感兴趣的文章: