android上传位置信息导致的流量大爆炸问题调查
原由:项目中有人写了个位置上传的服务,其实一直没问题,后来不知道什么时候出现了很多抱怨,是开着app流量一下子跑掉了几个G,差点就要卖房子还移动话费了,很多同事哭笑不得的找上门来,后来PM解决了,我一直没时间弄明白,今天终于还原了这个大Bug,解决后才发现,要细心那,很多问题有可能测试都测不出来的,好的产品真是不容易啊,从产品到开发到测试都需要慎之又慎。我还差得远,积累吧。
解决问题的过程就是不断百度的过程啊。还是先贴几个总结的比较好的Blog吧。
1. eclipse的输出不够用的,利用adb logcat的命令输出到文件。
adb logcat -f > D:\log.txt
adb logcat TAG:D *:S -f > D:\log.txt
-f 不好用的话试试-d。要好好设置过滤,否则出来一堆没用的log,我的有时候会不成功,还是Ubunt下的grep好用。
2. DDMS的Thread查看:说实话,信息太少,我没怎么用到
3.异常信息
06-23 16:35:59.045: W/System.err(14651): android.os.NetworkOnMainThreadException
06-23 16:35:59.050: W/System.err(14651): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1148)
06-23 16:35:59.050: W/System.err(14651): at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)
06-23 16:35:59.050: W/System.err(14651): at libcore.io.IoBridge.connectErrno(IoBridge.java:144)
06-23 16:35:59.050: W/System.err(14651): at libcore.io.IoBridge.connect(IoBridge.java:112)
06-23 16:35:59.050: W/System.err(14651): at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
06-23 16:35:59.050: W/System.err(14651): at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:459)
06-23 16:35:59.050: W/System.err(14651): at java.net.Socket.connect(Socket.java:843)
06-23 16:35:59.050: W/System.err(14651): at org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:119)
06-23 16:35:59.050: W/System.err(14651): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:144)
06-23 16:35:59.050: W/System.err(14651): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
06-23 16:35:59.050: W/System.err(14651): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
06-23 16:35:59.050: W/System.err(14651): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
06-23 16:35:59.050: W/System.err(14651): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:583)
06-23 16:35:59.050: W/System.err(14651): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:506)
06-23 16:35:59.050: W/System.err(14651): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:484)
06-23 16:35:59.055: W/System.err(14651): at com.cmcc.wepa.util.NetworkManager.httpConnectOpt(NetworkManager.java:185)
06-23 16:35:59.055: W/System.err(14651): at com.cmcc.wepa.home.UploadLocationThread.run(UploadLocationThread.java:90)
06-23 16:35:59.055: W/System.err(14651): at com.cmcc.wepa.location.LocationUploadService.upload(LocationUploadService.java:141)
06-23 16:35:59.055: W/System.err(14651): at com.cmcc.wepa.location.LocationUploadService.access$0(LocationUploadService.java:94)
06-23 16:35:59.055: W/System.err(14651): at com.cmcc.wepa.location.LocationUploadService$1.handleMessage(LocationUploadService.java:159)
06-23 16:35:59.055: W/System.err(14651): at android.os.Handler.dispatchMessage(Handler.java:102)
06-23 16:35:59.055: W/System.err(14651): at android.os.Looper.loop(Looper.java:136)
06-23 16:35:59.055: W/System.err(14651): at android.app.ActivityThread.main(ActivityThread.java:5314)
06-23 16:35:59.055: W/System.err(14651): at java.lang.reflect.Method.invokeNative(Native Method)
06-23 16:35:59.055: W/System.err(14651): at java.lang.reflect.Method.invoke(Method.java:515)
06-23 16:35:59.055: W/System.err(14651): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:862)
06-23 16:35:59.055: W/System.err(14651): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:678)
06-23 16:35:59.055: W/System.err(14651): at dalvik.system.NativeStart.main(Native Method)
网络连接一直报这个异常,其实以前遇到过没仔细看没仔细想,以后可记住了AndroidBlockGuardPolicy.onNetwork,碰到他的话很有可能是因为把网络请求防导弹主线程了。Android现在的版本是不允许将网络请求这样的耗时操作放到主线程。
4.好了,差不多了,来两个低级错误,我犯过,今天是逮到同事犯了,害我找了一下午bug都没发现这个问题,否则也不会有上面的3了。
1). 写了线程调用忘了开始,也就是没有.start();
2). 在主线程了傻乎乎的把.start()写错为.run(),这不是调用类的方法吗,根本没开启线程,相当于主线程执行,不出上面的问题3才怪。
5.还有service的启动,显示启动的话OnCreate是执行一次的,多次start也是,但是每次start都会执行OnStart(),因此把什么操作放在哪个方法里要想清楚。
@Override
public void onStart(Intent intent, int startId)
{
// TODO Auto-generated method stub
super.onStart(intent, startId);
Log.i("TEST", "onStart ");
task = new TimerTask()
{
@Override
public void run()
{
// TODO Auto-generated method stub
Message msg = mHandler.obtainMessage();
msg.what = 0;
mHandler.sendMessage(msg);
}
}; timer.schedule(task, 0, 30000); }
像上面这个启动计时器任务放在了onsStart中,我觉得不是太好,因为别的页面这个service会被多次显示启动,这样没有必要每次都新建这个task并放在计时任务中了。话说,如果真是这样做了,下回再执行到这个函数的时候,原来的线程任务是等执行完已经开始的就over了吧?不能立即终止吧?现在还没有能力验证。这样猜想好像蛮合理的呵呵,等待高手指教。
6.最后就是我原本要解决的问题:问题5中的代码是启动位置上传的,当然下面的代码现在已经不用了因为这个写法自找麻烦了,直接上传就好了,干嘛还要用handler
呢。又不关心上传的返回值。不过,研究bug这个还是不错的。
上面的timer定时发送message到handler,然后handler判断what字段后进行upload上传。
private Handler mHandler = new Handler(){ @Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
super.handleMessage(msg);
Log.i("TEST", "handleMessage msg.what= " + msg.what);
if(msg.what == 0)
{
//Log.i("TEST", "handleMessage ");
upload();
} }
};
本来看起来没问题,上传函数中会调用一个上传的Thread进行子线程操作。这个子线程代码
public class UploadLocationThread extends Thread
{ private Handler handler;
private String memberId; // 用户id
private String memLng; // 经度
private String menLat; // 纬度 public UploadLocationThread()
{
} public UploadLocationThread(Handler handler, String memberId,
String memLng, String menLat)
{
super();
this.handler = handler;
this.memberId = memberId;
this.memLng = memLng;
this.menLat = menLat;
} public String getMemberId()
{
return memberId;
} public void setMemberId(String memberId)
{
this.memberId = memberId;
} public String getMemLng()
{
return memLng;
} public void setMemLng(String memLng)
{
this.memLng = memLng;
} public String getMenLat()
{
return menLat;
} public void setMenLat(String menLat)
{
this.menLat = menLat;
} // 上传位置信息
@Override
public void run()
{
super.run(); HashMap<String, String> param = new HashMap<String, String>();
param.put("userId", memberId);
param.put("userLng", memLng);
param.put("userLat", menLat);
Message msg = handler.obtainMessage();
Log.i("TEST", "UploadLocationThread msg " + msg.what);
// 获取返回
try
{
String result = NetworkManager.getInstance().httpConnectOpt(
Constant.URL_UPDATE_LOCATION, param); JSONObject obj = JSON.parseObject(result);
Log.i("TEST", "UploadLocationThread result= " + result);
Log.i("TEST", "UploadLocationThread obj= " + obj);
String code = obj.getString("code");
Log.i("TEST", "UploadLocationThread code " + code);
if (code.equals("0"))
{
msg.what = 1;
} else
{
msg.what = -1;
} } catch (Exception e)
{
e.printStackTrace();
}
Log.i("TEST", "UploadLocationThread msg.what end = " + msg.what);
handler.sendMessage(msg);
} }
好了,问题来了,以前没有问题是正常情况,后来出问题了是因为:
线程中Message msg = handler.obtainMessage();这个初始值logcat打印会发现,msg.what的初始值是0,然而后面的上传网络链接异常情况根本也没处理这个值,所以异常的场合handler.sendMessage(msg);就把msg.what = 0,给传递到主线程中了,回去看task中的代码,也是msg.what=0时调用update这个没问题,但是下面的mHandler定义也是msg.what=0时调用上传线程,这样timer的task中正常上传之外,每当线程上传有异常的情况可能就陷入死循环了,因为handler收到的timer和上传线程返回的msg.what全是0。再多的流量也不能这么玩啊。
所以,对自己特殊的业务,msg.what的值最好是离默认值远一点啊。还有,handler的机制搞清楚,主线程收到的handler的机制等等。还有就是异常处理一定要飞各种情况想清楚。
所以子线程上传结果没必要用handler发msg反馈了。主线程只处理timer的就可以,而且最好改成100,200这样的值吧。
android上传位置信息导致的流量大爆炸问题调查的更多相关文章
- android上传文件到服务器
package com.spring.sky.image.upload.network; import java.io.DataOutputStream; import java.io.File; i ...
- 【经验记录】Android上传文件到服务器
Android中实现上传文件,其实是很简单的,和在java里面是一样的,基本上都是熟悉操作输出流和输入流!还有一个特别重要的就是需要配置content-type的一些参数!如果这些都弄好了,上传就很简 ...
- Android上传文件到服务器(转)
Android中实现上传文件,其实是很简单的,和在java里面是一样的,基本上都是熟悉操作输出流和输入流!还有一个特别重要的就是需要配置content-type的一些参数!如果这些都弄好了,上传就很简 ...
- android 上传文件
android对于上传文件,还是非常easy的,和java里面的上传都是一样的,基本上都是熟悉操作输出流和输入流!另一个特别重要的就是须要一些content-type这些參数的配置! 假设这些都弄好 ...
- javaweb中上传视频,并且播放,用上传视频信息为例
1.上传视频信息的jsp页面uploadVideo.jsp <body background="image/bk_hero.jpg"><div id=" ...
- Android 上传开源项目到 jcenter 实战踩坑之路
本文微信公众号「AndroidTraveler」首发. 背景 其实 Android 上传开源项目到 jcenter 并不是一件新鲜事,网上也有很多文章. 包括我本人在将开源项目上传到 jcenter ...
- Android开发(二十五)——Android上传文件至七牛
设置头像: Drawable drawable = new BitmapDrawable(dBitmap); //Drawable drawable = Drawable.createFromPath ...
- android -上传文件到服务器
android上传文件到服务器 重点:最好是设置好content-type这些参数的配置! package com.spring.sky.image.upload.network; ...
- javaweb中上传图片并显示图片,用我要上传课程信息(里面包括照片)这个例子说明
原理: 从客户端上传到服务器 照片——文件夹——数据库 例如:桌面一张照片,在tomacat里创建upload文件夹,把桌面照片上传到upload文件夹里,并且把照片的 ...
随机推荐
- 每天一道题:LeetCode
本人是研二程旭猿一枚,还有半年多就要找工作了,想想上一年度面试阿里的算法工程师挂了,心有不甘啊,主要还是准备不足,对一些常见的算法问题没有去组织准备,为了明年找一份好的实习,就从现在开始,好好准备吧, ...
- OC运行时和方法机制笔记
在OC当中,属性是对字段的一种特殊封装手段. 在编译期,编译器会将对字段的访问替换为内存偏移量,实质是一种硬编码. 如果增加一个字段,那么对象的内存排布就会改变,需要重新编译才行. OC的做法是,把实 ...
- HipHop算法:利用微博互动关系挖掘社交圈
/* 版权声明:可以任意转载,转载时请务必标明文章原始出处和作者信息 .*/ CopyMiddle: 张俊林 TimeStamp:2012年3 月 在微博环境下,如何 ...
- 浅谈android的selector,背景选择器
shape和selector的结合使用 (2013-04-07 11:11:00) 转载▼ 分类: android 1.Shape (1)作用:XML中定义的几何形状 (2)位置:res/draw ...
- SQL Server 备份维护计划
1. 创建维护计划:SSMS -> 管理 -> 维护计划 -> 新建维护计划 2. 添加子计划(备份计划) a) 每30分钟:事务日志备份 每天:差异备份 每周:完整备份 b) ...
- NSThread 的用法
一.线程的注意点: 1.不要同时开太多的线程(最多不要超过5条,其中包括主线程) 2.线程概念 2.1.主线程:UI 线程,显示.刷新 UI 界面,处理 UI 控件的事件 2.2.子线程:后台线程,异 ...
- 漫谈AOP开发之初探AOP及AspectJ的用法
一.为什么需要AOP技术 AOP 是一个很成熟的技术. 假如项目中有方法A.方法B.方法C……等多个方法, 如果项目需要为方法A.方法B.方法C……这批方法增加具有通用性质的横切处理. 下图可以形 ...
- 在linux中限制用户ftp访问权限
1.环境:redhat linux企业版4.ftp为vsftp.被限制用户名为aaa.被限制路径为/bbb.2.建用户:在root用户下,相继进行如下操作 adduser aaa ...
- Android 数据库ORM框架GreenDao学习心得及使用总结<二>
转:http://blog.csdn.net/xushuaic/article/details/24496191 第五篇 查询 查询会返回符合某些特定标准的实体.你可以使用原始的SQL定制查询语句,或 ...
- -webkit-filter是神马?
这些滤镜效果最初是用于SVG的,W3C引入到CSS中,然后制定了CSS Filter Effects 1.0,现在webkit率先支持了它. 现在规范中支持的效果有: - grayscale ...