閱讀《阿里巴巴Android開發(fā)手冊1.0.1》筆記

背景

2018春節(jié)余味尚未消态辛,阿里巴巴為移動開發(fā)者們準(zhǔn)備了一份遲到的新年禮物——《阿里巴巴Android開發(fā)手冊》1.0.1版本起惕。

在此寫下我的閱讀筆記,記錄下自己平時沒有注意的一些問題廷支,規(guī)范自己秦士。

正文

1.【強(qiáng)制】Activity 間通過隱式 Intent 的跳轉(zhuǎn),在發(fā)出 Intent 之前必須通過 resolveActivity 檢查,避免找不到合適的調(diào)用組件,造成 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.【強(qiáng)制】避免在 BroadcastReceiver#onReceive()中執(zhí)行耗時操作,如果有耗時工作, 應(yīng)該創(chuàng)建 IntentService 完成,而不應(yīng)該在 BroadcastReceiver 內(nèi)創(chuàng)建子線程去做。

說明:

由于該方法是在主線程執(zhí)行,如果執(zhí)行耗時操作會導(dǎo)致 UI 不流暢伍宦⊙克溃可以使用IntentService 、 創(chuàng) 建 HandlerThread 或 者 調(diào) 用 Context#registerReceiver (BroadcastReceiver, IntentFilter, String, Handler)方法等方式,在其他 Wroker 線程執(zhí)行 onReceive 方法次洼。BroadcastReceiver#onReceive()方法耗時超過 10 秒鐘,可能會被系統(tǒng)殺死关贵。

3.【 推 薦 】 添 加 Fragment 時 , 確 保 FragmentTransaction#commit() 在 Activity#onPostResume()或者 FragmentActivity#onResumeFragments()內(nèi)調(diào)用。 不要隨意使用 FragmentTransaction#commitAllowingStateLoss()來代替,任何 commitAllowingStateLoss()的使用必須經(jīng)過 code review,確保無負(fù)面影響卖毁。

說明:

Activity 可能因?yàn)楦鞣N原因被銷毀,Android 支持頁面被銷毀前通過Activity#onSaveInstanceState() 保 存 自 己 的 狀 態(tài) 揖曾。 但 如 果FragmentTransaction.commit()發(fā)生在 Activity 狀態(tài)保存之后,就會導(dǎo)致 Activity 重 建落萎、恢復(fù)狀態(tài)時無法還原頁面狀態(tài),從而可能出錯。為了避免給用戶造成不好的體驗(yàn),系統(tǒng)會拋出 IllegalStateExceptionStateLoss 異常炭剪。推薦的做法是在 Activity 的onPostResume() 或 onResumeFragments() ( 對 FragmentActivity ) 里 執(zhí) 行 FragmentTransaction.commit(),如有必要也可在 onCreate()里執(zhí)行练链。不要隨意改用FragmentTransaction.commitAllowingStateLoss() 或 者 直 接 使 用 try-catch 避 免 crash,這不是問題的根本解決之道,當(dāng)且僅當(dāng)你確認(rèn) Activity 重建、恢復(fù)狀態(tài)時,本次 commit 丟失不會造成影響時才可這么做奴拦。

4.【推薦】不要在 Activity#onDestroy()內(nèi)執(zhí)行釋放資源的工作,例如一些工作線程的 銷毀和停止,因?yàn)?onDestroy()執(zhí)行的時機(jī)可能較晚媒鼓。可根據(jù)實(shí)際需要,在 Activity#onPause()/onStop()中結(jié)合 isFinishing()的判斷來執(zhí)行错妖。

5.【推薦】總是使用顯式Intent啟動或者綁定Service,且不要為服務(wù)聲明IntentFilter, 保證應(yīng)用的安全性绿鸣。如果確實(shí)需要使用隱式調(diào)用,則可為 Service 提供 Intent Filter 并從 Intent 中排除相應(yīng)的組件名稱,但必須搭配使用 Intent#setPackage()方法設(shè)置 Intent 的指定包名,這樣可以充分消除目標(biāo)服務(wù)的不確定性。

6.【推薦】對于只用于應(yīng)用內(nèi)的廣播,優(yōu)先使用 LocalBroadcastManager 來進(jìn)行注冊 和發(fā)送,LocalBroadcastManager 安全性更好,同時擁有更高的運(yùn)行效率暂氯。

說明:

對于使用 Context#sendBroadcast()等方法發(fā)送全局廣播的代碼進(jìn)行提示潮模。如果該廣播僅用于應(yīng)用內(nèi),則可以使用 LocalBroadcastManager 來避免廣播泄漏以及廣播被攔截等安全問題,同時相對全局廣播本地廣播的更高效。

