以下內容摘自 阿里巴巴Android開發手冊
我們的目標是:
- 防患未然,提升質量意識,降低故障率和維護成本;
- 標準統一,提升協作效率;
- 追求卓越的工匠精神,打磨精品程式碼。
- 【強制】必須遵守,違反本約定或將會引起嚴重的後果;
- 【推薦】儘量遵守,長期遵守有助於系統穩定性和合作效率的提升;
- 【參考】充分理解,技術意識的引導,是個人學習、團隊溝通、專案合作的方向。
阿里Android開發規範:資原始檔命名與使用規範
阿里Android開發規範:四大基本元件
阿里Android開發規範:UI 與佈局
阿里Android開發規範:程式、執行緒與訊息通訊
阿里Android開發規範:檔案與資料庫
阿里Android開發規範:Bitmap、Drawable 與動畫
阿里Android開發規範:安全與其他
1、【強制】載入大圖片或者一次性載入多張圖片,應該在非同步執行緒中進行。圖片的載入,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡頓。
正例:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// 在後臺進行圖片解碼
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = BitmapFactory.decodeFile("some path");
return bitmap;
}
...
}
複製程式碼
反例:
Button btnLoadImage = (Button) findViewById(R.id.btn);
btnLoadImage.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
Bitmap bitmap = BitmapFactory.decodeFile("some path");
}
});
複製程式碼
2、【強制】在 ListView,ViewPager,RecyclerView,GirdView 等元件中使用圖片時,應做好圖片的快取,避免始終持有圖片導致記憶體洩露,也避免重複建立圖片,引起效能問題 。 建議使用 Fresco 、 Glide 等圖片庫。
正例:
例如使用系統 LruCache 快取,參考:developer.android.com/topic/perfo…
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// 獲取可用記憶體的最大值,使用記憶體超出這個值將丟擲 OutOfMemory 異常。LruCache 通過建構函式傳入快取值,以 KB 為單位。
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 把最大可用記憶體的 1/8 作為快取空間
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
...
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
} else {
mImageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
task.execute(resId);
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// 在後臺進行圖片解碼
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(getResources(),
params[0], 100, 100));
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
...
}
複製程式碼
反例:
沒有儲存,每次都需要解碼,或者有快取但是沒有合適的淘汰機制,導致快取效果很差,依然經常需要重新解碼。
3、【強制】png 圖片使用 tinypng 或者類似工具壓縮處理,減少包體積。
4、【推薦】應根據實際展示需要,壓縮圖片,而不是直接顯示原圖。手機螢幕比較小,直接顯示原圖,並不會增加視覺上的收益,但是卻會耗費大量寶貴的記憶體。
正例:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,int reqWidth, int reqHeight) {
// 首先通過 inJustDecodeBounds=true 獲得圖片的尺寸
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// 然後根據圖片解析度以及我們實際需要展示的大小,計算壓縮率
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 設定壓縮率,並解碼
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
複製程式碼
反例:
不經壓縮顯示原圖。
5、【強制】使用完畢的圖片,應該及時回收,釋放寶貴的記憶體。
正例:
Bitmap bitmap = null;
loadBitmapAsync(new OnResult(result){
bitmap = result;
});
...使用該 bitmap...
// 使用結束,在 2.3.3 及以下需要呼叫 recycle()函式,在 2.3.3 以上 GC 會自動管理,除非你明確不需要再用。
if (Build.VERSION.SDK_INT <= 10) {
bitmap.recycle();
}
bitmap = null;
複製程式碼
反例:
使用完成圖片,始終不釋放資源。
6、【推薦】針對不同的螢幕密度,提供對應的圖片資源,使記憶體佔用和顯示效果達到合理的平衡。如果為了節省包體積,可以在不影響 UI 效果的前提下,省略低密度圖片。
7、【強制】在 Activity.onPause()或 Activity.onStop()回撥中,關閉當前 activity 正在執行的的動畫。
正例
public class MyActivity extends Activity {
ImageView mImageView;
Animation mAnimation;
Button mBtn;
/** 首次建立 activity 時呼叫 */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mImageView = (ImageView)findViewById(R.id.ImageView01);
mAnimation = AnimationUtils.loadAnimation(this, R.anim.anim);
mBtn= (Button)findViewById(R.id.Button01);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mImageView.startAnimation(mAnimation);
}
});
}
public void onPause() {
//頁面退出,及時清理動畫資源
mImageView.clearAnimation()
}
}
複製程式碼
反例:
頁面退出時,不關閉該頁面相關的動畫。
8、【推薦】在動畫或者其他非同步任務結束時,應該考慮回撥時刻的環境是否還支援業務處理。例如 Activity 的 onStop()函式已經執行,且在該函式中主動釋放了資源,此時回撥中如果不做判斷就會空指標崩潰。
正例:
public class MyActivity extends Activity {
private ImageView mImageView;
private Animation mAnimation;
/** 首次建立 activity 時呼叫 */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mImageView = (ImageView)findViewById(R.id.ImageView01);
mAnimation = AnimationUtils.loadAnimation(this, R.anim.anim);
mAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
//判斷一下資源是否被釋放了
if (mImageView != null) {
mImageView.clearAnimation();
}
}
});
mImageView.startAnimation(mAnimation);
}
}
複製程式碼
反例:
動畫結束回撥中,直接使用資源不加判斷,導致異常。
9、【推薦】使用 inBitmap 重複利用記憶體空間,避免重複開闢新記憶體。
正例:
public static Bitmap decodeSampledBitmapFromFile(String filename, int reqWidth, intreqHeight, ImageCache cache) {
final BitmapFactory.Options options = new BitmapFactory.Options();
...
BitmapFactory.decodeFile(filename, options);
...
// 如果在 Honeycomb 或更新版本系統中執行,嘗試使用 inBitmap
if (Utils.hasHoneycomb()) {
addInBitmapOptions(options, cache);
}
...
return BitmapFactory.decodeFile(filename, options);
}
private static void addInBitmapOptions(BitmapFactory.Options options,ImageCache cache) {
// inBitmap 只處理可變的點陣圖,所以強制返回可變的點陣圖
options.inMutable = true;
if (cache != null) {
Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
if (inBitmap != null) {
options.inBitmap = inBitmap;
}
}
}
複製程式碼
10、【推薦】使用 ARGB_565 代替 ARGB_888,在不怎麼降低視覺效果的前提下,減少記憶體佔用。 說明: android.graphics.Bitmap.Config 類中關於圖片顏色的儲存方式定義:
- ALPHA_8 代表 8 位 Alpha 點陣圖;
- ARGB_4444 代表 16 位 ARGB 點陣圖;
- ARGB_8888 代表 32 位 ARGB 點陣圖;
- RGB_565 代表 8 位 RGB 點陣圖。
點陣圖位數越高,儲存的顏色資訊越多,影象也就越逼真。大多數場景使用的是ARGB_8888 和 RGB_565,RGB_565 能夠在保證圖片質量的情況下大大減少記憶體的開銷,是解決 oom 的一種方法。但是一定要注意 RGB_565 是沒有透明度的,如果圖片本身需要保留透明度,那麼就不能使用 RGB_565。
正例:
Config config = drawableSave.getOpacity() != PixelFormat.OPAQUE ? Config.ARGB_8888 :
Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
複製程式碼
反例:
Bitmap newb = Bitmap.createBitmap(width, height, Config.ARGB_8888);
複製程式碼
擴充套件參考:
11、【推薦】儘量減少 Bitmap (BitmapDrawable)的使用,儘量使用純色(ColorDrawable)、漸變色(GradientDrawable)、StateSelector(StateListDrawable)等與 Shape 結合的形式構建繪圖。
12、【推薦】謹慎使用 gif 圖片,注意限制每個頁面允許同時播放的 gif 圖片,以及單個gif 圖片的大小。
13、【參考】大圖片資源不要直接打包到 apk,可以考慮通過檔案倉庫遠端下載,減小包體積。
14、【推薦】根據裝置效能,選擇性開啟複雜動畫,以實現一個整體較優的效能和體驗;
15、【推薦】在有強依賴 onAnimationEnd 回撥的互動時,如動畫播放完畢才能操作頁面, onAnimationEnd 可能會因各種異常沒被回撥(參考:stackoverflow.com/questions/5…),建議加上超時保護或通過 postDelay 替代onAnimationEnd。
正例:
View v = findViewById(R.id.xxxViewID);
final FadeUpAnimation anim = new FadeUpAnimation(v);
anim.setInterpolator(new AccelerateInterpolator());
anim.setDuration(1000);
anim.setFillAfter(true);
new Handler().postDelayed(new Runnable() {
public void run() {
if (v != null) {
v.clearAnimation();
}
}
}, anim.getDuration());
v.startAnimation(anim);
複製程式碼
16、【推薦】當 View Animation 執行結束時,呼叫 View.clearAnimation()釋放相關資源。
正例:
View v = findViewById(R.id.xxxViewID);
final FadeUpAnimation anim = new FadeUpAnimation(v);
anim.setInterpolator(new AccelerateInterpolator());
anim.setDuration(1000);
anim.setFillAfter(true);
anim.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationEnd(Animation arg0) {
//判斷一下資源是否被釋放了
if (v != null) {
v.clearAnimation();
}
}
});
v.startAnimation(anim);
複製程式碼
阿里Android開發規範:資原始檔命名與使用規範
阿里Android開發規範:四大基本元件
阿里Android開發規範:UI 與佈局
阿里Android開發規範:程式、執行緒與訊息通訊
阿里Android開發規範:檔案與資料庫
阿里Android開發規範:Bitmap、Drawable 與動畫
阿里Android開發規範:安全與其他