Android源生代碼bug致使連續發通知應用卡死

項目中發現,連續發送同一個通知會致使應用愈來愈慢,最終卡死。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數組都拷貝下來,最終同樣會很慢。

相關文章
相關標籤/搜索