7.【推薦】當(dāng)前 Activity 的 onPause 方法執(zhí)行結(jié)束后才會創(chuàng)建(onCreate)或恢復(fù) (onRestart)別的 Activity,所以在 onPause 方法中不適合做耗時較長的工作,這 會影響到頁面之間的跳轉(zhuǎn)效率痴施。

8.【推薦】文本大小使用單位 dp,View 大小使用單位 dp擎厢。對于 TextView,如果在文 字大小確定的情況下推薦使用 wrap_content 布局避免出現(xiàn)文字顯示不全的適配問 題。

說明:

之所以文本大小也推薦使用 dp 而非 sp,因?yàn)?sp 是 Android 早期推薦使用的,但其 實(shí) sp 不僅和 dp 一樣受屏幕密度的影響,還受到系統(tǒng)設(shè)置里字體大小的影響,所以使用 dp 對于應(yīng)用開發(fā)會更加保證 UI 的一致性和還原度辣吃。

9.【推薦】使用 Toast 時,建議定義一個全局的 Toast 對象,這樣可以避免連續(xù)顯示 Toast 時不能取消上一次 Toast 消息的情況动遭。即使需要連續(xù)彈出 Toast,也應(yīng)避免直 接調(diào)用 Toast#makeText。

10.【強(qiáng)制】線程池不允許使用 Executors 去創(chuàng)建,而是通過 ThreadPoolExecutor 的方 式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險齿尽。

說明:

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

  1. FixedThreadPool 和 SingleThreadPool : 允 許 的 請 求 隊(duì) 列 長 度 為Integer.MAX_VALUE,可能會堆積大量的請求,從而導(dǎo)致 OOM;

  2. CachedThreadPool 和 ScheduledThreadPool : 允 許 的 創(chuàng) 建 線 程 數(shù) 量 為Integer.MAX_VALUE,可能會創(chuàng)建大量的線程,從而導(dǎo)致 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 設(shè)置線程存活時間(setKeepAliveTime),確钡平冢空閑時 線程能被釋放.

12. 【推薦】禁止在多進(jìn)程之間用 SharedPreferences 共享數(shù)據(jù),雖然可以 (MODE_MULTI_PROCESS),但官方已不推薦循头。

13. 【強(qiáng)制】任何時候不要硬編碼文件路徑,請使用 Android 文件系統(tǒng) API 訪問。

說明:

Android 應(yīng)用提供內(nèi)部和外部存儲,分別用于存放應(yīng)用自身數(shù)據(jù)以及應(yīng)用產(chǎn)生的用 戶數(shù)據(jù)炎疆】睿可以通過相關(guān) API 接口獲取對應(yīng)的目錄,進(jìn)行文件操作。

  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 更容易出現(xiàn)適配問題 
    File file = new File("/mnt/sdcard/Download/Album", alName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

14.【強(qiáng)制】當(dāng)使用外部存儲時,必須檢查外部存儲的可用性

正例:

// 讀/寫檢查
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.【強(qiáng)制】應(yīng)用間共享文件時,不要通過放寬文件系統(tǒng)權(quán)限的方式去實(shí)現(xiàn),而應(yīng)使用 FileProvider形入。

16.【強(qiáng)制】如果 ContentProvider 管理的數(shù)據(jù)存儲在 SQL 數(shù)據(jù)庫中,應(yīng)該避免將不受 信任的外部數(shù)據(jù)直接拼接在原始 SQL 語句中全跨。

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

正例:

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

反例:

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

17.【強(qiáng)制】png 圖片使用 TinyPNG 或者類似工具壓縮處理,減少包體積亿遂。

18.【推薦】應(yīng)根據(jù)實(shí)際展示需要,壓縮圖片,而不是直接顯示原圖浓若。手機(jī)屏幕比較小,直接顯示原圖,并不會增加視覺上的收益,但是卻會耗費(fèi)大量寶貴的內(nèi)存。

正例:

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);
    // 然后根據(jù)圖片分辨率以及我們實(shí)際需要展示的大小,計算壓縮率 
    options.inSampleSize =  calculateInSampleSize(options, reqWidth, reqHeight); 
    // 設(shè)置壓縮率,并解碼
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

19.【強(qiáng)制】在 Activity#onPause()或 Activity#onStop()回調(diào)中,關(guān)閉當(dāng)前 activity 正在執(zhí) 行的的動畫蛇数。

正例:

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,在不怎么降低視覺效果的前提下,減少內(nèi)存占用挪钓。

說明:

android.graphics.Bitmap.Config 類中關(guān)于圖片顏色的存儲方式定義:

  1. ALPHA_8 代表 8 位 Alpha 位圖;
  1. ARGB_4444 代表 16 位 ARGB 位圖;
  1. ARGB_8888 代表 32 位 ARGB 位圖;
  1. RGB_565 代表 8 位 RGB 位圖。

