開發(fā)手冊目前最新的為1.01版.本人閱讀的為正式版第一版1.0.其中許多技巧值得反復(fù)研讀,此文記錄本人認(rèn)為有價(jià)值的知識點(diǎn),部分知識點(diǎn)為延伸知識點(diǎn)
1.Activity & Fragment生命周期再回顧
上圖相比于平時(shí)接觸到的多了許多,簡單說幾個(gè):
1)onPostCreate和簸、onPostResume
onPostCreate是指onPostCreate方法是指onCreate方法徹底執(zhí)行完畢的回調(diào)亡电,onPostResume類似髓迎,那我們什么時(shí)候在可以使用這個(gè)呢。大家肯定遇到這種情況禁偎,在onCreate中獲取某個(gè)View的高度和寬度腿堤,發(fā)現(xiàn)獲取到的值是0,因?yàn)檫@個(gè)View可能還沒初始化好如暖,這時(shí)候比如我們在onPostResume中取獲取這個(gè)View的高和寬笆檀,因?yàn)閛nPostResume是指onResume徹底執(zhí)行完畢的回調(diào),所以這時(shí)候去獲取就可以了盒至。
2)onUserInteraction 酗洒,onUserLeaveHint
onUserInteraction我們可以用這個(gè)方法來監(jiān)控用戶有沒有與當(dāng)前的Activity進(jìn)行交互,那我們就可以針對這個(gè)來假設(shè)場景枷遂,有個(gè)APP要求N分鐘后用戶沒有進(jìn)行操作樱衷,那就自動(dòng)出來動(dòng)態(tài)壁紙,或者進(jìn)行鎖屏界面酒唉,或者跳到登錄界面重新登錄等箫老!那我們只需要寫個(gè)倒計(jì)時(shí),然后每次調(diào)用了onUserInteraction方法黔州,就把時(shí)間重置即可。阔籽。多方便A髌蕖!
我們再看onUserLeaveHint:
用戶手動(dòng)離開當(dāng)前activity笆制,會調(diào)用該方法绅这,比如用戶主動(dòng)切換任務(wù),短按home進(jìn)入桌面等在辆。系統(tǒng)自動(dòng)切換activity不會調(diào)用此方法证薇,如來電,滅屏等匆篓。
我們一般監(jiān)聽返回鍵浑度,肯定是重寫onKeyDown方法,但是Home鍵和Menu鍵就不好監(jiān)聽了鸦概。但是有了這個(gè)方法箩张。我們可以做統(tǒng)一的監(jiān)聽了。比如要監(jiān)聽用戶點(diǎn)了Home鍵跳回到桌面后。我們要求這個(gè)APP自動(dòng)跳轉(zhuǎn)到解鎖界面先慷。我們只要在這里做監(jiān)聽出來即可饮笛。
2.添 加 Fragment 時(shí) , 確 保 FragmentTransaction#commit() 在Activity#onPostResume()或者 FragmentActivity#onResumeFragments()內(nèi)調(diào)用。不 要 隨 意 使 FragmentTransaction#commitAllowingStateLoss() 來 代 替 , 任 何commitAllowingStateLoss()的使用必須經(jīng)過 code review,確保無負(fù)面影響论熙。
Activity 可 能 因 為 各 種 原 因 被 銷 毀 , Android 支 持 頁 面 被 銷 毀 前 通 過Activity#onSaveInstanceState()保存自己的狀態(tài)福青。但如果FragmentTransaction.commit()發(fā)生在 Activity 狀態(tài)保存之后,就會導(dǎo)致 Activity 重建、恢復(fù)狀態(tài)時(shí)無法還原頁面狀態(tài),從而可能出錯(cuò)脓诡。為了避免給用戶造成不好的體驗(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)時(shí),本次 commit 丟失不會造成影響時(shí)才可這么做指厌。
3.不要在 Activity#onDestroy()內(nèi)執(zhí)行釋放資源的工作,例如一些工作線程的銷 毀 和 停 止 , 因 為onDestroy() 執(zhí) 行 的 時(shí) 機(jī) 可 能 較 晚 。 可 根 據(jù) 實(shí) 際 需 要 , 在Activity#onPause()/onStop()中結(jié)合 isFinishing()的判斷來執(zhí)行踊跟。
4.總是使用顯式 Intent 啟動(dòng)或者綁定 Service,且不要為服務(wù)聲明 Intent Filter,保證應(yīng)用的安全性踩验。如果確實(shí)需要使用隱式調(diào)用,則可為 Service 提供 Intent Filter并從 Intent 中排除相應(yīng)的組件名稱,但必須搭配使用 Intent#setPackage()方法設(shè)置Intent 的指定包名,這樣可以充分消除目標(biāo)服務(wù)的不確定性。
5.當(dāng)前 Activity 的 onPause 方法執(zhí)行結(jié)束后才會執(zhí)行下一個(gè) Activity 的 onCreate方法,所以在 onPause 方法中不適合做耗時(shí)較長的工作,這會影響到頁面之間的跳轉(zhuǎn)效率商玫。
6.【強(qiáng)制】不要在 Android 的 Application 對象中緩存數(shù)據(jù)箕憾。基礎(chǔ)組件之間的數(shù)據(jù)共享請使用 Intent 等機(jī)制,也可使用 SharedPreferences 等數(shù)據(jù)持久化機(jī)制.
7.Activity 或者 Fragment 中動(dòng)態(tài)注冊 BroadCastReceiver 時(shí), registerReceiver()和 unregisterReceiver()要成對出現(xiàn)拳昌。
說明:
如果 registerReceiver()和 unregisterReceiver()不成對出現(xiàn),則可能導(dǎo)致已經(jīng)注冊的
receiver 沒有在合適的時(shí)機(jī)注銷,導(dǎo)致內(nèi)存泄漏,占用內(nèi)存空間,加重 SystemService
負(fù)擔(dān)袭异。
部分華為的機(jī)型會對 receiver 進(jìn)行資源管控,單個(gè)應(yīng)用注冊過多 receiver 會觸發(fā)管
控模塊拋出異常,應(yīng)用直接崩潰。
正例:
public class MainActivity extends AppCompatActivity {
private static MyReceiver myReceiver = new MyReceiver();
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter("com.example.myservice");
registerReceiver(myReceiver, filter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(myReceiver);
}
...
}
反例:
public class MainActivity extends AppCompatActivity {
private static MyReceiver myReceiver;
@Override
protected void onResume() {
super.onResume();
myReceiver = new MyReceiver();
IntentFilter filter = new IntentFilter("com.example.myservice");
registerReceiver(myReceiver, filter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myReceiver);
}
}
8.在 Activity 中顯示對話框或彈出浮層時(shí),盡量使用 DialogFragment,而非Dialog/AlertDialog,這樣便于隨 Activity 生命周期管理對話框/彈出浮層的生命周期炬藤。
9.盡量不要使用 AnimationDrawable,它在初始化的時(shí)候就將所有圖片加載到內(nèi)存中,特別占內(nèi)存,并且還不能釋放,釋放之后下次進(jìn)入再次加載時(shí)會報(bào)錯(cuò)御铃。
說明:
Android 的幀動(dòng)畫可以使用 AnimationDrawable 實(shí)現(xiàn),但是如果你的幀動(dòng)畫中如果
包含過多幀圖片,一次性加載所有幀圖片所導(dǎo)致的內(nèi)存消耗會使低端機(jī)發(fā)生 OOM
異常。幀動(dòng)畫所使用的圖片要注意降低內(nèi)存消耗,當(dāng)圖片比較大時(shí),容易出現(xiàn) OOM沈矿。
圖片數(shù)量較少的 AnimationDrawable 還是可以接受的上真。
10.【推薦】新建線程時(shí),定義能識別自己業(yè)務(wù)的線程名稱,便于性能優(yōu)化和問題排查。
public class MyThread extends Thread {
public MyThread(){
super.setName("ThreadName");
...
}
}
11.【推薦】謹(jǐn)慎使用 Android 的多進(jìn)程,多進(jìn)程雖然能夠降低主進(jìn)程的內(nèi)存壓力,但會遇到如下問題:
- 不能實(shí)現(xiàn)完全退出所有 Activity 的功能;
- 首次進(jìn)入新啟動(dòng)進(jìn)程的頁面時(shí)會有延時(shí)的現(xiàn)象(有可能黑屏羹膳、白屏幾秒,是白屏還是黑屏和新 Activity 的主題有關(guān));
- 應(yīng)用內(nèi)多進(jìn)程時(shí),Application 實(shí)例化多次,需要考慮各個(gè)模塊是否都需要在所有進(jìn)程中初始化;
- 多進(jìn)程間通過 SharedPreferences 共享數(shù)據(jù)時(shí)不穩(wěn)定睡互。
12.【 推 薦 】 SharedPreference 提 交 數(shù) 據(jù) 時(shí) , 盡 量 使 用 Editor#apply() , 而 非Editor#commit()。一般來講,僅當(dāng)需要確定提交結(jié)果,并據(jù)此有后續(xù)操作時(shí),才使用 Editor#commit()陵像。說明:SharedPreference 相關(guān)修改使用 apply 方法進(jìn)行提交會先寫入內(nèi)存,然后異步寫入磁盤,commit 方法是直接寫入磁盤就珠。如果頻繁操作的話 apply 的性能會優(yōu)于 commit,apply 會將最后修改內(nèi)容寫入磁盤。但是如果希望立刻獲取存儲操作的結(jié)果,并據(jù)此做相應(yīng)的其他操作,應(yīng)當(dāng)使用 commit醒颖。
正例:
public void updateSettingsAsync() {
SharedPreferences mySharedPreferences = getSharedPreferences("settings",
Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
editor.putString("id", "foo");
editor.apply();
}
public void updateSettings() {
SharedPreferences mySharedPreferences = getSharedPreferences("settings",
Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
editor.putString("id", "foo");
if (!editor.commit()) {
Log.e(LOG_TAG, "Failed to commit setting changes");
}
}
反例:
editor.putLong("key_name", "long value");
editor.commit();
13.【強(qiáng)制】在 Activity.onPause()或 Activity.onStop()回調(diào)中,關(guān)閉當(dāng)前 activity 正在執(zhí)行的的動(dòng)畫妻怎。
14.【推薦】使用 ARGB_565 代替 ARGB_888,在不怎么降低視覺效果的前提下,減少內(nèi)存占用。
說明:
android.graphics.Bitmap.Config 類中關(guān)于圖片顏色的存儲方式定義:
- ALPHA_8 代表 8 位 Alpha 位圖;
- ARGB_4444 代表 16 位 ARGB 位圖;
- ARGB_8888 代表 32 位 ARGB 位圖;
- 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_8888 :
Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
反例:
Bitmap newb = Bitmap.createBitmap(width, height, Config.ARGB_8888);
15.【推薦】在有強(qiáng)依賴 onAnimationEnd 回調(diào)的交互時(shí),如動(dòng)畫播放完畢才能操作頁面 , onAnimationEnd 可 能 會 因 各 種 異 常 沒 被 回 調(diào) ( 參 考 :https://stackoverflow.com/questions/5474923/onanimationend-is-not-getting-called-onanimationstart-works-fine ), 建 議 加 上 超 時(shí) 保 護(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);