برنامه نویسی اندروید - دانشگاه صنعتی قم

این وبلاگ کانال ارتباطی با دانشجویان درس«برنامه نویسی موبایل» دانشگاه صنعتی قم می باشد.

برنامه نویسی اندروید - دانشگاه صنعتی قم

این وبلاگ کانال ارتباطی با دانشجویان درس«برنامه نویسی موبایل» دانشگاه صنعتی قم می باشد.

محتویات این وبلاگ با هدف آموزش اندروید در دوره درسی "برنامه نویسی موبایل" در دانشگاه صنعتی قم توسط اینجانب گردآوری شده است.
تعداد زیادی از مطالب ترجمه شده در این وبلاگ توسط دانشجویان دانشگاه صنعتی قم به عنوان پروژه های این درس ترجمه شده است.
در صورتی که تصمیم به تماس با من دارید، می توانید از طریق آدرس ایمیل qut دات android در جی میل دات کام با من تماس بگیرید.
-------------------
اگر بتوانم به شما کمکی در زمینه برنامه نویسی اندروید بکنم، خوشحال خواهم شد با این حال اگر پرسشی را ارسال کردید و بنده نیز به دلیل مشکلات و مشغله نتوانستم پاسخگو باشم، پوزش بنده را پیشاپیش پذیرا باشید.
-------------------
چون افراد زیادی از من درخواست می کنند تا برنامه نویسان اندروید به خصوص در شهر قم را به آنها معرفی کنند، اگر تمایل دارید رزومه و یا مشخصات خود را برایم ارسال کنید تا در صورت وجود درخواستهایی از این دست به شما اطلاع دهم.
از نظر من محدویتی به شهر قم وجود ندارد، لذا اگر برنامه نویس اندروید در شهرهایی دیگر هستید و یا به دنبال نیروی برنامه نویس اندروید هستید با من در تماس باشید، شاید بتوانم در این زمینه به شما کمکی بکنم :)
ناگفته نماند از آنجایی که در فضای مجازی من شناختی از هیچ شخص یا شرکتی ندارم، اگر شخصی به دنبال نیروی کار باشد، به افرادی که با مشخصات درخواستی ایشان تطابق داشته باشند، اطلاع خواهم داد و توصیه می شود برنامه نویسان محترم نیز قبل از شروع به همکاری، ملاحظات لازم در این زمینه را به عمل آورند چون من نیز شناختی از طرف مقابل ندارم.

طبقه بندی موضوعی

20- پروسس ها و نخها (Threads)

شنبه, ۳ خرداد ۱۳۹۳، ۱۲:۲۳ ق.ظ

مترجم: http://developer.android.com/guide/components/processes-and-threads.html

آدرس مطلب اصلی: احمد کریمی - حسن دربندی

زمانی که یک component برنامه شروع می شود و برنامه componentهای در حال اجرای دیگری ندارد، سیستم Android یک process لینوکسی جدید برای برنامه با یک single thread of execution شروع می کند. به صورت پیش فرض، همه ی component های همان برنامه در همان process و thread (که به آن main thread می گویند) اجرا می شود. اگر یک component برنامه شروع شود و برای آن برنامه همچنان یک process وجود داشته باشد (چون component دیگری از برنامه وجود دارد)، سپس component در مدت آن process شروع می شود و از همان thread اجرا استفاده می کند. هرچند شما می توانید component های متفاوت موجود در برنامه خود را مرتب کنید تا در process های جداگانه ای اجرا شوند و thread های اضافی برای هر process ایجاد کنید.

این سند چگونگی عملکرد process ها و thread ها را در یک برنامه اندروید مورد بحث و بررسی قرار می دهد.

process ها

به صورت پیش فرض، همه ی component های همان برنامه در همان process اجرا می شوند و اکثر برنامه ها نباید این مسئله را تغییر دهند. هرچند، اگر شما بفهمید که نیاز دارید کنترل کنید که یک component مشخص به کدام process تعلق دارد، شما می توانید این کار را در فایل manifest انجام دهید.

