كثير من مطوري الاندرويد يقومون باستخدام AsyncTask وخاصة عندما يريدون تنفيذ بعض الاكواد في thread منفصل, ولكن كثير منهم يتعامل مع هذا الكلاس بطريقة خاطئة ودون فهم لدورة حياة الاندرويد وما الذي يحدث بالضبط في الخلفية, في هذه المقالة سنوضح الخطأ الشائع وكيف تقوم بمعالجة المشكلة.

الاستخدام الخاطئ

الطريقة الشائعة والتي تجدها في معظم المواقع والتي ستجدها بالتاكيد في معظم مشاريع مطوري الاندرويد هي كالتالي:

public class MainActivity extends Activity {     @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);   }     // Somewhere the AsyncTask is started     public class MyAsyncTask extends AsyncTask<Void, Void, String> {       @Override protected String doInBackground(Void... params) {       // Do work       return result;     }       @Override protected void onPostExecute(String result) {       Log.d("MyAsyncTask", "Received result: " + result);     }   } }

المشكلة بالكود هي ان كلاس  AsyncTask مضمن بداخل الـ Activity وبالتالي اي تغيير في حالة الـ Activity والتي قامت باستدعاء الـ AsyncTask الموجود بداخلها قد يقتل عمل الـ AsyncTask, ولكن لن يتم ازالة الـ Activity من الذاكرة الا في حالة انتهاء الـ AsyncTask.

الان لتتخيل معي المثال التالي: لدي اكتفتي واحدة وبداخلها AsyncTask وفي كل مرة اقوم باستدعاء الاكتفتي فانا استدعي AsyncTask جديد واي تغيير في حالة الاكتفتي ربما استدعي AsyncTask اخر, وكل واحد من الـ AsyncTask لديه عملية يجب عليه ان ينفذها قبل ان يُقتل وقبل ان يسمح للـ Activity بان تزال من الذاكرة.

ماسيحدث بالمثال بالاعلى هو مشكلتين, الاولى هي مشكلة في الذاكرة memory issues والثانية هي انك ستفقد العمليات التي قام الـ AsyncTask بتنفيذها وستقوم بتكرار العملية اكثر من مرة.

### معالجة المشكلة

لمعالجة المشكلة بالاعلى فالافضل ان تقوم باستخدام مبدأ الـ event bus وطريقة تنفيذه كالتالي:

  • نقل الـ AsyncTask الى كلاس منفصل عن الـ  Activity.
  • انشاء كلاس جديد يقوم بتخزين ناتج  AsyncTask عند اكتماله.
  • ربط الـ الـAsyncTask بالـ Activity الخاصة بك عن طريق الـ bus.

لذلك سنقوم بانشاء كلاس جديد تحت مبدأ الـ Singleton ويكون كالتالي:

public class MyBus {`` private static final Bus BUS = new Bus();``  public static Bus getInstance() {`` return BUS;``   `` }``}

ولنفترض ان الـ AsyncTask سيقوم بارجاع String فقط عن اكتمال عمله, لذلك سنقوم بانشاء  كلاس جديد وليكن اسمه AsyncTaskResultEvent :
public class AsyncTaskResultEvent { private String result; public AsyncTaskResultEvent(String result) { this.result = result; } public String getResult() { return result; } }

نقوم بانشاء كلاس الـ AsyncTask والذي سيقوم بتنفيذ اي كود تريده:

public class MyAsyncTask extends AsyncTask { @Override protected String doInBackground(Void... params) { Random random = newRandom(); final long sleep = random.nextInt(10); try{ Thread.sleep(sleep * 1000); } catch(InterruptedException e) { e.printStackTrace(); } return"Slept for "+ sleep + " seconds"; } @Override protected void onPostExecute(String result) { MyBus.getInstance().post(newAsyncTaskResultEvent(result)); } }

اخيرا سنقوم بربط الـ AsyncTask  مع الـ Activity ولكن يجب عليك بالغاء الربط بينهم في ميثود onDestroy حتى لانقع في نفس المشكلة من جديد:

public class MainActivity extends Activity {   @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);     findViewById(R.id.button).setOnClickListener(newView.OnClickListener() {       @OverridepublicvoidonClick(View v) {         newMyAsyncTask().execute();       }     });     MyBus.getInstance().register(this);   }   @Override protected void onDestroy() {     MyBus.getInstance().unregister(this);     super.onDestroy();   }   @Subscribe public void onAsyncTaskResult(AsyncTaskResultEvent event) {     Toast.makeText(this, event.getResult(), Toast.LENGTH_LONG).show();   } }
### اخيراً
هذه الطريقة ليست محصورة مع الـ AsyncTask بل تستطيع استخدامها مع اي امر اخر, فعلى سبيل المثال الـ services لديه نفس المشكلة اذا استخدمتها بطريقة خاطئة وحلها هو باستخدام الـ event bus.