位圖位數(shù)越高,存儲的顏色信息越多,圖像也就越逼真耳舅。大多數(shù)場景使用的是ARGB_8888 和 RGB_565,RGB_565 能夠在保證圖片質(zhì)量的情況下大大減少內(nèi)存的開銷,是解決 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.【推薦】在有強(qiáng)依賴 onAnimationEnd 回調(diào)的交互時,如動畫播放完畢才能操作頁面,onAnimationEnd 可能會因各種異常沒被回調(diào)(參考: https://stackoverflow.com/questions/5474923/onanimationend-is-not-getting-calle d-onanimationstart-works-fine ), 建 議 加 上 超 時 保 護(hù) 或 通 過 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.【推薦】當(dāng) View Animation 執(zhí)行結(jié)束時,調(diào)用 View.clearAnimation()釋放相關(guān)資源馏予。

正例:

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

總結(jié)

說真的天梧,這手冊總結(jié)得挺好的,雖然內(nèi)容少了點(diǎn)霞丧,但是才1.0.1版本呢岗,還會繼續(xù)修改完善的。

我覺得上面的第8點(diǎn)寫得不太合理:

8.【推薦】文本大小使用單位 dp,View 大小使用單位 dp蛹尝。對于 TextView,如果在文 字大小確定的情況下推薦使用 wrap_content 布局避免出現(xiàn)文字顯示不全的適配問 題敷燎。

說明:

之所以文本大小也推薦使用 dp 而非 sp,因?yàn)?sp 是 Android 早期推薦使用的,但其 實(shí) sp 不僅和 dp 一樣受屏幕密度的影響,還受到系統(tǒng)設(shè)置里字體大小的影響,所以使用 dp 對于應(yīng)用開發(fā)會更加保證 UI 的一致性和還原度。

我覺得:如果用戶設(shè)置了系統(tǒng)字體大小箩言,那么肯定是希望系統(tǒng)整體字體變大或變小硬贯,而你的APP卻不怎么變,這看起來一來不協(xié)調(diào)陨收,二來沒有達(dá)到用戶修改系統(tǒng)字體大小的目的饭豹,感覺這樣的做法有點(diǎn)破壞系統(tǒng)的生態(tài),不推薦這樣做务漩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拄衰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子饵骨,更是在濱河造成了極大的恐慌翘悉,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件居触,死亡現(xiàn)場離奇詭異妖混,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)轮洋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門制市,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人弊予,你說我怎么就攤上這事祥楣。” “怎么了汉柒?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵误褪,是天一觀的道長。 經(jīng)常有香客問我碾褂,道長兽间,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任斋扰,我火速辦了婚禮渡八,結(jié)果婚禮上啃洋,老公的妹妹穿的比我還像新娘。我一直安慰自己屎鳍,他們只是感情好宏娄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逮壁,像睡著了一般孵坚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窥淆,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天卖宠,我揣著相機(jī)與錄音,去河邊找鬼忧饭。 笑死扛伍,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的词裤。 我是一名探鬼主播刺洒,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吼砂!你這毒婦竟也來了逆航?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤渔肩,失蹤者是張志新(化名)和其女友劉穎因俐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體周偎,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抹剩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了栏饮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吧兔。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡磷仰,死狀恐怖袍嬉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情灶平,我是刑警寧澤伺通,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站逢享,受9級特大地震影響罐监,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瞒爬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一弓柱、第九天 我趴在偏房一處隱蔽的房頂上張望沟堡。 院中可真熱鬧,春花似錦矢空、人聲如沸航罗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粥血。三九已至,卻和暖如春酿箭,著一層夾襖步出監(jiān)牢的瞬間复亏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工缭嫡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缔御,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓妇蛀,卻偏偏與公主長得像刹淌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子讥耗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,185評論 25 707
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程有勾,因...
    小菜c閱讀 6,426評論 0 17
  • 白馬藍(lán)籌股持續(xù)調(diào)整,臨近年底古程,市場震蕩蔼卡,但我們認(rèn)為中期向好趨勢并沒有改變,價值藍(lán)籌整體并未高估挣磨。投資者短期可...
    陳悅_Cyue閱讀 185評論 0 0
  • 這幾天不吵架和好了雇逞,我越來越愛你了,愛到無法自拔了快茁裙。 今兒晚上和你朋友們一起玩兒塘砸,因?yàn)橛心愣_心,而且不知怎地炒...
    撕裂的光線閱讀 199評論 0 0
  • 如果命運(yùn)是一條孤獨(dú)的河流晤锥,誰會是你靈魂的擺渡人掉蔬? 今天是情人節(jié),對于我來說矾瘾,沒什么特別的感受女轿。而在...
    箬茶閱讀 743評論 0 3