ورودی مانیفست برای هر گونه component از یک ویژگی android:process که می تواند یک process را که component باید در آن اجرا شود مشخص کند پشتیبانی می کند. شما می توانید این ویژگی را طوری تعیین کنید که هر component در process خودش اجرا شود یا بعضی از component ها یک process را به اشتراک بگذارند در حالی که دیگر component ها این کار را انجام نمی دهند. شما همچنین می توانید طوری android:process را تعیین کنید که component های برنامه های متفاوت در process یکسان اجرا شوند – به شرطی که برنامه ها شناسه Linux یکسانی را به اشتراک بگذارند و با گواهینامه های یکسان وارد شده باشند.

عنصر <application> هم از ویژگی android:process برای تعیین مقدار پیش فرض جهت اعمال به همه ی component ها پشتیبانی می کند.

اندروید تصمیم می گیرد که در برخی موارد یک process را ببندد، زمانی که حافظه کم است و توسط process های دیگری که بلافاصله به کاربر خدمت رسانی می کنند درخواست شده است. آن دسته از component های برنامه که در processی که از بین رفته در حال اجرا هستند در نتیجه ی آن از بین می روند. زمانی که دوباره کاری برای آن component ها وجود داشته باشد که انجام دهند یک process برای آن ها شروع می شود.

در زمان تصمیم گیری برای نابود کردن یک process، سیستم اندروید اهمیت نسبی آن ها برای کاربر را می سنجد. به عنوان مثال، سیستم اندروید processی را که میزبان activity هایی است که دیگر در صفحه قابل رویت نیستند با سهولت بیشتری می بندد، در مقایسه با processی که میزبان activity های قابل رویت است. بنابراین تصمیم گیری درباره ی به پایان رساندن یک process به وضعیت component های در حال اجرا در آن process بستگی دارد. قوانین استفاده شده در تصمیم گیری درباره به پایان رساندن یک process در ادامه مورد بررسی قرار گرفته.

چرخه ی حیات process

سیستم اندروید تلاش می کند تا از یک process برنامه تا زمانی که ممکن است نگهداری کند، اما سرانجام نیاز دارد که برای نجات دادن حافظه برای process های جدید یا مهم تر process های قدیمی را حذف کند. برای تعیین کردن کدام process ها نگه داشته شود و کدام process ها از بین برود، سیستم process ها را در یک "سلسله مراتب اهمیت" که مبتنی است بر component های در حال اجرا در process و وضعیت آن component ها قرار می دهد. process های کم اهمیت تر ابتدا حذف می شوند، سپس آن هایی که در درجه اهمیت بعدی قرار دارند، و به همین ترتیب، تا زمانی که برای بازیابی منابع سیستم ضرورت دارد.

5 سطح در سلسله مراتب اهمیت قرار دارد. لیستی که در زیر آمده انواع مختلف process ها را به ترتیب اهمیت نشان می دهد (process اول مهم ترین است و در آخر از بین می رود):

process های پیش زمینه

processی که برای آنچه که کاربر در حال حاضر در حال انجام دادن است مورد نیاز است. یک process پیش زمینه در نظر گرفته می شود اگر هر یک از شرایط زیر صادق باشد:

  • میزبان یک activity باشد که کاربر در حال تعامل با آن است (متد onResume() activity فراخوانی شده است).
  • میزبان سرویسی باشد که محدود به activity ای است که کاربر در حال تعامل با آن است.
  •  میزبان سرویسی است که در پیش زمینه در حال اجراست – سرویس startForground() را فراخوانی کرده است.
  • میزبان سرویسی است که در حال اجرای یکی از کال بک های چرخه ی حیاتش است (onCreate() ، onStart() یا onDestry()).
  • میزبان یک BroadcastReciever است که در حال اجرا متد onRecieve() خودش است.

