web-dev-qa-db-ja.com

JavaおよびAndroid開発でWeakReferenceを使用する方法は?

私はJava開発者として2年間働いています。

しかし、コードにWeakReferenceを記述したことはありません。 WeakReferenceを使用してアプリケーション、特にAndroidアプリケーションをより効率的にする方法は?

156
Chris

AndroidでWeakReferenceを使用することは、単純な古いJavaで使用することと変わりません。詳細な説明を提供する素晴らしいガイドがあります: nderstanding Weak References

オブジェクトへの参照が必要な場合はいつでも使用することを検討する必要がありますが、その参照がオブジェクトをガベージコレクターから保護することは望ましくありません。典型的な例は、メモリ使用量が高くなりすぎたときにガベージコレクションを行いたいキャッシュです(多くの場合、WeakHashMapで実装されます)。

必ずSoftReferencePhantomReferenceもチェックアウトしてください。

EDIT:トムはWeakHashMapを使用してキャッシュを実装することに関して懸念を表明しました。ここに問題を説明する記事があります: WeakHashMapはキャッシュではありません!

トムは、WeakHashMapキャッシングによるNetbeansパフォーマンスの低下について 苦情 があったことは正しいです。

WeakHashMapを使用してキャッシュを実装し、SoftReferenceを使用して実装された独自の手動ロールキャッシュと比較することは、まだ良い学習経験になると思います。 Apache JCS のようなサードパーティのライブラリを使用する方が理にかなっているため、現実の世界ではこれらのソリューションのいずれも使用しないでしょう。

220
dbyrne

[EDIT2] WeakReferenceの別の良い例を見つけました。 UIスレッドからのビットマップの処理 ページの ビットマップの効率的な表示 トレーニングガイドでは、AsyncTaskのWeakReferenceの1つの使用法を示しています。

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    private final WeakReference<ImageView> imageViewReference;
    private int data = 0;

    public BitmapWorkerTask(ImageView imageView) {
        // Use a WeakReference to ensure the ImageView can be garbage collected
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    // Decode image in background.
    @Override
    protected Bitmap doInBackground(Integer... params) {
        data = params[0];
        return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
    }

    // Once complete, see if ImageView is still around and set bitmap.
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
}

それは言います、

ImageViewへのWeakReferenceは、AsyncTaskがImageViewおよびそれが参照するものがガベージコレクションされることを妨げないことを保証します。タスクが終了してもImageViewがまだ存在しているという保証はないため、onPostExecute()で参照も確認する必要があります。たとえば、ユーザーがアクティビティから移動したり、タスクが完了する前に構成が変更されたりすると、ImageViewは存在しなくなる可能性があります。

ハッピーコーディング!


[EDIT] facebook-Android-sdk からWeakReferenceの非常に良い例を見つけました。 ToolTipPopup クラスは、アンカービューの上にツールチップを表示する単純なウィジェットクラスにすぎません。スクリーンショットをキャプチャしました。

scrumptious screenshot

このクラスは本当にシンプル(約200行)で、見る価値があります。そのクラスでは、WeakReferenceクラスを使用してアンカービューへの参照を保持します。これは、ツールチップインスタンスがアンカービューより長く存続している場合でもアンカービューをガベージコレクションできるため、完全に理にかなっています。

ハッピーコーディング! :)


WeakReferenceクラスの実例を1つ紹介します。これは、 AutoCompleteTextView と呼ばれるAndroidフレームワークウィジェットの小さなコードスニペットです。

要するに、WeakReferenceクラスは、Viewオブジェクトを保持するために使用され、 メモリリーク この例では。

AutoCompleteTextViewのネストされたクラスであるPopupDataSetObserverクラスをコピーして貼り付けます。それは本当にシンプルで、コメントはクラスをよく説明しています。ハッピーコーディング! :)

    /**
     * Static inner listener that keeps a WeakReference to the actual AutoCompleteTextView.
     * <p>
     * This way, if adapter has a longer life span than the View, we won't leak the View, instead
     * we will just leak a small Observer with 1 field.
     */
    private static class PopupDataSetObserver extends DataSetObserver {
    private final WeakReference<AutoCompleteTextView> mViewReference;
    private PopupDataSetObserver(AutoCompleteTextView view) {
        mViewReference = new WeakReference<AutoCompleteTextView>(view);
    }
    @Override
    public void onChanged() {
        final AutoCompleteTextView textView = mViewReference.get();
        if (textView != null && textView.mAdapter != null) {
            // If the popup is not showing already, showing it will cause
            // the list of data set observers attached to the adapter to
            // change. We can't do it from here, because we are in the middle
            // of iterating through the list of observers.
            textView.post(updateRunnable);
        }
    }

    private final Runnable updateRunnable = new Runnable() {
        @Override
        public void run() {
            final AutoCompleteTextView textView = mViewReference.get();
            if (textView == null) {
                return;
            }
            final ListAdapter adapter = textView.mAdapter;
            if (adapter == null) {
                return;
            }
            textView.updateDropDownForFilter(adapter.getCount());
        }
    };
}

