閱讀《阿里巴巴Android開發手冊1.0.1》筆記

背景

2018春節餘味還沒有消,阿里巴巴爲移動開發者們準備了一份遲到的新年禮物——《阿里巴巴Android開發手冊》1.0.1版本。android

在此寫下個人閱讀筆記,記錄下本身平時沒有注意的一些問題,規範本身。數據庫

正文

1.【強制】Activity 間經過隱式 Intent 的跳轉,在發出 Intent 以前必須經過 resolveActivity 檢查,避免找不到合適的調用組件,形成 ActivityNotFoundException 的異常。

public void viewUrl(String url, String mimeType) {
	Intent intent = new Intent(Intent.ACTION_VIEW);
	intent.setDataAndType(Uri.parse(url), mimeType);
	if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ ONLY) != null) {
		startActivity(intent);
	} else {
		// 找不到指定的 Activity
	}
}
複製代碼

2.【強制】避免在 BroadcastReceiver#onReceive()中執行耗時操做,若是有耗時工做, 應該建立 IntentService 完成,而不該該在 BroadcastReceiver 內建立子線程去作。

說明:安全

因爲該方法是在主線程執行,若是執行耗時操做會致使 UI 不流暢。可使用IntentService 、 創 建 HandlerThread 或 者 調 用 Context#registerReceiver (BroadcastReceiver, IntentFilter, String, Handler)方法等方式,在其餘 Wroker 線程執行 onReceive 方法。BroadcastReceiver#onReceive()方法耗時超過 10 秒鐘,可能會被系統殺死。bash

3.【 推 薦 】 添 加 Fragment 時 , 確 保 FragmentTransaction#commit() 在 Activity#onPostResume()或者 FragmentActivity#onResumeFragments()內調用。 不要隨意使用 FragmentTransaction#commitAllowingStateLoss()來代替,任何 commitAllowingStateLoss()的使用必須通過 code review,確保無負面影響。

說明:app

Activity 可能由於各類緣由被銷燬,Android 支持頁面被銷燬前經過Activity#onSaveInstanceState() 保 存 自 己 的 狀 態 。 但 如 果FragmentTransaction.commit()發生在 Activity 狀態保存以後,就會致使 Activity 重 建、恢復狀態時沒法還原頁面狀態,從而可能出錯。爲了不給用戶形成很差的體驗,系統會拋出 IllegalStateExceptionStateLoss 異常。推薦的作法是在 Activity 的onPostResume() 或 onResumeFragments() ( 對 FragmentActivity ) 裏 執 行 FragmentTransaction.commit(),若有必要也可在 onCreate()裏執行。不要隨意改用FragmentTransaction.commitAllowingStateLoss() 或 者 直 接 使 用 try-catch 避 免 crash,這不是問題的根本解決之道,當且僅當你確認 Activity 重建、恢復狀態時,本次 commit 丟失不會形成影響時纔可這麼作。ide

4.【推薦】不要在 Activity#onDestroy()內執行釋放資源的工做,例如一些工做線程的 銷燬和中止,由於 onDestroy()執行的時機可能較晚。可根據實際須要,在 Activity#onPause()/onStop()中結合 isFinishing()的判斷來執行。

5.【推薦】老是使用顯式Intent啓動或者綁定Service,且不要爲服務聲明IntentFilter, 保證應用的安全性。若是確實須要使用隱式調用,則可爲 Service 提供 Intent Filter 並從 Intent 中排除相應的組件名稱,但必須搭配使用 Intent#setPackage()方法設置 Intent 的指定包名,這樣能夠充分消除目標服務的不肯定性。

6.【推薦】對於只用於應用內的廣播,優先使用 LocalBroadcastManager 來進行註冊 和發送,LocalBroadcastManager 安全性更好,同時擁有更高的運行效率。

說明:工具

對於使用 Context#sendBroadcast()等方法發送全局廣播的代碼進行提示。若是該廣播僅用於應用內,則可使用 LocalBroadcastManager 來避免廣播泄漏以及廣播被攔截等安全問題,同時相對全局廣播本地廣播的更高效。佈局