عموما، فقط تعداد کمی از process های پیش زمینه در هر زمان داده شده وجود دارند. آن ها فقط به عنوان آخرین پناهگاه از بین می روند – اگر حافظه خیلی کم باشد به طوری که همه آن ها نتوانند اجرا شوند. عموما، در آن نقطه، دستگاه به یک وضعیت memory paging رسیده است، بنابراین از بین بردن بعضی از process های پیش زمینه لازم است تا رابط کاربری پاسخگو باقی بماند.

process های قابل رویت

processی که هیچ component پیش زمینه ای ندارد، اما هنوز می تواند بر آنچه که کاربر در صفحه می بیند تاثیر بگذارد. یک process قابل رویت در نظر گرفته می شود اگر هر یک از شرایط زیر صادق باشد:

  • میزبان یک activity باشد که پیش زمینه نیست، اما هنوز برای کاربر قابل رویت است (متد onPause() آن فراخوانی شده است). این مسئله می تواند رخ دهد به عنوان مثال اگر activity پیش زمینه یک دیالوگ را آغاز کند که به activity قبلی اجازه دهد که پشت آن دیده شود.
  • میزبان سرویسی باشد که محدود به activity ای است که کاربر در حال تعامل با آن است.

یک process قابل رویت خیلی مهم در نظر گرفته می شود و از بین نمی رود مگر اینکه از بین رفتن آن برای در حال اجرا نگاه داشتن همه ی process های پیش زمینه نیاز باشد.

process های سرویس

processی که در حال اجرای سرویسی است که با متد startService() آغاز شده است و داخل دسته های بالاتر قرار نمی گیرد. اگرچه process های سرویس مستقیما ربطی به آنچه کاربر می بیند ندارد، عموما در حال انجام چیزهایی هستند که برای کاربر مهم است (مثل پخش موسیقی در پس زمینه یا دانلود داده از اینترنت)، پس سیستم آن ها را در حال اجرا نگه می دارد مگر اینکه حافظه کافی برای نگاه داشتن آن ها همراه با همه ی process های پیش زمینه و process های قابل رویت نباشد.

process های پس زمینه

آن دسته از process هاست که یک activity را که در حال حاضر برای کاربر قابل رویت نیست نگاه می دارد (متد onStop() مربوط به activity فراخوانی شده است). این process ها تاثیر مستقیمی بر تجربه ی کاربری ندارند، و سیستم می تواند در هر زمانی آن ها را برای حفظ حافظه برای یک process پیش زمینه، قابل رویت یا سرویس از بین ببرد. معمولا process های پس زمینه ی بسیاری در حال اجرا هستند، بنابراین در یک لیست LRU نگهداری می شوند تا مطمئن شوند.... اگر یک activity متدهای چرخه ی حیاتش را به درستی اجرا کند، و وضعیت جاری خود را حفظ کند، از بین بردن process آن تاثیر نمایانی بر تجربه کاربری ندارد، زیرا زمانی که کاربر به activity برمی گردد، همه ی وضعیت نمایان خود را به حال اول بر می گرداند. سند Activities را برای کسب اطلاعات درباره ی ذخیره سازی و بازیابی وضعیت ببینید.

process خالی

process ی که هیچ component برنامه فعالی را نگاه نمی دارد. تنها دلیل زنده نگه داشتن این نوع process، cache کردن اهداف جهت بهبود زمان راه اندازی برای دفعه ی بعدی است که یک component می خواهد در آن اجرا شود. سیستم معمولا این process ها را به منظور ایجاد تعادل در تقسیم منابع بین کش های process و کش های اساسی کرنل از بین می برد.

اندروید یک process را بر اساس اهمیت component هایی که در حال حاضر در آن فعال هستند، در بالاترین سطحی که می تواند درجه بندی می کند. به عنوان مثال اگر یک process میزبان یک سرویس و یک activity قابل رویت باشد، این process به عنوان یک process قابل رویت درجه بندی می شود نه یک process سرویس.

