メッセージオブジェクトの再利用(AndroidのFlyweightモード)

1.フライウェイトモデルとは

1はじめに

Flyweightモード、つまりFlyWeightは、オブジェクトを再利用し、重複を回避し、多数のオブジェクトを作成することで、システムリソースの消費を節約することを目的としています。

フライウェイトモードの紹介:https//www.cnblogs.com/adamjwh/p/9070107.html

フライウェイトモデルの練習:https//www.jianshu.com/p/b925b8cb6494

第二に、メッセージでの実装

Androidはイベント駆動型のメカニズムであることがわかっています。アプリケーションの表示と相互作用を形成するためにさまざまなイベントの組み合わせがあり、スレッド間の通信も含まれます。Messageを使用して、Messageを介してオブジェクトをカプセル化する必要があります。 、イベントのタイプ、パラメーター、およびハンドラーを記述します連続イベントまたは同時イベ​​ントでは、メッセージは多数のオブジェクトによって作成および生成さ、多数のオブジェクトが頻繁に作成されると、メモリの深刻な消費GCのトリガーなどが頻繁に発生しアプリケーションのパフォーマンスに影響を与えることがわかります。フライウェイトモードを使用してメッセージオブジェクトを再利用します毎回新しいオブジェクトを作成する必要はありません

1.オブジェクトの取得

メッセージオブジェクトを取得する方法は2つあります。1つは新しいオブジェクトを構築メソッドnewから直接取得する方法、もう1つはgetメソッドを使用してオブジェクトを取得する方法です。公式の推奨事項はgetを使用することです。これは、オブジェクトプールからオブジェクトを取得するためです。再利用メカニズムにより、多数のオブジェクトが生成されないようにします。newは、毎回新しいオブジェクトが生成されることを意味します。

getの実現を見てみましょう:

/**
 * Return a new Message instance from the global pool. Allows us to
 * avoid allocating new objects in many cases.
 */
public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

読んだ後、おそらくそのようなロジックがあります。sPoolが空でない場合、sPoolはMessageオブジェクトとして返されます。そうでない場合、新しいオブジェクトが作成されます。このsPoolは何ですか?m.nextとは何ですか?フラグとは何ですか?sPoolSizeはどうですか?

さて、これらの質問で、メッセージのグローバル変数に移動して、以下を見つけましょう。

// sometimes we store linked lists of these things
/*package*/ Message next;


/** @hide */
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;

private static final int MAX_POOL_SIZE = 50;

OK、sPoolとnextの両方がメッセージタイプであることがわかります。もう一度考えてみてください。リンクされたリストのようなものですか?はい、実際には、これらのリサイクルされたメッセージオブジェクトを格納するためのリンクリストを維持し、使用時にリンクリストの先頭からそれらを取得します。

画像

一般的な構造は上の図を参照できます。sPoolはポインタの役割に似ており、次に再利用できる次のメッセージオブジェクトを指し、使用可能なオブジェクトがない場合はnullを指します。同様に、sPoolの初期値もnull参照を指しています。現時点では、リンクリストにオブジェクトはありません。つまり、上記のコードを入力しreturn new Message();、新しいオブジェクトを直接作成して戻ります。

また、sPoolSizeフィールドとMAX_POOL_SIZEフィールドがあり、1つは現在のリンクリストの長さを表し、もう1つは最大長を表していることがわかります。したがって、上記のコードでオブジェクトを取得した後、size–。

オブジェクトを取得した後、別の設定フラグ= 0があります。これはなぜですか?実はただのマークです。このオブジェクトにマークを付けることは、すでに誰かが使用しています。このマークは後で使用され、リサイクル中にオブジェクトが使用されているかどうかが判断されます。

2.オブジェクトの保存

上で見たように、再利用されたメッセージオブジェクトを格納および提供するようなリンクリストがあります。これらのオブジェクトはいつリンクリストに挿入されますか?単純なフライウェイトモードでは、オブジェクトを作成した後、内部状態をキーとして使用し、オブジェクトを値として使用してマップに保存しますが、オブジェクトの作成時にここでは保存されませんか?

OK、キーの位置を確認して見つけ続けます。recycle()

public void recycle() {
    if (isInUse()) {
        if (gCheckRecycle) {
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    recycleUnchecked();
}

ご覧のとおり、ここでは上記のマークを使用しています。まず、利用可能でリサイクル可能かどうかを判断します。利用できない場合は、直接返品するか例外をスローし、リサイクルできる場合はリサイクルプログラムに参加します。

/**
 * Recycles a Message that may be in-use.
 * Used internally by the MessageQueue and Looper when disposing of queued Messages.
 */
void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    //类似于reset,将各个变量状态重置
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;
    
    //插入到链表表头
    //链表长度限制为50
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

このコードは2つの部分に分けることができます。最初の部分はこれらの属性をリセットします。結局、それは再利用されます。新しいものを与えない場合は、空の属性を与える必要があります。コンテンツのあるオブジェクトに与えることはできません。そうすると問題が発生します。2番目の部分は、リンクリストに挿入することです。これは、リンクリストの先頭に挿入する操作であることがわかります。現在のsPoolが指すメッセージオブジェクトを次へ、次にsPoolを現在のオブジェクトをポイントし、最後にsize ++を指定します。

sPoolは、現在のリンクされたリストの先頭を指します。この理解は問題ありません。

写真を撮る
ここに画像の説明を挿入
リサイクルする
ここに画像の説明を挿入

リサイクルメソッドはいつ呼び出されますか?いつリサイクルされますか?
メソッドをCtrlキーを押したままにして、メソッドを呼び出す場所をたくさん見つけましたが、実際には、いくつかの場所に注意を払っています。

Message msg = mHandler.obtainMessage(MSG_DISABLE, state1, state2, animate);
        if (Looper.myLooper() == mHandler.getLooper()) {
            // If its the right looper execute immediately so hides can be handled quickly.
            mHandler.handleMessage(msg);
            msg.recycle();
        } else {
            msg.sendToTarget();
        }

ここでは、handleMessageメソッドが呼び出された後、recycleメソッドが呼び出されていることがわかります。

if (Looper.myLooper() == mMainLooper) {
            mCallback.executeMessage(msg);
            msg.recycle();
            return;
        }

コールバックが実行された後、recycleメソッドが呼び出されます。

3.まとめ

メッセージはFlyweightの再利用です。MessageクラスはFlyweightAbstraction、Flyweight Object、Flyweight Factory Classの複数の役割を担いますが、実際には非常に理解しやすいです。リンクリストは内部で維持され、最大50個のオブジェクトを格納します。getを使用してオブジェクトを取得する場合、テーブルの先頭からオブジェクトを取り出すか、新しいオブジェクトを作成します。オブジェクトがリサイクルされると、オブジェクトのプロパティがリセットされ、リンクリストに保存されます。

おすすめ

転載: blog.csdn.net/lizebin_bin/article/details/91381542