7.【推薦】當前 Activity 的 onPause 方法執行結束後纔會建立(onCreate)或恢復 (onRestart)別的 Activity,因此在 onPause 方法中不適合作耗時較長的工做,這 會影響到頁面之間的跳轉效率。

8.【推薦】文本大小使用單位 dp,View 大小使用單位 dp。對於 TextView,若是在文 字大小肯定的狀況下推薦使用 wrap_content 佈局避免出現文字顯示不全的適配問 題。

說明:post

之因此文本大小也推薦使用 dp 而非 sp,由於 sp 是 Android 早期推薦使用的,但其 實 sp 不只和 dp 同樣受屏幕密度的影響,還受到系統設置裏字體大小的影響,因此使用 dp 對於應用開發會更加保證 UI 的一致性和還原度。字體

9.【推薦】使用 Toast 時,建議定義一個全局的 Toast 對象,這樣能夠避免連續顯示 Toast 時不能取消上一次 Toast 消息的狀況。即便須要連續彈出 Toast,也應避免直 接調用 Toast#makeText。

10.【強制】線程池不容許使用 Executors 去建立,而是經過 ThreadPoolExecutor 的方 式,這樣的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。

說明:

Executors 返回的線程池對象的弊端以下:

  1. FixedThreadPool 和 SingleThreadPool : 允 許 的 請 求 隊 列 長 度 爲Integer.MAX_VALUE,可能會堆積大量的請求,從而致使 OOM;

  2. CachedThreadPool 和 ScheduledThreadPool : 允 許 的 創 建 線 程 數 量 爲Integer.MAX_VALUE,可能會建立大量的線程,從而致使 OOM。

正例:

int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); 

int KEEP_ALIVE_TIME = 1;

TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;

BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>(); 

ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES, NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,
taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());

複製代碼

反例:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

複製代碼

11.【推薦】ThreadPoolExecutor 設置線程存活時間(setKeepAliveTime),確保空閒時 線程能被釋放.

12. 【推薦】禁止在多進程之間用 SharedPreferences 共享數據,雖然能夠 (MODE_MULTI_PROCESS),但官方已不推薦。

13. 【強制】任什麼時候候不要硬編碼文件路徑,請使用 Android 文件系統 API 訪問。

說明:

Android 應用提供內部和外部存儲,分別用於存放應用自身數據以及應用產生的用 戶數據。能夠經過相關 API 接口獲取對應的目錄,進行文件操做。

  1. android.os.Environment#getExternalStorageDirectory()

  2. android.os.Environment#getExternalStoragePublicDirectory()

  3. android.content.Context#getFilesDir()

  4. android.content.Context#getCacheDir

正例:

public File getDir(String alName) {
	File file = new File(Environment.getExternalStoragePublicDirectory(Environment. 		DIRECTORY_PICTURES), alName);
	if (!file.mkdirs()) {
		Log.e(LOG_TAG, "Directory not created");
	}
	return file;
}

複製代碼

反例:

public File getDir(String alName) {
	// 任什麼時候候都不要硬編碼文件路徑,這不只存在安全隱患,也讓 app 更容易出現適配問題 
	File file = new File("/mnt/sdcard/Download/Album", alName);
	if (!file.mkdirs()) {
		Log.e(LOG_TAG, "Directory not created");
	}
	return file;
}

複製代碼

14.【強制】當使用外部存儲時,必須檢查外部存儲的可用性

正例:

// 讀/寫檢查
public boolean isExternalStorageWritable() {
	String state = Environment.getExternalStorageState();
	if (Environment.MEDIA_MOUNTED.equals(state)) { 
		return true;
	}
	return false;
	}
// 只讀檢查
public boolean isExternalStorageReadable() {
	String state = Environment.getExternalStorageState();
	if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
		return true;
	}
	return false; 
}

複製代碼

15.【強制】應用間共享文件時,不要經過放寬文件系統權限的方式去實現,而應使用 FileProvider。

