วิธีใช้ WeakReference ในการพัฒนา Java และ Android


166

ฉันเป็นนักพัฒนา java มา 2 ปีแล้ว

แต่ฉันไม่เคยเขียน WeakReference ในรหัสของฉัน วิธีใช้ WeakReference เพื่อทำให้แอปพลิเคชันของฉันมีประสิทธิภาพมากขึ้นโดยเฉพาะแอปพลิเคชัน Android


อาจมีความแตกต่างสำหรับ Android: stackoverflow.com/questions/299659/…
Pacerier

คำตอบ:


229

การใช้งานWeakReferenceใน Android นั้นไม่แตกต่างไปจากการใช้งานใน Java แบบเก่า นี่คือคำแนะนำที่ดีซึ่งจะช่วยให้คำอธิบายรายละเอียด: ความเข้าใจอ้างอิงอ่อนแอ

คุณควรคิดถึงการใช้สิ่งใดสิ่งหนึ่งเมื่อคุณต้องการการอ้างอิงไปยังวัตถุ แต่คุณไม่ต้องการการอ้างอิงนั้นเพื่อปกป้องวัตถุจากตัวรวบรวมขยะ ตัวอย่างคลาสสิกคือแคชที่คุณต้องการรวบรวมขยะเมื่อการใช้หน่วยความจำสูงเกินไป (มักใช้กับWeakHashMap)

ให้แน่ใจว่าได้ตรวจสอบSoftReferenceและPhantomReferenceเช่นกัน

แก้ไข: Tom ทำให้เกิดความกังวลเกี่ยวกับการนำแคชWeakHashMapไปใช้ นี่คือบทความที่จัดทำปัญหา: WeakHashMap ไม่ใช่แคช!

ทอมถูกต้องที่มีการร้องเรียนเกี่ยวกับประสิทธิภาพของ Netbeans ที่ไม่ดีเนื่องจากการWeakHashMapแคช

ผมยังคิดว่ามันจะเป็นประสบการณ์การเรียนรู้ที่ดีที่จะใช้กับแคชแล้วเปรียบเทียบกับแคชมือรีดของคุณเองดำเนินการกับWeakHashMap SoftReferenceในโลกแห่งความจริงที่คุณอาจจะไม่ใช้อย่างใดอย่างหนึ่งของการแก้ปัญหาเหล่านี้เพราะมันทำให้รู้สึกมากขึ้นในการใช้ห้องสมุดของบุคคลที่ 3 เช่นApache JCS


15
No! No! No! WeakHashMapใช้เป็นแคชร้ายแรง สามารถลบรายการได้ทันทีที่สร้างขึ้น สิ่งนี้อาจจะไม่เกิดขึ้นเมื่อคุณทำการทดสอบ แต่อาจใช้งานได้ดี จากการสังเกตเห็น NetBeans นั้นสามารถหยุดการทำงานของ CPU ได้อย่างมีประสิทธิภาพ 100%
Tom Hawtin - tackline

3
@Tom ฉันได้อัปเดตคำตอบของฉันแล้ว เพื่อความเป็นธรรม แต่ผมเป็นเทคนิคที่ถูกต้องที่แคชมักจะถูกนำมาใช้กับWeakHashMapแม้ว่าคุณจะเป็นที่ถูกต้องว่ามันเป็นทางเลือกที่ดี;)
dbyrne

1
คำตอบที่ยอดเยี่ยมโดย dbyrne ขอบคุณสำหรับสิ่งนี้. ฉันเห็นเหตุผลที่คริสไม่ยอมรับคำตอบนี้
San

@dbyrne ฉันใช้วัตถุในกิจกรรมของฉันเช่น GridView, ImageView หรือ BaseAdapter ในเมธอด onDestroy เมื่อฉันเสร็จสิ้นกิจกรรมฉันต้องทำบางสิ่งกับวัตถุนี้โดยใช้ Weak / SoftReferences หรือไม่ หรือระบบจะล้างหน่วยความจำของวัตถุนี้โดยอัตโนมัติ
beni

4
ลิงก์เสียไปที่ java.net
Suragch

64

