برمجة الأندرويد

الـ AsyncTask البديل البسيط للـ Thread فى الأندرويد

عندما تريد القيام بعملية تستغرق وقت كبير كـ اتصال بالانترنت أو استعلام فى قاعدة بيانات كبيرة الحجم فانه يجب عليك تنفيذ هذه العملية فى Thread اخر بعيدا عن الـ Mainthread  فببساطه عندما يعمل التطبيق يكون هناك Thread رئيسى أو اساسى يدعى بالـ Main Thread او UI Thread  او الـ Thread الرئيسى واذا كانت المرة الأولى لك تسمع كلمة Thread وتشعر بانها غريبه فدعنا نتخيل أن التطبيق عباره عن شركة والـ Main Thread هو صاحب الشركة ومديرها  والسؤال هنا هل يستطيع المدير (Main Thread) القيام بأعمال الشركة كلها من ادارة وتسويق ومبيعات وتنظيف الشركة وحراستها و مقابلة العملاء والقيام بباقى الامور ؟

بالتأكيد لا .. لكن السؤال لما لا ؟ أقصد لماذا لا يستطيع مدير الشركة القيام بكل الاعمال ؟!

الاجابة ببساطه لأنه كشخص واحد يستطيع تنفيذ مهمة واحده فقط فى الوقت نفسه لا يستطيع المدير ان يقوم بانهاء العمل المحاسبى وتسجيل الفواتير فى نفس الوقت الذى يقوم فيه بمقابلة احد عملاء الشركة  فاهو اما ان يقابل العميل او ينجز العمل المحاسبى  .

وقد تفكر فى فكرة ذكية وتقول لى لماذا لا يقابل العميل ثم يقوم بالعمل المحاسبى وهكذا  ينجز الاعمال بشكل تسلسلى ؟

حسنا كلامك صحيح لكن عندما يقوم المدير بمقابلة احد العملاء فهو لا يستطيع انجاز العمل المحاسبى وان كان يقوم بالعمل المحاسبى فانه لا يستطيع مقابلة العميل وبالتالى سوف يركز على مهمة واحدة .

لذلك لو عدنا للأندرويد واردت إنجاز مهمة تتعلق بالانترنت أو اى مهمة اخرى ستأخذ الكثير من الوقت فإن الـ MainThread سينشغل بهذه المهمة وسيتوقف عن الاستجابة لأى شىء اخر حتى يقوم بانهاء مهمة الانترنت وبالتالى فى هذه الفترة تتجمد الواجهة فعندما تضغط على زر لتنفيذ كود معين فى التطبيق فانه لا يستجيب لان الـ MainThread  مشغول الان فى task الانترنت وحينها سيتدخل نظام  الاندرويد لانقاذ المستخدم من تطبيقك لانه سيكون متجمد  ويظهر له الشاشة المعروفة بالـ ANR اى ان التطبيق لا يستجيب هل تريد اغلاقه ام الانتظار .

 

kha

main-qimg-e704dbc1708a471bd5b9439ef69d7736

 

لذلك لا بد من انجاز اى امور تستغرق وقتا فى Thread اخر  (فى المثال السابق تعيين شخص اخر فى الشركة ليقوم بمهمة او عدة مهام اخرى  محددة ) .

ويعتبر موضوع الـ Threads من الموضوعات المتقدمة ليس فى الاندرويد فقط بل فى الجافا بشكل عام  وهناك الكثير من الامور المتعلقة به  تحتاج فهمها جيدا قبل التعامل معه ولذلك وفر لك الاندرويد بديل بسيط جدا وهو الـ AsyncTask تستطيع استخدامه لإجراء العمليات التى ستستغرق وقتا  حيث يسهل عليك الكثير من الامور مثل فعل شىء قبل تنفيذ المهمة وتنفيذ المهمة وفعل شىء بعد تنفيذ المهمة وتحديث متزامن للمهمة مع البروجرس بار ان وجد وهى أمور لا يمكن تنفيذها بسهولة باستخدام الـ Threads العادية  .

 

الـ AsyncTask

 

يمكنك استخدام الـ AsyncTask بشكل سريع هكذا

asynctasksimple

قمنا بعمل new AsyncTask وهنا أخذ ثلاثة محددات أو انواع  سنشرحهم بعد قليل لكن الان اعطيتهم جميعا  Void  ثم تم عمل ovverride  الزامى للميثود doInBackground  وهى الـ method المسؤولة عن تنفيذ المهمة الطويلة او التى ستسغرق وقتا وهى تأخذ بارامتر على params والـ ثلاث نقاط معناها انها يمكن تمرير اى عدد من البارامترز من النوع Void لها . وتعود لنا بـ Void ايضا .

و مكان الكومنت task here يمكنك وضع الكود الذى سيتم تنفيذه فى هذه الـ asynctask .

 

نتحدث الان عن المحددات او الانواع التى اخذتها الـ AsyncTask والتى وضعهتم مؤقتا Void