16.【強制】若是 ContentProvider 管理的數據存儲在 SQL 數據庫中,應該避免將不受 信任的外部數據直接拼接在原始 SQL 語句中。

???這是個什麼梗,都沒說清楚???

正例:

// 使用一個可替換參數
String mSelectionClause = "var = ?"; String[] selectionArgs = {""}; selectionArgs[0] = mUserInput;

複製代碼

反例:

// 拼接用戶輸入內容和列名
String mSelectionClause = "var = " + mUserInput;

複製代碼

17.【強制】png 圖片使用 TinyPNG 或者相似工具壓縮處理,減小包體積。

18.【推薦】應根據實際展現須要,壓縮圖片,而不是直接顯示原圖。手機屏幕比較小,直接顯示原圖,並不會增長視覺上的收益,可是卻會耗費大量寶貴的內存。

正例:

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);
}

複製代碼

19.【強制】在 Activity#onPause()或 Activity#onStop()回調中,關閉當前 activity 正在執 行的的動畫。

正例:

public class MyActivity extends Activity {
        ImageView mImageView;
        Animation mAnimation;
        Button mBtn;

        @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);
                }
            };
        }

        @Override
        public void onPause() {
            //頁面退出,及時清理動畫資源
            mImageView.clearAnimation();
        }
    }

複製代碼

20.【推薦】使用 RGB_565 代替 RGB_888,在不怎麼下降視覺效果的前提下,減小內存佔用。

說明:

android.graphics.Bitmap.Config 類中關於圖片顏色的存儲方式定義:

  1. ALPHA_8 表明 8 位 Alpha 位圖;

  2. ARGB_4444 表明 16 位 ARGB 位圖;

  3. ARGB_8888 表明 32 位 ARGB 位圖;

  4. RGB_565 表明 8 位 RGB 位圖。

位圖位數越高,存儲的顏色信息越多,圖像也就越逼真。大多數場景使用的是ARGB_8888 和 RGB_565,RGB_565 可以在保證圖片質量的狀況下大大減小內存的開銷,是解決 OOM 的一種方法。

可是必定要注意 RGB_565 是沒有透明度的,若是圖片自己須要保留透明度,那麼就不能使用 RGB_565。

正例:

Config config = drawableSave.getOpacity() != PixelFormat.OPAQUE ? 	Config.ARGB_8565 : Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(w, h, config);

複製代碼

反例:

Bitmap newb = Bitmap.createBitmap(width, height, Config.ARGB_8888);

複製代碼

21.【推薦】在有強依賴 onAnimationEnd 回調的交互時,如動畫播放完畢才能操做頁面,onAnimationEnd 可能會因各類異常沒被回調(參考: https://stackoverflow.com/questions/5474923/onanimationend-is-not-getting-calle d-onanimationstart-works-fine ), 建 議 加 上 超 時 保 護 或 通 過 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);

複製代碼

22.【推薦】當 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);

複製代碼

總結

說真的,這手冊總結得挺好的,雖然內容少了點,可是才1.0.1版本,還會繼續修改完善的。

我以爲上面的第8點寫得不太合理:

8.【推薦】文本大小使用單位 dp,View 大小使用單位 dp。對於 TextView,若是在文 字大小肯定的狀況下推薦使用 wrap_content 佈局避免出現文字顯示不全的適配問 題。

說明:

之因此文本大小也推薦使用 dp 而非 sp,由於 sp 是 Android 早期推薦使用的,但其 實 sp 不只和 dp 同樣受屏幕密度的影響,還受到系統設置裏字體大小的影響,因此使用 dp 對於應用開發會更加保證 UI 的一致性和還原度。

我以爲:若是用戶設置了系統字體大小,那麼確定是但願系統總體字體變大或變小,而你的APP卻不怎麼變,這看起來一來不協調,二來沒有達到用戶修改系統字體大小的目的,感受這樣的作法有點破壞系統的生態,不推薦這樣作。

相關文章
相關標籤/搜索