[EDIT2]WeakReferenceผมพบอีกหนึ่งตัวอย่างที่ดีของ การประมวลผลบิตแมปปิดหน้า UI เธรดในการแสดงคู่มือการฝึกอบรมบิตแมปอย่างมีประสิทธิภาพแสดงการใช้งานครั้งเดียวWeakReferenceใน AsyncTask

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);
            }
        }
    }
}

มันบอกว่า,

WeakReference เพื่อให้แน่ใจ ImageView ที่ AsyncTask ไม่ได้ป้องกัน ImageView และสิ่งที่อ้างอิงจากการเก็บขยะ ไม่มีการรับประกัน ImageView จะยังคงอยู่เมื่องานเสร็จดังนั้นคุณต้องตรวจสอบการอ้างอิงใน onPostExecute () ImageView อาจไม่มีอยู่อีกต่อไปตัวอย่างเช่นผู้ใช้นำทางออกจากกิจกรรมหรือหากมีการเปลี่ยนแปลงการกำหนดค่าเกิดขึ้นก่อนที่งานจะเสร็จสิ้น

การเข้ารหัสที่มีความสุข!


[แก้ไข]ผมพบว่าเป็นตัวอย่างที่ดีจริงๆWeakReferenceจากFacebook-หุ่นยนต์ SDK คลาสToolTipPopupเป็นเพียงคลาสวิดเจ็ตธรรมดาที่แสดงคำแนะนำเครื่องมือเหนือมุมมองจุดยึด ฉันจับภาพหน้าจอ

ภาพหน้าจอแสนอร่อย

ชั้นเรียนง่ายมาก (ประมาณ 200 บรรทัด) และควรค่าแก่การดู ในคลาสนั้นWeakReferenceคลาสถูกใช้เพื่อเก็บการอ้างอิงถึงมุมมองจุดยึดซึ่งเหมาะสมอย่างยิ่งเพราะมันเป็นไปได้ที่สมอมุมมองจะถูกเก็บขยะแม้ในกรณีที่อินสแตนซ์คำแนะนำเครื่องมือมีชีวิตยาวนานกว่ามุมมองจุดยึด

การเข้ารหัสที่มีความสุข! :)


ให้ฉันแบ่งปันตัวอย่างการทำงานของWeakReferenceชั้นเรียน มันเป็นข้อมูลโค้ดเล็กน้อยจาก Android AutoCompleteTextViewกรอบที่เรียกว่าวิดเจ็ต

กล่าวโดยสรุป WeakReference คลาสใช้เพื่อเก็บ View วัตถุเพื่อป้องกันการรั่วไหลของหน่วยความจำในตัวอย่างนี้