فى الحقيقة هذه المحددات  كالتالى :

asynctask

 

Params  : نوع المدخلات التى ستقوم بتمريرها الى الـ AsyncTask على سبيل المثال اذا كنت تقوم بعمل task للاتصال بالانترنت فبالتأكيد ستحتاج الى تمرير اللينك او الرابط لأنك قد تستخدم الـ AsyncTask للاتصال لكل الروابط فى تطبيقك  وبالتالى تريد فى كل مرة تقوم بتنفيذها باستخدام رابط ومختلف وبذلك لا بد من وجود مدخلات تمررها لداخل الـ AsyncTask  او مثلا تقوم بعمل task للبحث فى قاعدة بيانات كبيرة تريد تمرير الـ query والذى سيكون على هيئة String وبالتالى يجب جعل الـ Params هى String  واذا كنت ستمرر Integer ستجعل الـ Params من من النوع Integer وهكذا  .

 

Progress : بعض التاسكات التى ستقوم بتنفيذها احيانا تريد تنفيذ كود معين مع تقدم تنفيذ العملية باستمرار كلما تقدمت العملية فى التنفيذ  تنفذ كود معين  وأشهر مثال على ذلك عندما تريد عمل Progressbar للعملية يتقدم بتقدم الـ Task او بانجاز اجزاء منها لذلك تضع هنا نوع الـ Progrss وفى حالة الـ Progrss bar بالتأكيد سنقوم بوضع Integer  لاننا مع تقدم العملية نريد تمرير int يزيد بتقدم التنفيذ .

 

Result  : وهو كود ناتج الtask التى ستتم فى الخلفية عندما تنتهى هل مثلا نقوم بعمل httpurlconnection لصفحة انترنت وسيقوم بارجاع JSONObject فنضعه هنا أم نقوم باجراء عملية حسابية معقدة مثلا وسيكون الناتج int فنضع Integer أم سيكون الناتج عن الـ task هو String وبالتلى نضعها هنا .

 

وفى حالة لم يكن لديك Params  أو لم تريد Progress أو لا تهتم بالـ Result يمكنك أن تضع Void بدلا من النوع كما فعلت انا فى المثال السابق .

 

وتعتبر الـ Method السابقة doInBackground الزامية فى الـ AsyncTask كما رايت بالسابق لكن هناك عدة ميثود اختيارية رائعة اخرى توفرها لك الـ AsyncTask   وتفيدك عند انشاء task  وهى :

 

onPreExecute : وهى ميثود يتم تنفيذها فى الـ MainThread قبل بدء الـ task لذلك اى امر تريد تنفيذه قبل بدء الـ task مباشرة قم بكتابة الكود بداخلها على سبيل المثال اظهار dialog للمستخدم .

onPreExecute

 

onPostExecute : وهى ميثود يتم تنفيذها فى الـ MainThread ايضا لكن  بعد انتهاء تنفيذ الـ Task  وتجد أن لديها بارامتر  وهو هنا String s وهو عباره عن ناتج الـ doInBackground بعد انتهائها فاذا كان هذا اتصالا بالانترنت يتم فى الـ doInBackground ويعود بـ String فان  s الان تساوى الـ String هذا ويمكنك هنا تحويله الى اوبجكت جيسون او التعامل معه باى طريقة تحب وهذه الميثود ايضا تنفذ فى الـ mainthread

onPostExecute

 

 

onProgressUpdate : وهذه الميثود تستدعى عندما تنادى ميثود اخرى تدعى publishProgress من داخل الـ donInBackground  وهى المسؤولة عن تحديث الـ Progress او زيادة مستوى تنفيذ التاسك وهى تتم تنفيذها فى الـ Main Thread أيضا

onProg

 

هذه هى أهم الـ Methods الموجودة فى الـ AsynTask  والان يبدو الامر واضحا عندما تقوم باستدعاء الـ Task  للتنفيذ عن طريق الميثود execute  تتم العملية كالاتى :

يتم تنفيذ كود الـ onPreExecute

يتم تنفيذ كود doInBackground واثناء تنفيذها لو تم مناداة publishProgress يتم تنفيذ الكود الموجود بـ onProgressUpdated

يتم تنفيذ كود الـ onPostExecute

 

 

مثال تطبيقى  على الـ AsyncTask   

 

 

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    ProgressBar bar;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportActionBar().hide();
        bar = (ProgressBar) findViewById(R.id.progressBar);
        textView = (TextView) findViewById(R.id.textView);
        bar.setProgress(0);
        bar.setMax(100);

        new AsyncTask<String, Integer, String>() {

            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                // code will executed before task start (main thread)
            }

            @Override
            protected String doInBackground(String... params) {
                // task will done in background

                for (int i = 0; i < 100; i++) {
                    try {
                        // sleep 100 millisecond every loop so progress will not finished fast with out see it
                        Thread.sleep(100);
                        publishProgress(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                return null;

            }

            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                // code executed after task finish hide progress and change text
                bar.animate().alpha(0).setDuration(2000).start();
                textView.setText("Don't wait any thing Do it Now");
            }

            @Override
            protected void onProgressUpdate(Integer... values) {
                super.onProgressUpdate(values);
                // progress come as array maybe there is mote than one value or progress update so i put [0]
                bar.setProgress(values[0]);


            }


        }.execute();


    }

 

 

