Android Application Thread CPU GC Operatiing and OOM Question 0603-隨手筆記,operatiing0603-

來源:互聯網
上載者:User

Android Application Thread CPU GC Operatiing and OOM Question 0603-隨手筆記,operatiing0603-

在之前app寫完測試的時候,跑完整個老化階段包括資料收發都沒問題,鍵入 adb shell top -m 5  發現我的 app pid 佔用的 

CPU是最多的,其實我想說寫一個app是不難,你又沒有全面的分析app的記憶體佔用?避免一些OOM之類的問題,和其他可

能帶來的一些偶發性問題,這些估計很多小夥伴都沒考慮,沒事,今天就給大夥說說這方面的東西,雖說不是什麼高難度的

知識點,但最重要的是養成這種習慣,才能在後續的開發中減少不必要的時間浪費,下面我就帶大家怎麼發現並且解決問

題,一步一步分析


首先看看 我們的app cpu 佔用情況:


我們可以看到 com.digissin.twelve 這個進程是一直排在第一位的,這個就是我們測試的進程,下面我帶小夥伴們怎麼發現問

題,並且及時糾正


首先我們要分析,為什麼CPU 佔用會那麼高?是不是在主線程或者子線程做了耗時操作,網路操作,new 的執行個體對象過多?

帶著這個疑問,我們看看DDMS並且分析下:



查看 com.digissin.twelve.RSUDPProtocol&PostBytesThread 134 行代碼:



死迴圈讀取狀態導致的,但又不能去掉這個死迴圈,因為app需要這個死迴圈來給服務端進行通訊,只要非意外情況,app是

一直和後台保持通訊的!當有資料傳過來,isPause 會被設成true,代碼流程就會走到if裡面,一旦發完一條資料報,

isPause false while 就用進入了空死迴圈,不幹任何事情,且頻率很快的迴圈執行,如果我們在這個死迴圈裡面調用sleep()

雖然能成功,但是很顯然它是與app需求背道而馳的,所以必須排除,因為一旦進入sleep() 線程就不幹活了,來自主線成的

協議分發的資料報發送就沒任何意義了!所以這個方法就不可取了


所以我很快想到了一個辦法,就是當isPause false 的時候,我們就不需要子線程工作,那很簡單,我只需要讓他休眠,一旦

有來自協議分發過來的資料報,我們就wakeup 讓子線程繼續工作,那就非 wait() 和 notify() 莫屬了 首先區分 Thread 和 

Object 的 這兩個東西裡面的 wait() 和 notify() ,源碼分析太笼統了,我給大家舉例子分析

在Thread 裡直接調用這2兩個函數是不會起作用的,我們需要建立一個Object對象來管理子線程的暫停和繼續,意思就是說

子線程相當於一個普通員工,被new 出來的Object對象相當於一個管理者,員工要做什麼需要管理者來通知和告知,即使員

工知道自己下一步該幹什麼想幹什麼,都需要管理者的允許才行!員工也沒法自己獨立出來,就是不能自己做自己的事情,

否則整個管理員模式會亂套,所以我們必須建立Object對象來對子線程做這個暫停和繼續的控制著


所以我給這個內部類線程加 synchronized 欄位,並且添加執行個體化靜態方法,來建立這個Object(PostBytesThread)執行個體對象

別且給出暫停和繼續函數:

        private static PostBytesThread mThreadInstance = null;                  public synchronized static PostBytesThread getThreadInstance() {              if (mThreadInstance == null) {              mThreadInstance = new PostBytesThread();              }              return mThreadInstance;          }        public synchronized boolean isPause() {return isPause;}public synchronized void setPause(boolean isPause) {this.isPause = isPause;}public byte[] getPost_bytes() {return post_bytes;}public void setPost_bytes(byte[] post_bytes) {this.post_bytes = post_bytes;}public synchronized void onThreadPause(){try {Log.e(TAG, TAG+" onThreadPause() ----");this.wait();} catch (InterruptedException e) {Log.i(TAG, e.toString());}}public synchronized void onThreadResume(){Log.e(TAG, TAG+" onThreadResume() ----");this.notify();}    @Override    public void run() {           if(udpSocket == null){            Log.i(TAG, TAG+" udpSocket is null");            return;            }        while(true){        Log.i(TAG, TAG+" isPause() state:"+isPause());        if(isPause()){        try {        sendPacket.setData(getPost_bytes());        sendPacket.setLength(getPost_bytes().length);        sendPacket.setAddress(serverAddress);        sendPacket.setPort(DEFAULT_POTR);        udpSocket.send(sendPacket);        Thread.sleep(1000);        setPause(false);        } catch (InterruptedException e) {        Log.i(TAG, "Exception:"+e.toString());        } catch (IOException e) {        Log.i(TAG, "Exception:"+e.toString());        }        }else{        onThreadPause();        }}    }    }