ฉันเพิ่งจะคัดลอกและวางระดับ PopupDataSetObserver AutoCompleteTextViewซึ่งเป็นชั้นซ้อนกันของ มันง่ายมากและความเห็นอธิบายคลาสได้ดี การเข้ารหัสที่มีความสุข! :)

    /**
     * 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);
}

สิ่งสุดท้าย. ฉันต้องการทราบตัวอย่างการทำงานของWeakReferenceแอปพลิเคชัน Android และฉันสามารถหาตัวอย่างบางส่วนในแอปพลิเคชันตัวอย่างที่เป็นทางการได้ แต่ฉันไม่เข้าใจการใช้งานบางอย่างจริงๆ ยกตัวอย่างเช่นThreadSampleและDisplayingBitmapsการใช้งานที่ใช้WeakReferenceในรหัสของมัน แต่หลังจากใช้การทดสอบหลายฉันพบว่าได้รับ () วิธีการที่ไม่เคยผลตอบแทนnullเพราะมุมมองวัตถุอ้างอิงรีไซเคิลในอะแดปเตอร์แทนแล้วเก็บขยะ


1
ขอบคุณสำหรับตัวอย่างที่ดี - ฉันรู้สึกว่านี่ควรเป็นคำตอบที่ได้รับการยอมรับสำหรับคำถาม ไชโย!
Ackshaey Singh

@AckshaeySingh ขอบคุณ! :)
김준호

16

คำตอบอื่น ๆ บางส่วนดูเหมือนไม่สมบูรณ์หรือยาวเกินไป นี่คือคำตอบทั่วไป

วิธีใช้ WeakReference ใน Java และ Android

คุณสามารถทำตามขั้นตอนต่อไปนี้:

  1. สร้างWeakReferenceตัวแปร
  2. ตั้งค่าการอ้างอิงที่อ่อนแอ
  3. ใช้การอ้างอิงที่อ่อนแอ

รหัส

MyClassAnotherClassมีการอ้างอิงอ่อนแอ

public class MyClass {

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

    // 2. Set the weak reference (nothing special about the method name)
    void setWeakReference(AnotherClass anotherClass) {
        mAnotherClassReference = new WeakReference<>(anotherClass);
    }

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

}

AnotherClassMyClassมีการอ้างอิงที่แข็งแกร่งในการ

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

หมายเหตุ

  • เหตุผลที่คุณต้องการการอ้างอิงที่อ่อนแอคือ Garbage Collector สามารถกำจัดวัตถุเมื่อไม่ต้องการใช้อีกต่อไป หากวัตถุสองชิ้นยังคงมีการอ้างอิงที่แข็งแกร่งซึ่งกันและกันพวกเขาจะไม่สามารถรวบรวมขยะได้ นี่คือหน่วยความจำรั่ว
  • หากวัตถุสองชิ้นต้องการอ้างอิงซึ่งกันและกันวัตถุ A (โดยทั่วไปคือวัตถุที่มีอายุสั้นกว่า) ควรมีการอ้างอิงที่อ่อนแอไปยังวัตถุ B (โดยทั่วไปคือวัตถุที่มีอายุยืนกว่า) ในขณะที่ B มีการอ้างอิงที่แข็งแกร่งกับ A ในตัวอย่างข้างต้นMyClassคือ A และAnotherClassเป็น B.
  • อีกทางเลือกหนึ่งในการใช้ a WeakReferenceคือให้คลาสอื่นใช้อินเตอร์เฟส ซึ่งจะดำเนินการในรูปแบบฟัง / สังเกตการณ์

ตัวอย่างการปฏิบัติ


คำอธิบายที่สับสน สิ่งที่เป็น// allow MyClass to get a weak reference to this class void someMethod() { mMyClass = new MyClass(); mMyClass.someMethod(this); }??
likejudo

@ likejudo คุณพูดถูก ฉันปรับปรุงการตั้งชื่อตัวแปรและวิธีการบางอย่าง ตอนนี้เป็นอย่างไร
Suragch

คุณต้องตรวจสอบweakreferenceวัตถุในdoSomethingฟังก์ชั่นnullก่อนว่าจะไม่เรียกgetใช้ฟังก์ชัน
Behrouz.M

7

การแมป "canonicalized" เป็นที่ที่คุณเก็บอินสแตนซ์หนึ่งของวัตถุที่เป็นปัญหาในหน่วยความจำและอื่น ๆ ทั้งหมดค้นหาอินสแตนซ์นั้นผ่านพอยน์เตอร์หรือกลไกบางอย่าง ที่นี่การอ้างอิงที่อ่อนแอสามารถช่วยได้ คำตอบสั้น ๆ คือสามารถใช้วัตถุWeakReferenceเพื่อสร้างพอยน์เตอร์ไปยังวัตถุในระบบของคุณในขณะที่ยังคงอนุญาตให้เรียกคืนวัตถุเหล่านั้นโดยตัวเก็บรวบรวมขยะเมื่อพวกเขาผ่านขอบเขต เช่นถ้าฉันมีรหัสเช่นนี้:

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

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

วัตถุใด ๆ ฉันจะลงทะเบียนจะไม่ถูกยึดด้วย GC registeredObjectsเพราะมีการอ้างอิงไปเก็บไว้ในชุดของ ในทางกลับกันถ้าฉันทำสิ่งนี้:

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

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

จากนั้นเมื่อ GC ต้องการเรียกคืนวัตถุในชุดจะสามารถทำได้ คุณสามารถใช้เทคนิคนี้ในการแคชแคตตาล็อก ฯลฯ ดูด้านล่างสำหรับการอ้างอิงถึงการสนทนาเชิงลึกของ GC และการแคช

Ref: เก็บขยะและ WeakReference

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.