android上傳位置資訊導致的流量大爆炸問題調查

來源:互聯網
上載者:User

標籤:

原由:項目中有人寫了個位置上傳的服務,其實一直沒問題,後來不知道什麼時候出現了很多抱怨,是開著app流量一下子跑掉了幾個G,差點就要賣房子還移動話費了,很多同事哭笑不得的找上門來,後來PM解決了,我一直沒時間弄明白,今天終於還原了這個大Bug,解決後才發現,要細心那,很多問題有可能測試都測不出來的,好的產品真是不容易啊,從產品到開發到測試都需要慎之又慎。我還差得遠,積累吧。

解決問題的過程就是不斷百度的過程啊。還是先貼幾個總結的比較好的Blog吧。

1. eclipse的輸出不夠用的,利用adb logcat的命令輸出到檔案。

adb logcat 命令列用法

adb logcat -f > D:\log.txtadb logcat TAG:D *:S -f > D:\log.txt

-f 不好用的話試試-d。要好好設定過濾,否則出來一堆沒用的log,我的有時候會不成功,還是Ubunt下的grep好用。

2. DDMS的Thread查看:說實話,資訊太少,我沒怎麼用到

 Android的DDMS中的Threads的各個欄位的含義

3.異常資訊

06-23 16:35:59.045: W/System.err(14651): android.os.NetworkOnMainThreadException06-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)
View Code

網路連接一直報這個異常,其實以前遇到過沒仔細看沒仔細想,以後可記住了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);    }
View Code

像上面這個啟動計時器任務放在了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();            }        }    };
View Code

本來看起來沒問題,上傳函數中會調用一個上傳的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);    }}
View Code

好了,問題來了,以前沒有問題是正常情況,後來出問題了是因為:

線程中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上傳位置資訊導致的流量大爆炸問題調查

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.