RemoteViewsに隠された穴

5352 ワード

前言
先日、再起動の問題を調べていましたが、fd漏洩によるものと思っていたら、RemoteViewsのピットによるものだということが最終的にわかりました.次に、setImageViewBitmap(int viewId, Bitmap bitmap)の方法を例に、この穴について説明します.
setImageViewBitmapのピット
まず具体的な実装を見てみましょうjava
    /**
     * Equivalent to calling ImageView.setImageBitmap
     *
     * @param viewId The id of the view whose bitmap should change
     * @param bitmap The new Bitmap for the drawable
     */
    public void setImageViewBitmap(int viewId, Bitmap bitmap) {
        setBitmap(viewId, "setImageBitmap", bitmap);
    }

このapiの定義を見ると、まずbitmapを格納する構造はviewIdをkey、bitmapをvalueとするmap listの形式であり、このapiを呼び出すと以前と同じviewIdのbitmap(結局私は以前そう思っていた)が直接置き換えられると思いますが、実際には?私たちはその実現に沿って見続けます.
RemoteViews.java
    public void setBitmap(int viewId, String methodName, Bitmap value) {
        addAction(new BitmapReflectionAction(viewId, methodName, value));
    }

    private void addAction(Action a) {
        if (hasLandscapeAndPortraitLayouts()) {
            ...
        }
        if (mActions == null) {
            mActions = new ArrayList();
        }
        mActions.add(a);

        // update the memory usage stats
        a.updateMemoryUsageEstimate(mMemoryUsageCounter);
    }

上から分かるように、私たちが想像していた置換は発生せず、BitmapReflectionActionオブジェクトを新たに作成し、mActionsというArrayListに追加しました.BitmapReflectionActionの構造関数を見てみましょう.
RemoteViews.java
    private class BitmapReflectionAction extends Action {
        int bitmapId;
        Bitmap bitmap;
        String methodName;

        BitmapReflectionAction(int viewId, String methodName, Bitmap bitmap) {
            this.bitmap = bitmap;
            this.viewId = viewId;
            this.methodName = methodName;
            bitmapId = mBitmapCache.getBitmapId(bitmap);
        }
    }

getBitmapIdの実装をもう一度見てみましょう.
RemoteViews.java
    private static class BitmapCache {
        ArrayList mBitmaps;

        public BitmapCache() {
            mBitmaps = new ArrayList();
        }

        public int getBitmapId(Bitmap b) {
            if (b == null) {
                return -1;
            } else {
                if (mBitmaps.contains(b)) {
                    return mBitmaps.indexOf(b);
                } else {
                    mBitmaps.add(b);
                    return (mBitmaps.size() - 1);
                }
            }
        }

        public void writeBitmapsToParcel(Parcel dest, int flags) {
            int count = mBitmaps.size();
            dest.writeInt(count);
            for (int i = 0; i < count; i++) {
                mBitmaps.get(i).writeToParcel(dest, flags);
            }
        }
    }

 このメソッドは、mBitmapsのbitmapのindexを返し、mBitmapsにこのbitmapが含まれていない場合は追加されます. 大丈夫って言う人もいるかもしれませんが、もう少し貯金しただけなので、表示するときは問題ありません.ただし、上記のwriteBitmapsToParcelメソッドを見てください.プロセス間で転送する場合、各mBitmapsのbitmapはwriteToParcelになります. これは、bitmapがwriteBlobのようにParcelオブジェクトに格納されている場合、各bitmap転送はfdを開き、mBitmapsのbitmapの数が多い場合、appまたはsystem_を直接招く可能性があることを意味する.serverを切ります.私のもう一つのブログは、Binderの異常をlogで見たのは、この原因によるものです. 上記の問題がなくてもappやシステムの性能に影響を及ぼすに違いない.したがって,開発者は通常の開発では,setImageViewBitmapのようなメソッドを同じRemoteViewsインスタンスで頻繁に複数回呼び出さないように注意しなければならない.