علاوه بر این، رتبه ی یک process ممکن است رشد کند، چون دیگر process ها به آن وابسته هستند – آن process که به دیگر process ها خدمت رسانی می کند هیچ وقت نمی تواند رتبه ی کمتری نسبت به process هایی که به آن ها خدمت می دهد داشته باشد.

 

Threadها

زمانی که یک برنامه کاربردی راه اندازی می شود، سیستم یک thread اجرا برای برنامه ایجاد می کند که "main" خوانده می شود. این thread بسیار مهم است زیرا مسئول اعزام eventها به widgetهای رابط کاربری مناسب، از جمله eventهای طراحی است. همچنین این threadی است که در آن برنامه شما با componentهای android ui در تعامل است(componentهایی از packageهای android.widget و android.view). به این ترتیب، main thread گاهی اوقات UI thread نیز خوانده می شود.

این سیستم برای هر نمونه از componentها یک thread جداگانه ایجاد نمی کند. همه ی componentهایی که در همان process اجرا می شوند در ui thread نمونه سازی می شوند و سیستم همه ی componentهای اعزام شده از آن thread را فراخوانی می کند. در نتیجه، متدهایی که به callbackهای سیستم پاسخ می دهند (مثل onKeyDown() جهت گزارش اعمال کاربر یا یک متد مربوط به callback چرخه حیات) همواره در UI thread مربوط به process اجرا می شوند.

به عنوان مثال، زمانی که کاربر یک دکمه موجود در صفحه را لمس می کند، UI thread مربوط به برنامه ی شما event لمس را به widget می فرستد، که به نوبه خود حالت فشرده شده (فشار داده شده) ی خود را تنظیم کرده و یک درخواست بی اعتبار را به صف event ارسال می کند. UI thread درخواست را از صف خارج کرده و به widget اطلاع می دهد که باید خود را از نو ترسیم کند.

هنگامی که برنامه ی شما کار خود را در پاسخ به تعامل با کاربر انجام می دهد این مدل thread واحد می تواند عملکرد ضعیفی ارائه دهد مگر اینکه شما برنامه ی کاربردی خود را به درستی پیاده سازی کنید.

علاوه بر این، Android UI toolkit ایمن از thread نیست. بنابراین شما نباید رابط کاربری خود را از طریق یک worker thread دستکاری کنید – شما باید همه ی دستکاری ها در رابط کاربری خود را از طریق UI thred انجام دهید. بنابراین، دو قانون ساده برای مدل thread واحد Android وجود دارد:

  1. UI thread را مسدود نکنید
  2. خارج از UI thread به Android UI toolkit دسترسی پیدا نکنید

Worker threadها (thread های کارگر)

از آنجا که مدل thread واحد در بالا شرح داده شد، برای پاسخگویی رابط کاربری برنامه شما حیاتی است که UI thread را مسدود نکنید. اگر شما اعمالی برای انجام دارید که آنی نیستند، باید اطمینان حاصل کنید که آن ها را در threadهای جداگانه انجام دهید (threadهای background یا worker).

به عنوان مثال، در ادامه کدی برای یک click listener که یک عکس را از یک thread جداگانه دانلود کرده و آن را در یک ImageView نمایش می دهد.

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();

ابتدا به نظر می رسد که به درستی کار کند چون یک thread جدید برای کار با شبکه ایجاد می کند. هرچند قانون دوم single threaded model را نقض می کند: 1.      خارج از UI thread به Android UI toolkit دسترسی پیدا نکنید – این نمونه ImageView را بجای UI thread از طریقworker thread تغییر می دهد. این موضوع می تواند رفتار غیرمنتظره ای را نتیجه می دهد که پیگیری آن می تواند سخت و زمان باشد.