また、PopupDataSetObserverはアダプターの設定に使用されます。

    public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
    if (mObserver == null) {
        mObserver = new PopupDataSetObserver(this);
    } else if (mAdapter != null) {
        mAdapter.unregisterDataSetObserver(mObserver);
    }
    mAdapter = adapter;
    if (mAdapter != null) {
        //noinspection unchecked
        mFilter = ((Filterable) mAdapter).getFilter();
        adapter.registerDataSetObserver(mObserver);
    } else {
        mFilter = null;
    }
    mPopup.setAdapter(mAdapter);
}

最後に一つだけ。また、AndroidアプリケーションでWeakReferenceの動作例を知りたいと思っていました。公式サンプルアプリケーションでいくつかのサンプルを見つけることができました。しかし、私はそれらの使用法のいくつかを本当に理解できませんでした。たとえば、 ThreadSample および DisplayingBitmaps アプリケーションはコードでWeakReferenceを使用しますが、いくつかのテストを実行した後、参照されたビューオブジェクトはガベージコレクションではなくアダプタでリサイクルされるため、get()メソッドがnullを返すことはありません。

61
김준호

他の回答の中には、不完全または過度に長いものがあります。一般的な答えを次に示します。

JavaおよびAndroidでWeakReferenceを使用する方法

次の手順を実行できます。

  1. WeakReference変数を作成します
  2. 弱参照を設定します
  3. 弱参照を使用する

コード

MyClassには、AnotherClassへの弱い参照があります。

public class MyClass {

    // 1. Create a WeakReference variable
    private WeakReference<AnotherClass> mAnotherClassReference;

    // 2. Set the weak reference
    void someMethod(AnotherClass object) {
        mAnotherClassReference = new WeakReference<>(object);
    }

    // 3. Use the weak reference
    void anotherMethod() {
        AnotherClass object = mAnotherClassReference.get();
        if (object == null) return;
        // do something with the object
    }

}

AnotherClassには、MyClassへの強い参照があります。

public class AnotherClass {

    // strong reference
    MyClass mMyClass;

    // allow MyClass to get a weak reference to this class
    void someMethod() {
        mMyClass = new MyClass();
        mMyClass.someMethod(this);
    }
}

ノート

  • 弱い参照が必要な理由は、ガベージコレクターが不要になったオブジェクトを破棄できるようにするためです。 2つのオブジェクトが相互に強い参照を保持している場合、それらはガベージコレクションできません。これはメモリリークです。
  • 2つのオブジェクトが相互に参照する必要がある場合、オブジェクトA(一般に短命オブジェクト)はオブジェクトB(一般に長生きオブジェクト)への弱い参照を持つ必要がありますが、BはAへの強参照です。上記の例では、MyClassはAで、AnotherClassはBでした。
  • WeakReferenceを使用する代わりに、別のクラスにインターフェースを実装させることができます。これは Listener/Observer Pattern で行われます。

実用例

14
Suragch

「正規化された」マッピングでは、問題のオブジェクトの1つのインスタンスをメモリに保持し、他のすべてのインスタンスはポインターまたは何らかのメカニズムを介してその特定のインスタンスを検索します。これは、弱参照が役立つ場所です。簡単な答えは、WeakReferenceオブジェクトを使用して、システム内のオブジェクトへのポインターを作成しながら、それらのオブジェクトをgarbage-collectorスコープ外に出ると。たとえば、次のようなコードがあった場合:

class Registry {
     private Set registeredObjects = new HashSet();

     public void register(Object object) {
         registeredObjects.add( object );
     }
 }

登録したオブジェクトは、registeredObjectsのセットに格納された参照があるため、GCによって回収されることはありません。一方、これを行う場合:

class Registry {
     private Set registeredObjects = new HashSet();

     public void register(Object object) {
         registeredObjects.add( new WeakReference(object) );
     }
 }

その後、GCがSet内のオブジェクトを回収したい場合、回収することができます。この手法は、キャッシュ、カタログ化などに使用できます。GCとキャッシュの詳細については、以下を参照してください。

参照: ガベージコレクターおよびWeakReference

7
Akshay