วิธีการใช้คลาส AsyncTask ภายในคงที่
เพื่อป้องกันการรั่วไหลคุณสามารถทำให้ชั้นในคงที่ อย่างไรก็ตามปัญหานั้นคือคุณไม่สามารถเข้าถึงมุมมอง UI หรือตัวแปรสมาชิกของกิจกรรมได้อีกต่อไป คุณสามารถส่งผ่านการอ้างอิงถึงContext
แต่แล้วคุณก็เสี่ยงต่อการรั่วไหลของหน่วยความจำแบบเดียวกัน (Android ไม่สามารถเก็บรวบรวมขยะในกิจกรรมได้หลังจากปิดถ้าคลาส AsyncTask มีการอ้างอิงที่แข็งแกร่ง) การแก้ปัญหาคือการอ้างอิงที่อ่อนแอไปยังกิจกรรม (หรืออะไรก็ตามที่Context
คุณต้องการ)
public class MyActivity extends AppCompatActivity {
int mSomeMemberVariable = 123;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// start the AsyncTask, passing the Activity context
// in to a custom constructor
new MyTask(this).execute();
}
private static class MyTask extends AsyncTask<Void, Void, String> {
private WeakReference<MyActivity> activityReference;
// only retain a weak reference to the activity
MyTask(MyActivity context) {
activityReference = new WeakReference<>(context);
}
@Override
protected String doInBackground(Void... params) {
// do some long running task...
return "task finished";
}
@Override
protected void onPostExecute(String result) {
// get a reference to the activity if it is still there
MyActivity activity = activityReference.get();
if (activity == null || activity.isFinishing()) return;
// modify the activity's UI
TextView textView = activity.findViewById(R.id.textview);
textView.setText(result);
// access Activity member variables
activity.mSomeMemberVariable = 321;
}
}
}
หมายเหตุ
- เท่าที่ฉันรู้อันตรายจากการรั่วไหลของหน่วยความจำประเภทนี้เป็นจริงเสมอ แต่ฉันเริ่มเห็นคำเตือนใน Android Studio 3.0 เท่านั้น จำนวนมากของหลัก
AsyncTask
บทเรียนออกมียังไม่จัดการกับมัน (ดูที่นี่ , ที่นี่ , ที่นี่และที่นี่ )
- คุณจะทำตามขั้นตอนที่คล้ายกันหากคุณ
AsyncTask
อยู่ในระดับบนสุด คลาสภายในแบบสแตติกนั้นเหมือนกับคลาสระดับบนสุดใน Java
หากคุณไม่ต้องการกิจกรรม แต่ยังต้องการบริบท (เช่นเพื่อแสดง a Toast
) คุณสามารถส่งผ่านการอ้างอิงถึงบริบทแอป ในกรณีนี้ตัวAsyncTask
สร้างจะมีลักษณะเช่นนี้:
private WeakReference<Application> appReference;
MyTask(Application context) {
appReference = new WeakReference<>(context);
}
- มีข้อโต้แย้งบางอย่างที่มีสำหรับการละเว้นคำเตือนนี้และเพียงแค่ใช้คลาสที่ไม่คงที่ ท้ายที่สุด, AsyncTask ตั้งใจจะสั้นมาก (สองสามวินาทีที่ยาวที่สุด), และมันจะปล่อยการอ้างอิงถึงกิจกรรมเมื่อเสร็จสิ้นต่อไป. ดูนี้และนี้
- บทความที่ยอดเยี่ยม: วิธีการรั่วบริบท: ตัวจัดการ & ชั้นใน
Kotlin
ใน Kotlin ไม่ได้รวมinner
คำหลักสำหรับชั้นใน สิ่งนี้ทำให้คงที่โดยค่าเริ่มต้น
class MyActivity : AppCompatActivity() {
internal var mSomeMemberVariable = 123
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// start the AsyncTask, passing the Activity context
// in to a custom constructor
MyTask(this).execute()
}
private class MyTask
internal constructor(context: MyActivity) : AsyncTask<Void, Void, String>() {
private val activityReference: WeakReference<MyActivity> = WeakReference(context)
override fun doInBackground(vararg params: Void): String {
// do some long running task...
return "task finished"
}
override fun onPostExecute(result: String) {
// get a reference to the activity if it is still there
val activity = activityReference.get()
if (activity == null || activity.isFinishing) return
// modify the activity's UI
val textView = activity.findViewById(R.id.textview)
textView.setText(result)
// access Activity member variables
activity.mSomeMemberVariable = 321
}
}
}