جهت رفع این مشکل Android راه های مختلفی را برای دسترسی به UI thread از threadهای دیگر پیشنهاد می دهد. لیستی از متدهایی را که می تواند کمک کند می توانید ببینید:

به عنوان مثال، می توانید کد بالا را با استفاده از متد View.post(Runnable) درست کنید:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

حالا این پیاده سازی thread-safe است: عملیات شبکه از طریق یک thread جداگانه انجام می شود درحالیکه ImageVeiw از طریق UI thread دستکاری می شود.

هرچند با رشد پیچیدگی عملیات، این نوع کد پیچیده شده و نگهداری از آن دشوار می شود. برای سر و کار داشتن با فعل و انفعالات پیچیده تر با استفاده از worker thread، برای پردازش پیغام های دریافت شده از UI thread، از یک Handler در worker thread خود استفاده کنید. شاید بهترین راه حل extend کردن کلاس AsyncTask است که اجرا شدن وظایف worker thread را که نیاز دارند با UI تعامل داشته باشند را ساده می کند.

استفاده از AsyncTask

AsyncTask به شما اجازه می دهد که کار غیر همزمان روی رابط کاربری خود انجام دهید. بدون نیاز به سروکار داشتن شما با threadها و handlerها AsyncTask مسدود کردن عملیات ها در یک worker thread را انجام می دهد و سپس نتایج را در UI thread منتشر می کند.

برای استفاده از آن شما باید AsyncTask را subclass کرده و متد doInBackground() را پیاده سازی کنید که در یک استخر از threadهای پس زمینه اجرا می شود. برای به روز رسانی رابط کاربری خود، شما باید onPauseExecute() را پیاده سازی کنید که نتیجه را از doInBackground() ارایه کرده و UI thread را اجرا می کند بنابراین می توانید با خیال راحت رابط کاربری خود را بروزرسانی کنید. سپس می توانید با فراخوانی execute() از UI thread این وظیفه را انجام دهید.

به عنوان مثال، شما می توانید مثال قبلی را با استفاده از AsyncTask به این روش پیاده سازی کنید:

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }
   
    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}

حالا رابط کاربری امن و کد ساده تر است، زیرا این (AsyncTask) کار را به بخشی که باید در یک worker thread انجام شود و بخشی که باید در UI thread انجام شود تقسیم می کند.

شما باید مرجع AsyncTask را به منظور فهم کامل نحوه استفاده این کلاس بخوانید اما اینجا یک دید کلی از نحوه عملکرد آن آمده است:

  • شما می توانید نوع پارامترها، مقادیر پیشرفت و مقدار نهایی کار را با استفاده از genericها مشخص کنید
  • متد onInBackground() به صورت خودکار در یک worker thread اجرا می شود
  • onPreExecute() ، onPostExecute() و onProgressUpdate() همه به UI thread استناد می کنند
  • مقدار برگردانده شده توسط doInBackground() به onPostExecute() فرستاده می شود
  • شما می توانید در هر زمانی publishProgress() را در doInBackground() برای اجرای onProgressUpdate() در UI thread فراخوانی کنید
  • شما می توانید وظیفه (task) را در هر زمانی از هر thread لغو کنید

توجه: یکی دیگر از مشکلاتی که ممکن است در زمان استفاده از یک worker thread با آن مواجه شوید، restartهای غیرمنتظره در activity شما با توجه به یک تغییر در پیکربندی زمان اجرا است (مثل زمانی که کاربر جهت گیری صفحه نمایش را تغییر می دهد)، که ممکن است worker thread شما را از بین ببرد. برای مشاهده اینکه چگونه می توانید کار خود را در طی یکی از این restartها ادامه دهید و اینکه چگونه به درستی زمانی که کار از بین رفت آن را لغو کنید، کد منبع برنامه نمونه قفسه ها را ببینید.

متدهای thread-safe

