編輯:關於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使用百度地圖SDK獲取定位信息示例
本文使用Android Studio開發。獲取定位信息相對簡單,我們只需要如下幾步:第一步,注冊百度賬號,在百度地圖開放平台新建應用、生成API_KEY。這些就不細說了,
Android安卓---Hellword 簡單提示框
添加公用單元文件com..java package com.example.myapplication;import android.a
android事件處理機制
談到android事件處理,最復雜的就是對Touch事件的處理,因為Touch事件包括:down, move, up, cancle和多點觸摸等多種情況,多點觸摸的情況先
Android如何適配雙卡
這裡僅以獲取sim卡的IMSI接口(getSubscriberId)和發短信接口(sendTextMessage)為例來詳細講解一下Android5.0-6.0雙卡適配的