標籤:
Android 效能調優
TPS(TransactionPer Second)每秒處理的事物數,是系統輸送量的指標。回應時間,使用者操作開始到系統給使用者正確反饋的時間。一般包括系統處理時間+網路傳輸時間+展現時間
同步改非同步
耗時操作放線上程中執行防止佔用主線程,一定程度上解決anr,注意線程和service結合(防止activity被回收後線程也被回收)以及線程的數量。
線程池特點:
1.重用存在的線程,減少對象建立,消亡的開銷
2.刻有效控制最大並發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞
3.提供定時執行,定期執行,單線程,並發數控制等功能
四種線程池:
1. newCachedThreadPool建立一個可以緩衝線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程,pool.execute(runnale)
2. newFiaedThreadPool建立一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待,定長線程池的大小最好根據系統資源進行設定如Runtime.getRuntime().availableProcessors(), pool.execute(runnable)
3. newScheduledThreadPool建立一個定長線程池,支援定時及週期性任務執行
定時執行pool.schedule(runnable,3, TimeUnit.SECONDS);
定期執行pool.scheduledAtFixedRate(runnable,1, 3, TimeUnit.SECONDS),延遲一秒後每三秒執行一次,比Timer更安全
4. newSingleThreadExecutor建立一個單線程化的線程池,他只會用唯一的背景工作執行緒來執行任務,保證所有任務按照指定順序(FIFO,LIFO,優先順序)執行
緩衝
Java的對象建立需要分配資源較消耗時間,加上建立的對象越多會造成越頻繁的gc影響系統響應。主要使用單例模式、緩衝(圖片緩衝、線程池、View緩衝、IO緩衝、訊息緩衝、通知欄notification緩衝)及其它方式減少對象建立。
單例模式
私人化建構函式,定義靜態instance對象和getInstance()方法,getInstance()方法需要使用同步鎖synchronized(Singleton.class)防止多線程同時進入造成instance多次執行個體化,判斷是否執行個體化不必每次執行synchronized擷取對象鎖。
public classSingleton{
private static Singleton instance =null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance ==null){
instance = newSingleton();
}
}
}
}
}
圖片緩衝
ImageCache
ImageSdCache
View緩衝
ListView緩衝機制,通過convertView是否為空白減少layout inflate次數,通過Viewholder減少findviewbyid的次數,inflate很費時。
@Override
public ViewgetView(int position, View convertView, ViewGroup parent){
ViewHolder holder;
if(convertView == null){
convertView =inflater.inflate(R.layout.list_item, null);
holder = newViewHolder();
convertView.setTag(holder);
}else{
holder =(ViewHolder)convertView.getTag();
}
}
private staticclass ViewHolder{
private ImageView appIcon;
private TextView appName;
}
IO緩衝
使用具有緩衝策略的輸入資料流,BufferedInputStream替代InputStream,BufferedRead替代Reader, BufferedReader替代BufferedInputStream,對檔案,網路IO皆適用。
訊息緩衝
通過handler的obtainMessage回收Message對象,減少Message對象的建立開銷hander.sendMessaage(handler.obtainMessage(1))。
通知欄緩衝
下載中需要不斷改變通知欄進度條狀態,如果不斷建立Notification會導致通知欄很卡,Map<Sting, Notifiction> notificationMap = newHashMap<String, Notification>();如果notificationMap中不存在,則建立notification並put into map。
其他
能建立基類解決問題就不用具體子類:除需要設定優先權的線程使用new Thread建立外,其餘線程使用new Runnable, 因為子類會有自己的屬性建立需要更多開銷
控制最大並發數量:使用java的Executors類,通過Executors.newFixedThreadPool(nThreads)控制線程池最大線程並發。
http請求增加timeout,資料庫緩衝http response ,根據http投資訊中得Cache-Control域確定緩衝到期時間。
頻繁訪問,或一次訪問消耗較大的資料緩衝。
Layout
使用抽象布局標籤(include, viewstub, merge)去除不必要的嵌套和view節點。減少不必要的inflate,布局調優相關工具(hierarchy viewer 和 lint)。
TextView屬性最佳化 android:ellipsize=”marquee”跑馬燈效果極耗效能。
a. 抽象布局標籤:
<include>布局模組化
<includelayout=”@layout/layout.xml”>
<viewstub>同include標籤一樣引入外部布局,viewstub引入的布局預設不會擴張,即不會佔用顯示也不會佔用位置,常用作引入預設不會顯示,只在特殊情況下顯示(如進度布局,網路失敗顯示的重新整理布局,資訊出錯的提示布局)。
ViewStub stub =(ViewStub)findViewById(R.id.network_error_layout);
ViewnetworkErrorView = stub.inflate();
ButtonnetworkSetting = (Button)networkErrorView.findViewById(R.id.network_setting);
<merge>布局頂節點是FrameLayout且不需要設定background或padding等屬性,可以用merge代替,某布局作為子布局被其他布局include(外層為RelativeLayout)時,使用merge當作該布局節點。
b. 去除不必要的嵌套和View節點,首次不需要使用的節點設定GONE或者使用Viewstub, LinearLayout比例時層級較深使用RelativeLayout。
c. 減少不必要的inflate
上面stub.inflate(),可以先判斷networkErrorView是否為空白。
d. 用SurfaceView或TextureView代替普通View,SurfaceView或TextureView可以通過將繪圖操作移動到另外一個單獨線程上提高效能,普通View繪製過程都是在主線程(UI線程)中完成,SurfaceView在常規視圖系統之外,所以無法像常規視圖一樣移動,縮放或旋轉,TextureVIew不僅在單獨線程繪製,還可以像常規視圖一樣操作。
e. 盡量為所有解析度建立資源,減少不必要的硬體縮放,會降低UI的繪製速度。
GPU過渡繪製
過渡繪製是指一個像素被繪製了多次。
原因主要有:
1. 太多的View疊加
2. 複雜的層級疊加
3. 更長的inflation
顏色標識: 從好到差:藍-綠-淡紅-紅
1. 藍色1x過度繪製
2. 綠色2x過度繪製
3. 淡紅色3x過度繪製
4. 紅色超過4x過度繪製
驗收標準:
1. 控制過度繪製為2x
2. 不允許存在4x過度繪製
3. 不允許存在面積超過螢幕1/4地區的3x過度繪製(淡紅色地區)
資料庫
主要包括索引和事物及針對Sqlite的最佳化:
1. 索引,加快了資料庫檢索的速度,難維護
2. 事務,原子提交,同一事務內的修改要麼完成要麼失敗
3. 語句拼接使用StringBuilder代替String
4. 查詢時返回更少的結果集及更少的欄位
5. 少用cursor.getColumnIndex,建表時使用static 變數記住某列的index,
6. 非同步線程,並發,大小,網路局限性,並且為表級鎖,不用多線程操作,資料量大時也會有延時卡頓,使用單線程池,在任務中執行db操作,通過handler返回結果和ui線程互動。
演算法
使用hashMap代替arrayList
使用SpqrseArray代替key為int型的hashmap
HanshMap迴圈,既需要key也需要value
Map<Srting, String> map = newHashMap<String, String>();
for(Entry<String, String> entry :map.entrySet()){
entry.getKey();
entry.getValue();
}
只遍曆key
for(String key : map.keySet()){}
順延強制
對於很多耗時邏輯沒必要立即執行,可以將其順延強制;
//線程順延強制
ScheduledExecutorServicescheduledThreadPool = Executors.newScheduledThreadPool(10);
//訊息延遲發送
handler.sendMessageDelayed(handler.obtainMessage(0),1000);
網路最佳化
a. 圖片必須緩衝,最好根據機型做圖片適配
b. 所有的http請求必須添加httptimeout
c. 開啟gzip壓縮
d. 介面資料以json格式返回,而不是xml或html
e. 根據http頭資訊中得Cache-Control及expires域確定是否緩衝請求結果
f. 確定網路請求的connection是否keep-alive
g. 減少網路請求次數,伺服器端適當做請求合并
h. 減少重新導向次數
i.介面伺服器端回應時間不超過100ms
其他Bitmap
在Java的設計中沒有釋放記憶體這個概念的,但實際使用中,大對象除外。Android中的Bitmap就是這麼一個例外。Bitmap的不及時回收會造成記憶體的彪升。Recycle方法不是必須調用,就算調用了GC也不會立即回收java層的bitmap對象,這跟把一個對象手動置空一個道理。
Recycle方法就是把c層的像素記憶體給釋放。C層的記憶體還是算到當前進程的記憶體裡的,而構成一個bitmap對象的記憶體大部分都是c層的像素數組,所以,手動調用bitmap。Recycle並把bitmap對象的java層引用手動置null可以瞬間釋放像素所佔的記憶體,並讓虛擬機器下次運行時回收bitmap對象所佔的記憶體。
AlarmManager
不要不做限制地使用定時器,要根據情境選擇性地註冊並及時登出定時器。比如,依賴網路條件的應該使用SyncAdapter;使用者登出後,記得登出定時器等。另外,定時器的周期最好控制在15分鐘以上,這個是Android官方給出的經驗值,再頻繁的話,對待機電量消耗的影響就會明顯變大了。關於定時器的模式。wakeup的好處是可以對使用者關心的狀態及時更新,但會打斷機器休眠,如果很頻繁的話,會使用機器睡不下去,耗電量明顯上升。非wakeup的好處就是比較省電,但會把所有的任務集中到使用者喚醒機器的那一刹那來執行,從而造成使用者喚醒機器時的卡頓。
WakeLock
除了像視頻播放這種即使使用者長時間不操作也需要保持在前台的程式,實質上很少有申請Wake Lock的需求。另外,一旦程式退出前台,記得釋放。
Broadcast
通過AndroidManifest.xml靜態聲明的全域廣播接收器是耗電大戶。可能會有很多程式註冊像“網路狀態改變”、“電話狀態”、“設定解鎖”等常見事件。那麼,在這些事件發生時會觸發多個進程建立,頻繁的GC,卡頓也就來了。所以,請盡量使用動態註冊。另外,“網路狀態改變”可以使用Android提供的SyncAdapter來實現低成本的監聽。
Receiver的進程配置也有些技巧。如果Receiver需要喚醒UI進程,就把它和UI配置到一個進程當中,如果需要喚醒後台服務,就把它和後台服務配置到一個進程當中。可以減少系統建立進程的開銷。
後台服務
把後台服務單獨做為一個進程,可以大大減少UI進程的記憶體壓力。如果把它們放到一個進程中,只要它們中的任意一個沒有完成任務,它們倆都會一直存在於記憶體中。
長串連
長串連意味著需要保持網路連接、周期性的心跳資料轉送、裝置無法休眠,使用需謹慎。
其他
64位類型long, double的處理比32位int慢
final類型儲存在常量區中讀取效率更高
LocalBroadcastManager代替普通BroadcastReceiver,效率和安全性都更高
android_效能調優