編輯:關於Android編程
在之前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(); } } } }
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); }
可以看到com.digissin.twelve的CPU占用大幅降低了,從而達到了我們的目的,在解決這個問題的同時,我也給大家說一個
常犯的錯誤,並且以代碼和注釋的形式給大家看清楚
創建不必要的新實例:
在一些進度條更新或者上傳下載數據等情況,我們通常需要對UI進行跟新之類的,這就涉及子線程跟Handler的交互,需要
我們不停地向Handler發送Message 對象,這時候就易犯這個錯誤,如下:
@Override public 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); }
@Override public 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(); // 來自 MessagePool msg.what=MainSessionUtil.SEND_POST_BYETS_DATA; bundle.putByteArray(MainSessionUtil.BYTES_DATA_KEY, _data);// 裝新的水(填充新的數據源) msg.setData(bundle); handler.sendMessage(msg); }
本文使用Android Studio開發。獲取定位信息相對簡單,我們只需要如下幾步:第一步,注冊百度賬號,在百度地圖開放平台新建應用、生成API_KEY。這些就不細說了,
添加公用單元文件com..java package com.example.myapplication;import android.a
談到android事件處理,最復雜的就是對Touch事件的處理,因為Touch事件包括:down, move, up, cancle和多點觸摸等多種情況,多點觸摸的情況先
這裡僅以獲取sim卡的IMSI接口(getSubscriberId)和發短信接口(sendTextMessage)為例來詳細講解一下Android5.0-6.0雙卡適配的