در برخی شرایط، متدهایی که پیاده سازی می کنید ممکن است در بیش از یک thread فراخوانی شوند، بنابراین باید thread-safe نوشته شوند.

این موضوع اساسا برای متدهایی که می توانند به صورت remote فراخوانی شوند درست است – مثل متدهایی که در یک سرویس محدود هستند. زمانی که یک متد پیاده سازی شده در یک IBinder از همان process که در آن در حال اجراست سرچشمه می گیرد، این متد در thread فراخوانی کننده اجرا می شود. با این حال، زمانی که یک فراخوانی در یک process دیگر سرچشمه دارد، این متد در یک thread که از یک استخر threadها انتخاب شده اجرا می شود که سیستم را در همان process به عنوان theIBinder حفظ می کند (این در UI thread مربوط به process اجرا نمی شود). به عنوان مثال، در حالی که متد onBind() سرویس می تواند از UI thread مربوط به process سرویس فراخوانی شود، متدهایی که در شیئی onBind() برمی گرداند پیاده سازی شده اند (به عنوان مثال، یک subclass که متدهای RPC را پیاده سازی می کند) ممکن است از طریق threadهای موجود در استخر فراخوانی شود. از آنجا که یک سرویس می تواند بیش از یک مشتری داشته باشد، بیش از یک thread استخر می تواند در همان زمان با همان متد IBinder درگیر شود. بنابراین متدهای IBinder باید طوری پیاده سازی شوند که thread-safe باشند.

به طور مشابه یک مهیا کننده ی محتوا می تواند درخواست های داده ای را دریافت کند که در processهای دیگری سرچشمه دارد. اگرچه کلاس های ContentResolver و ContentProvider جزئیات چگونگی مدیریت ارتباط بین پردازه ای را پنهان می کنند، آن دسته از متدهای ContentProvider که به آن درخواست ها پاسخ می دهند – متدهای query() ، insert() ، delete() ، update() و getType() - در process فراهم کننده، از یک استخر thread فراخوانی می شوند. از آنجا که این متدها ممکن است در یک زمان از هر تعدادی thread فراخوانی شوند، آن ها نیز باید thread-safe پیاده سازی شوند.

ارتباط بین پردازه ای

اندروید با استفاده از remote procedure calls (RPCs) یک مکانیزم برای ارتباط بین پردازه ای پیشنهاد می کند، که در آن یک متد توسط یک activity یا دیگر اجزای برنامه فراخوانی می شود، اما به صورت remote با هر نتیجه ای که به فراخوانی کننده برگردانده می شود اجرا می شود (در یک process دیگر). این امر مستلزم تجزیه ی یک فراخوانی متد و داده اش به یک سطحی که سیستم عامل بتواند بفهمد، انتقال آن از process محلی و فضای آدرس به process از نوع remote و فضای آدرس، سپس اسمبل کردن مجدد و تصویب مجدد فراخوانی در آن جا است. سپس مقادیر بازگردانده شده در جهت مخالف منتقل می شوند. اندروید همه ی کدهای مورد نیاز برای این تراکنش های IPC را فراهم می کند، پس شما می توانید روی تعریف کردن و پیاده سازی رابط برنامه نویسی RPC تمرکز کنید.

برای انجام دادن IPC، برنامه ی شما باید با استفاده از bindService() به یک سرویس متصل شود. جهت کسب اطلاعات بیشتر راهنمای توسعه دهنده ی Services را مشاهده کنید.

  • وهاب صمدی بخارایی

نظرات  (۱)

سلام.
ممنون بابت مطالب مفیدتون
خداخیرتون بده

ارسال نظر

کاربران بیان میتوانند بدون نیاز به تأیید، نظرات خود را ارسال کنند.
اگر قبلا در بیان ثبت نام کرده اید لطفا ابتدا وارد شوید، در غیر این صورت می توانید ثبت نام کنید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی