項目中發現,連續發送同一個通知會致使應用愈來愈慢,最終卡死。android
調試發現,若是每次都new一個新的RemoteView就不會卡死,這是爲何?數組
跟蹤進入android源碼終於發現緣由所在。app
應用發送通知是進程交互的過程。app須要將通知類(Notification)傳送給通知服務進程。由通知服務進程管理髮送通知。ide
Notification中的組建都實現了Parcelable接口,包括RemoteView。卡死的緣由就在於RemoteView的實現原理上。
佈局
RemoteView提供了一系列方法,方便咱們操做其中的View控件,好比setImageViewResource()等,其實現的機制是:this
由RemoteView內部定一個Action類:spa
/** * Base class for all actions that can be performed on an * inflated view. * * SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!! */ private abstract static class Action implements Parcelable {
RemoteView維護一個Action抽象類的數組:.net
/** * An array of actions to perform on the view tree once it has been * inflated */ private ArrayList<Action> mActions;
每個對RemoteView內View控件的操做都對應一個ReflectionAction(繼承於Action):調試
/** * Base class for the reflection actions. */ private class ReflectionAction extends Action {
ReflectionAction的做用是利用反射調用View的相關方法,用來操做View繪圖。code
繪圖時候遍歷RemoteView的Action數組,得到數據利用反射調用相關View的方法:
@Override public void apply(View root, ViewGroup rootParent) { ... Class klass = view.getClass(); Method method; try { method = klass.getMethod(this.methodName, getParameterType()); ... method.invoke(view, this.value); } ... }
致使應用卡死的根源問題就是,這個RemoteView維護的Action數組是隻會增長不會減小的:
private void addAction(Action a) { if (mActions == null) { mActions = new ArrayList<Action>(); } mActions.add(a); // update the memory usage stats a.updateMemoryUsageEstimate(mMemoryUsageCounter); }
也就是說,若是每次都使用同一個RemoteView發送通知,那麼每次發送通知就會把以前全部的操做都重複的執行一遍。消耗比上次更多的時間,若是通知欄里布局複雜,notify(int id, Notification notification)方法就會消耗很長時間,致使應用卡死:
private void performApply(View v, ViewGroup parent) { if (mActions != null) { final int count = mActions.size(); for (int i = 0; i < count; i++) { Action a = mActions.get(i); a.apply(v, parent); } } }
我的認爲,這個是Android的一個bug,可是確實沒有有效的方法在根源上解決這個問題,只有再每次發通知前,new一個新RemoteView出來,這樣Action裏就沒有多餘的操做。花費時間很短。須要注意的是,不要clone原有的RemoteView,clone()會將Action數組都拷貝下來,最終同樣會很慢。