調用方式,回調介面收到來自主線程的協議訊息資料包分發,並開始工作,當然只是為了方便大家觀看,其實start()方法不用發在這裡,因為這個同步對象只有在子線程消亡才會被回收,所以相當於每次都多判斷了一次這個同步對象的執行個體情況了

    public void setPostBytesData(byte[] data){    PostBytesThread.getThreadInstance().start();    PostBytesThread.getThreadInstance().onThreadResume();    PostBytesThread.getThreadInstance().setPause(true);    PostBytesThread.getThreadInstance().setPost_bytes(data);    boolean isPause = PostBytesThread.getThreadInstance().isPause();    Log.d("PostBytesThread", "PostBytesThread  isPause() state:"+isPause);    }

處理完這段代碼後我們繼續查看 cpu的佔用情況:


 

可以看到com.digissin.twelve的CPU佔用大幅降低了,從而達到了我們的目的,在解決這個問題的同時,我也給大家說一個

常犯的錯誤,並且以代碼和注釋的形式給大家看清楚

建立不必要的新執行個體:

在一些進度條更新或者上傳下載資料等情況,我們通常需要對UI進行跟新之類的,這就涉及子線程跟Handler的互動,需要

我們不停地向Handler發送Message 對象,這時候就易犯這個錯誤,如下:

@Overridepublic void run() {while(true){try {SettingLocationTime();} catch (InterruptedException e) {e.printStackTrace();}}}private void SettingLocationTime() throws InterruptedException{if(handler!=null){SendMessage(post_data);time = setting_time>0?setting_time:default_time;//Log.i(TAG, TAG+" SettingLocationTime() time:"+time);Thread.sleep(time*1000);}}/** * 這個函數會在run while(true)裡面一直跑 * Message\Bundle會被不停的建立新執行個體對象 * 所以這是個極低的錯誤!也是致命的! * */private void SendMessage(byte[]data){byte[]_data=ByteParseBeanTools.PostProtocolByte(ByteProtocolSessionType.LOCATION_STATE_SEND, data);Message msg = new Message();  // 不必要的 Message 新執行個體對象msg.what=MainSessionUtil.SEND_POST_BYETS_DATA;Bundle bundle = new Bundle(); // 不必要的 Bundle 新執行個體對象bundle.putByteArray(MainSessionUtil.BYTES_DATA_KEY, _data);msg.setData(bundle);handler.sendMessage(msg);}

解決方案:

@Overridepublic void run() {while(true){try {SettingLocationTime();} catch (InterruptedException e) {e.printStackTrace();}}}private void SettingLocationTime() throws InterruptedException{if(handler!=null){SendMessage(post_data);time = setting_time>0?setting_time:default_time;//Log.i(TAG, TAG+" SettingLocationTime() time:"+time);Thread.sleep(time*1000);}}/** * 可以把Bundle放在class被載入的地方,執行個體化這個對象 * 裝載完一次資料之後,下次調用之前執行clear()函數即可,此時的bundle對象就相當於一個鐵碗 * 每次裝不同的水而已,就避免了每次開闢新的記憶體空間來存放Bundle對象 * Message 對象就更簡單了,因為我這類回調了一個Handler對象過來,我們可以直接 * 調用Handler對象的obtainMessage()函數,這個函數當Handler被建立時,不管你用不用,它都在那裡 * 隨Handler消亡而消亡,不需要執行個體化,不需要建立,可以直接取出來用,這又避免了每次開闢新的記憶體空間 * 來裝載Message對象,obtainMessage() 函數 來自 MessagePool * **/private void SendMessage(byte[]data){bundle.clear();// 倒掉碗裡的老水(清空之前的緩衝),裝新來的水(填充來自回呼函數的新資料)byte[]_data=ByteParseBeanTools.PostProtocolByte(ByteProtocolSessionType.LOCATION_STATE_SEND, data);Message msg = handler.obtainMessage();  // 來自 MessagePoolmsg.what=MainSessionUtil.SEND_POST_BYETS_DATA;bundle.putByteArray(MainSessionUtil.BYTES_DATA_KEY, _data);// 裝新的水(填充新的資料來源)msg.setData(bundle);handler.sendMessage(msg);}

這樣CPU佔用問題就能大幅降低,從而問題也能得到解決!


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.