النتيجة

asyncexample

 

 

مثال اخر  كلاس اتصال بالانترنت باستخدام اسينك تاسك

import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class MainActivity extends AppCompatActivity {
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportActionBar().hide();
        textView = (TextView) findViewById(R.id.textView);
        
        new InternetTask().execute("http://developerhendy.16mb.com/helloworld.php");

    }

    class InternetTask extends AsyncTask<String, Integer, String> {
        ProgressDialog dialog;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            dialog = new ProgressDialog(MainActivity.this);
            dialog.setTitle("Wait ");
            dialog.setMessage("I am Connecting .. ");
        }

        @Override
        protected String doInBackground(String... params) {
            // task will done in background
            String link = params[0];
            String result = null;
            try {
                URL url = new URL(link);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                InputStreamReader streamReader = new InputStreamReader(connection.getInputStream());
                BufferedReader reader = new BufferedReader(streamReader);
                result = reader.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return result;


        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            textView.setText(s);
            dialog.dismiss();

        }


    }


}

ولاحظ اننى مررت الـurl عن طريق الميثود execute ويمكنك تمرير اكثر من url او اكثر من شىء واستقبالهم داخل doInBackground عن طريق params[i] حسب ترتيب البارامترز .

ولاحظ انى قمت بعمل كلاس باسم Internttask  يقوم بعمل extend   وبالتالى استطيع استخدام الكلاس InternetTask فى عمل اتصالات اخرى بالانترنت فقط سأعطيه الرابط كل مرة فى الميثود execute .

 

النتيجة

Screen Shot 2016-02-22 at 9.14.35 AM

 

 

ومع كل هذه المميزات الا ان الـ AsyncTask لديه بعض العيوب عند عمل rotate للشاشة من قبل المستخدم تحدث مشكلة وايضا عند ايقاف الاكتيفيتى لا يتوقف الـ asynctask مما تجعلك هذه العيوب تحاول حلها فى تطبيقك باستخدام عدة حيل .. لكن هناك بديل افضل  يتجنب هذه المشاكل وهو الـ AsyncTaskLoader  ربما قد نتطرق له فى تدوينة لاحقا .

السابق
ما يجب أن يعرفه كل مبرمج عن الـ Http
التالي
الصلاحيات فى أندرويد مارشميلو

10 تعليقات

أضف تعليقا

  1. Ahmed Ali قال:

    شرح جميل لكن انا بفضل اعمل ال Thread العادية 😀
    طلب بسيط هل ممكن تشرحو GCM لل notifications لان شايف ان الموضوع دا قليل جدا
    ربنا يعطيك العافية 🙂

    1. Hendiware قال:

      معمول حسابه من فترة قريب ان شاء الله هينزل واحتمال كبير انه يكون شرح فيديو .
      منورنا دائما يا أحمد .

      1. Ahmed Ali قال:

        الله يخليك دا نورك 🙂
        مشكور علي المجهود الرائع دا

  2. محمد قال:

    شرح وافي كافي ممتع ..
    اضافة الأمثلة للموضوع أكثر من رائعة
    ننتظر جديدكبفارغ الصبر
    بارك الله فيك

  3. mohammed-alsatouF قال:

    شرح ممتاز ولقيته بالوقت المناسب
    شكرا

  4. مصرى قال:

    يعنى ايه getSupportActionBar().hide();
    و Thread.sleep(100);

    1. Hendiware قال:

      مرحبا مصرى ،
      getSupportActionBar().hide(); تعنى الحصول على الـ toolbar الحالى واخفاؤه .
      و Thread.sleep(100); تعنى ايقاف الثريد الحالى 100 ملى ثانية او 1/10 من الثانية قم بمراجعة درسين الـ MultiThreading الموضوعين هنا فى الموقع .

  5. ismail قال:

    ممتاز

  6. عمر قال:

    فعلا انت عبقري في شرحك و خصوصا الكومكسات اللي بتحطها في نص الدرس 🙂 🙂 … شكرا

  7. ِAhmed Torres قال:

    شرح ممتاز جدا جزاك الله خيرا
    عندي استفسار بستخدم async task في عمل اتصال بملف php وعايز كل مرة اغير اللينك علي حسب الاختيار عمل ايه عشان جربت ابعت كل مرة ال Url بس النتيجة مش بتكون مظبوطة كل مرة وبيكرر نفس النتيجة احيانا

اترك تعليقاً

This site uses Akismet to reduce spam. Learn how your comment data is processed.