安卓性能Performance
Android性能
- 內存
- UI流暢度
================================
數據體現
- 內存 ( 佔用多少 泄露多少)
- UI流暢度度 每秒幀數 響應時間
- IO 阻塞式響應時間
================================
安卓內存緊張引起問題
- OOM (Out Of Memory) 引起GC
- UI不流暢
- …….
Android內存緊張引起的原因:
- 單例模式下static數據
- 多線程進程週期過長 導致佔有的資源遲遲不能回收
- 大胖子 bitmap
- Cursor 索引
解決:
- 及時銷燬不使用對象
- bitmap可設置縮放比例
- SoftReference 軟引用
- Cursor及時關閉
================================
Android內存情況工具
- DDMS ( HEAP堆 Allocation Tracker內存分配情況)
- MAT Momery Analyzer( Leak Suspect 內存泄露報告) (Top Components吃貨報告) (Histogram Class佔用內存) (Dominator tree 佔用內存較多對象 Hold了什麼導致不能釋放)
- android.os.Debug (自有API)
================================
安卓UI卡頓原因:
================================
垂直同步
http://www.jianshu.com/p/838835d21ea6
測評:對系統進行大量有針對性的測試,以得到合適的測試數據。
分析系統瓶頸:分析測試數據,找到其中的hotspot(熱點,即bottleneck 瓶頸)。
性能優化:對hotspot相關的代碼進行優化
Traceview簡介
Traceview是Android平臺特有的數據採集和分析工具,它主要用於分析Android中應用程序的hotspot。
Traceview本身只是一個數據分析工具,而數據的採集則需要使用Android SDK中的Debug類或者利用DDMS工具。
Traceview打點
Debug.startMethodTracing(String tracePath);
Debug.stopMethodTracing();
Systrace 打點
python systrace.py –time=10 -o mynewtrace.html -a[your-app-package-name] sched gfx view wm
Trace.beginSection(“tag”);
Trace.endSection;
================================
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
int mMemery = am.getMemoryClass(); // 以M為單位 一般256M 大於該大小 程序會崩潰
int mLagerMemery = am.getLargeMemoryClass(); // 以M為單位 一般256M 大於該大小 程序會崩潰
float totalMomery = Runtime.getRuntime().totalMemory() * 1.0f /( 1024 * 1024 );
float freeMomery = Runtime.getRuntime().freeMemory() * 1.0f /( 1024 * 1024 );
float maxMomery = Runtime.getRuntime().maxMemory() * 1.0f /( 1024 * 1024 );
adb shell dumpsys meminfo surfaceflinger 命令查看進程內存情況
adb shell dumpsys meminfo com.xxx.xxx.xxx 查看進程內存情況
【頻繁字符串拼接使用 StringBuffer】
package test;
import java.util.Random;
public class A {
int row = 30;
int length = 1000 ;
int[][] intArray = new int[row][length];
public A() {
begin();
showString();
showStringBuffer();
}
public static void main(String[] args) {
A a =new A();
}
private void showString() {
String str = "";
long time1 = System.currentTimeMillis();
for (int i = 0; i < row; i++) {
for (int j = 0; j < length; j++) {
str = str + intArray[i][j] ;
}
}
long time2 = System.currentTimeMillis();
System.out.println("String拼接運行了"+(time2-time1)/1000.0 + "秒");
}
private void showStringBuffer() {
StringBuffer str = new StringBuffer();
long time1 = System.currentTimeMillis();
for (int i = 0; i < row; i++) {
for (int j = 0; j < length; j++) {
str = str.append(intArray[i][j]+"");
}
}
long time2 = System.currentTimeMillis();
System.out.println("StringBuffer 拼接運行了"+(time2-time1)/1000.0 + "秒");
}
private void begin() {
Random ran = new Random();
for (int i = 0; i < row; i++) {
for (int j = 0; j < length; j++) {
intArray[i][j] = ran.nextInt(10000);
}
}
}
}
輸出:
String拼接運行了1.226秒
StringBuffer 拼接運行了0.009秒
package com.example.testa;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.ArrayMap;
/**
* 通過軟引用管理多張圖片的方法
*
*
*/
@SuppressLint("NewApi")
public class BitmapCache {
static private BitmapCache cache;
// 每次取bitmap 先查看該bitmap對應的string標誌是否在該MAP中
// 如果不在 說明被釋放掉了 或者第一次加載
private ArrayMap<String, MySoftRef> hashRefArayMap;
// 垃圾引用的隊列 所引用的對象已被收回 則將該引用加入該隊列
private ReferenceQueue<Bitmap> queue;
// 繼承 SoftReference 使得每一個引用都有唯一的標識
private class MySoftRef extends SoftReference<Bitmap> {
private String key = "";
public MySoftRef(Bitmap bitmap, ReferenceQueue<Bitmap> queue, String str) {
super(bitmap, queue);
//SoftReference(Bitmap , ReferenceQueue<Bitmap>) 原始構造器
//bitmap的軟引用被回收,會出現在隊列ReferenceQueue中 ,ReferenceQueue 保存了被回收的引用
key = str;
}
}
@SuppressLint("NewApi")
private BitmapCache() {
hashRefArayMap = new ArrayMap<String, MySoftRef>();
queue = new ReferenceQueue<Bitmap>();
}
static public BitmapCache getInstance() {
if (cache == null) {
cache = new BitmapCache();
}
return cache;
}
// 以軟引用的方式對Bitmap對象的實例進行引用 並保存該引用
public void addCacheBitmap(Bitmap bmp, String key) {
cleanCache(); // 清除垃圾引用
MySoftRef ref = new MySoftRef(bmp, queue, key);
hashRefArayMap.put(key, ref);
}
/**
* 依據所指定的drawable下資源ID (後者SD卡路徑 網絡路徑),獲取到bitmap對象的實例
*/
public Bitmap getBitmap(String resId) {
Bitmap bitmap = null;
try {
if(hashRefArayMap.containsKey(resId)){
MySoftRef ref = (MySoftRef) hashRefArayMap.get(resId);
bitmap = (Bitmap) ref.get();
if(bitmap != null)
return bitmap;
}
} catch (Exception e) {
return null;
}
// 如果沒有軟引用,或者索引的實例爲 null ,需要重新創建實例
// bitmap = BitmapFactory.decodeFile(resId);
// hashRefArayMap.put(resId, new MySoftRef(bitmap, queue, resId));
return bitmap;
}
private void cleanCache() { // 移除已經被回收的軟引用 使得 hashRefArayMap正常
MySoftRef ref = null;
// 系統清除軟引用 會把回收的東西放到 隊列裏 queue.poll()
/*在任何時候,我們都可以調用ReferenceQueue的poll()方法來檢查是否有它所關心的非強可及對象被回收。
* 如果隊列爲空,將返回一個null,否則該方法返回隊列中前面的一個Reference對象。
* 利用這個方法,我們可以檢查哪個SoftReference所軟引用的對象已經被回收。
* 於是我們可以把這些失去所軟引用的對象的SoftReference對象清除掉
* */
while ((ref = (MySoftRef) queue.poll()) != null){ // 隊列裏的軟引用不爲空 那麼說明這個引用被回收了
hashRefArayMap.remove(ref.key);
}
}
public void clearCache() { // 清除 BitmapCache 全部數據
cleanCache();
hashRefArayMap.clear();
System.gc();
System.runFinalization();
}
}
======================================
package com.example.testa;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import android.graphics.Bitmap;
//使用LRU算法, 比BitmapCache能達到更好的回收控制,BitmapCache無法保證當前取到的Bitmap爲空 是隨機的
// LRU算法能實現 最不常用的那個Bitmap能 首先被回收掉
// 使用LRU 算法 實現 Bitmap的緩存
public class MemoryCache {
private static final String TAG = "MemoryCache";
// true 表示 使用LRU算法 進行排序 8 表示當前最大容量 0.75表示容量因子 當容量大於75% 時,容量曾倍 最大容量變爲16
// 這個LinkedHashMap容器爲線程不安全的 所在在外面套一個 synchronizedMap 使得線程安全
private Map<String, Bitmap> cache = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(8, 0.75f, true));
private long size = 0; // 保存當前Map中已經保存了的字節數
private long limit = 1000000; // Max byte 容器最大字節
public void MemoryCache() {
// TODO Auto-generated constructor stub
setLimit(Runtime.getRuntime().maxMemory() / 4);
}
public void setLimit(long mLimit) {
// TODO Auto-generated constructor stub
limit = mLimit;
// 最大緩存設置爲 mLimit / (1024*1024) MB 大小
}
public Bitmap get(String id) {
try {
if (!cache.containsKey(id))
return null;
return cache.get(id);
} catch (Exception e) {
return null;
}
}
public void put(String id, Bitmap bitmap) {
try {
if (cache.containsKey(id)) // 檢查圖片是否存在 如果有的話 可能是更新 那麼更新大小
size -= getSizeInBytes(cache.get(id));
cache.put(id, bitmap);
size += getSizeInBytes(cache.get(id));
checkSize();
} catch (Exception e) {
}
}
public void clear() {
cache.clear();
}
private long getSizeInBytes(Bitmap bitmap) {
if (bitmap == null) {
return 0;
}
return bitmap.getRowBytes() * bitmap.getHeight();
}
private void checkSize() { // 確保 每次size 都小於 limit
// 迭代的原則是 從最近最少用的那個對象開始迭代
if (size > limit) {
Iterator<Map.Entry<String, Bitmap>> iter = cache.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Bitmap> entry = iter.next();
size -= getSizeInBytes(entry.getValue());
iter.remove(); // 在MAP中最少用的那個Bitmap就被釋放掉了
if (size <= limit)
break;
}
}
}
}