Androidソース生コードバグによる連続通知アプリの死


プロジェクトでは、同じ通知を連続的に送信すると、アプリケーションが遅くなり、最終的にカードが死んでしまうことがわかりました.
デバッグでは、毎回newの新しいRemoteViewが詰まって死なないことがわかりました.これはなぜですか.
追跡はandroidソースコードに入ってやっと原因を発見した.
通知の送信を適用することは、プロセスが対話するプロセスです.appは通知クラス(Notification)を通知サービスプロセスに転送する必要がある.通知サービスプロセス管理による通知の送信.
Notificationの構築では、RemoteViewを含むParcelableインタフェースが実装されています.カード死の原因はRemoteViewの実現原理にある.
RemoteViewは、setImageView Resource()などのViewコントロールの操作を容易にする一連の方法を提供しています.その実現メカニズムは、次のとおりです.
RemoteViewの内部でアクションクラスを設定します.
    /**
     * 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抽象クラスの配列を維持します.
/**
     * An array of actions to perform on the view tree once it has been
     * inflated
     */
    private ArrayList mActions;

RemoteView内のViewコントロールの各操作は、ReflectionAction(Actionに継承)に対応します.
    /**
     * Base class for the reflection actions.
     */
    private class ReflectionAction extends Action {

ReflectionActionの役割は、反射呼び出しViewの関連メソッドを使用して、View図面を操作することです.
図面を描くときに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();
        }
        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のバグだと思いますが、確かにこの問題を根源的に解決する有効な方法はなく、通知を出すたびにnewが新しいRemoteViewが出てくるだけで、Actionに余分な操作はありません.時間がかかる.cloneの元のRemoteViewではなく、clone()がAction配列をコピーし、最終的には遅いことに注意してください.