هنگامی که یک جزء برنامه شروع می شود و برنامه هیچ مؤلفه دیگری در حال اجرا ندارد، سیستم Android یک فرآیند لینوکس جدید را برای برنامه با یک رشته اجرا شروع می کند. به طور پیش فرض، تمام اجزای یک برنامه کاربردی در یک فرآیند و رشته یکسان اجرا می شوند که به آن نخ اصلی می گویند.
اگر یک مؤلفه برنامه شروع شود و از قبل یک فرآیند برای آن برنامه وجود داشته باشد، زیرا مؤلفه دیگری از برنامه قبلاً شروع شده است، مؤلفه در آن فرآیند شروع می شود و از همان رشته اجرا استفاده می کند. با این حال، میتوانید ترتیبی دهید که مؤلفههای مختلف در برنامه خود در فرآیندهای جداگانه اجرا شوند و میتوانید رشتههای اضافی برای هر فرآیندی ایجاد کنید.
این سند نحوه عملکرد پردازش ها و رشته ها در یک برنامه اندروید را مورد بحث قرار می دهد.
فرآیندها
بهطور پیشفرض، همه اجزای برنامه در یک فرآیند اجرا میشوند و اکثر برنامهها این را تغییر نمیدهند. با این حال، اگر متوجه شدید که باید کنترل کنید که یک جزء خاص به کدام فرآیند تعلق دارد، می توانید این کار را در فایل مانیفست انجام دهید.
ورودی مانیفست برای هر نوع عنصر مؤلفه - <activity>
، <service>
، <receiver>
و <provider>
—از ویژگی android:process
پشتیبانی میکند که میتواند فرآیندی را مشخص کند که جزء اجرا میشود. میتوانید این ویژگی را طوری تنظیم کنید که هر کامپوننت در فرآیند خود اجرا می شود یا به طوری که برخی از اجزای یک فرآیند را به اشتراک می گذارند در حالی که برخی دیگر این کار را نمی کنند.
همچنین میتوانید android:process
طوری تنظیم کنید که اجزای برنامههای مختلف در یک فرآیند اجرا شوند، مشروط بر اینکه برنامهها شناسه کاربری لینوکس یکسانی داشته باشند و با گواهینامههای یکسانی امضا شده باشند.
عنصر <application>
همچنین از ویژگی android:process
پشتیبانی میکند که میتوانید از آن برای تنظیم یک مقدار پیشفرض که برای همه مؤلفهها اعمال میشود، استفاده کنید.
اندروید ممکن است زمانی تصمیم بگیرد که فرآیندی را خاموش کند، زمانی که سایر فرآیندها به منابع نیاز دارند که سریعتر در خدمت کاربر هستند. مؤلفه های برنامه در حال اجرا در فرآیندی که خاموش شده اند، در نتیجه از بین می روند. زمانی که کاری برای آن مؤلفهها وجود داشته باشد، یک فرآیند دوباره شروع میشود.
هنگامی که تصمیم می گیرید کدام فرآیندها را خاموش کنید، سیستم اندروید اهمیت نسبی آنها را برای کاربر می سنجد. به عنوان مثال، در مقایسه با فرآیندی که میزبان فعالیتهای قابل مشاهده است، با سهولت بیشتری فعالیتهای میزبانی فرآیند را که دیگر روی صفحه قابل مشاهده نیستند، خاموش میکند. بنابراین، تصمیم در مورد خاتمه دادن به یک فرآیند به وضعیت اجزای در حال اجرا در آن فرآیند بستگی دارد.
جزئیات چرخه عمر فرآیند و ارتباط آن با حالت های برنامه در فرآیندها و چرخه عمر برنامه مورد بحث قرار گرفته است.
موضوعات
هنگامی که یک برنامه راه اندازی می شود، سیستم یک رشته اجرایی برای برنامه ایجاد می کند که به آن رشته اصلی می گویند. این موضوع بسیار مهم است، زیرا وظیفه ارسال رویدادها به ویجت های رابط کاربری مناسب از جمله رویدادهای ترسیمی را بر عهده دارد. همچنین تقریباً همیشه رشتهای است که در آن برنامه شما با مؤلفههای بستههای android.widget
و android.view
جعبه ابزار Android UI تعامل دارد. به همین دلیل، رشته اصلی گاهی اوقات رشته UI نامیده می شود. با این حال، تحت شرایط خاص، رشته اصلی برنامه ممکن است رشته رابط کاربری آن نباشد. برای اطلاعات بیشتر، حاشیه نویسی موضوع را ببینید.
سیستم یک رشته مجزا برای هر نمونه از یک جزء ایجاد نمی کند. تمام اجزایی که در یک فرآیند اجرا می شوند در رشته UI نمونه سازی می شوند و فراخوانی های سیستم به هر جزء از آن رشته ارسال می شود. در نتیجه، روشهایی که به تماسهای سیستم پاسخ میدهند - مانند onKeyDown()
برای گزارش اقدامات کاربر، یا یک روش بازگشت به تماس چرخه حیات - همیشه در رشته UI فرآیند اجرا میشوند.
به عنوان مثال، وقتی کاربر دکمهای را روی صفحه لمس میکند، رشته رابط کاربری برنامه شما رویداد لمسی را به ویجت ارسال میکند، که به نوبه خود حالت فشرده آن را تنظیم میکند و یک درخواست نامعتبر را در صف رویداد ارسال میکند. رشته UI درخواست را از صف خارج می کند و به ویجت اطلاع می دهد تا خودش را دوباره ترسیم کند.
اگر برنامه خود را به درستی پیاده سازی نکنید، این مدل تک رشته ای زمانی که برنامه شما کار فشرده ای را در پاسخ به تعامل کاربر انجام می دهد، می تواند عملکرد ضعیفی داشته باشد. انجام عملیات طولانی در رشته UI، مانند دسترسی به شبکه یا جستجوهای پایگاه داده، کل UI را مسدود می کند. وقتی رشته مسدود می شود، هیچ رویدادی از جمله رویدادهای ترسیمی قابل ارسال نیست.
از منظر کاربر، به نظر می رسد که برنامه معلق است. حتی بدتر از آن، اگر رشته UI برای بیش از چند ثانیه مسدود شود، گفتگوی " برنامه پاسخ نمی دهد " (ANR) به کاربر نمایش داده می شود. ممکن است کاربر تصمیم بگیرد که برنامه شما را ترک کند یا حتی آن را حذف نصب کند.
به خاطر داشته باشید که جعبه ابزار Android UI امن نیست . بنابراین، رابط کاربری خود را از یک موضوع کارگر دستکاری نکنید. تمام دستکاری ها را در رابط کاربری خود از رشته UI انجام دهید. دو قانون برای مدل تک رشته ای اندروید وجود دارد:
- رشته رابط کاربری را مسدود نکنید.
- به جعبه ابزار Android UI از خارج از رشته UI دسترسی نداشته باشید.
نخ های کارگری
به دلیل این مدل تک رشته ای، برای پاسخگویی رابط کاربری برنامه شما ضروری است که رشته رابط کاربری را مسدود نکنید. اگر عملیاتی دارید که باید آنی انجام دهید، مطمئن شوید که آنها را در پسزمینه یا رشتههای کارگری جداگانه انجام دهید. فقط به یاد داشته باشید که نمیتوانید UI را از هیچ رشتهای غیر از UI یا رشته اصلی بهروزرسانی کنید.
برای کمک به پیروی از این قوانین، Android راههای مختلفی برای دسترسی به رشته رابط کاربری از موضوعات دیگر ارائه میکند. در اینجا لیستی از روش هایی وجود دارد که می تواند کمک کند:
مثال زیر از View.post(Runnable)
استفاده می کند:
کاتلین
fun onClick(v: View) { Thread(Runnable { // A potentially time consuming task. val bitmap = processBitMap("image.png") imageView.post { imageView.setImageBitmap(bitmap) } }).start() }
جاوا
public void onClick(View v) { new Thread(new Runnable() { public void run() { // A potentially time consuming task. final Bitmap bitmap = processBitMap("image.png"); imageView.post(new Runnable() { public void run() { imageView.setImageBitmap(bitmap); } }); } }).start(); }
این پیادهسازی از نظر thread ایمن است، زیرا عملیات پسزمینه از یک رشته جداگانه انجام میشود در حالی که ImageView
همیشه از رشته UI دستکاری میشود.
با این حال، با افزایش پیچیدگی عملیات، این نوع کد می تواند پیچیده و نگهداری آن دشوار باشد. برای مدیریت تعاملات پیچیده تر با یک رشته کاری، ممکن است از یک Handler
در رشته کاری خود برای پردازش پیام های ارسال شده از رشته UI استفاده کنید. برای توضیح کامل نحوه زمانبندی کار بر روی رشتههای پسزمینه و برقراری ارتباط مجدد با رشته رابط کاربری، به نمای کلی کار پسزمینه مراجعه کنید.
روش های ایمن با نخ
در برخی موقعیتها، متدهایی که پیادهسازی میکنید از بیش از یک رشته فراخوانی میشوند، و بنابراین باید به گونهای نوشته شوند که ایمن باشند.
این در درجه اول برای روش هایی که می توان از راه دور فراخوانی کرد، مانند روش هایی در یک سرویس محدود صادق است. هنگامی که یک فراخوانی روی یک متد پیادهسازی شده در یک IBinder
از همان فرآیندی شروع میشود که در آن IBinder
در حال اجرا است، متد در رشته تماسگیرنده اجرا میشود. با این حال، هنگامی که فراخوانی در فرآیند دیگری منشأ میگیرد، این متد در رشتهای انتخاب شده از مجموعه رشتههایی که سیستم در همان فرآیند IBinder
نگهداری میکند، اجرا میشود. در رشته UI فرآیند اجرا نمی شود.
به عنوان مثال، در حالی که متد onBind()
یک سرویس از رشته UI فرآیند سرویس فراخوانی می شود، متدهای پیاده سازی شده در شیئی که onBind()
برمی گرداند، مانند زیر کلاسی که متدهای فراخوانی روش راه دور (RPC) را پیاده سازی می کند، از رشته ها فراخوانی می شوند. در استخر از آنجایی که یک سرویس میتواند بیش از یک کلاینت داشته باشد، بیش از یک رشته Pool میتواند به طور همزمان یک روش IBinder
را درگیر کند، بنابراین روشهای IBinder
باید پیادهسازی شوند تا از نظر thread ایمن باشند.
به طور مشابه، ارائهدهنده محتوا میتواند درخواستهای دادهای را دریافت کند که از فرآیندهای دیگر سرچشمه میگیرند. کلاس های ContentResolver
و ContentProvider
جزئیات نحوه مدیریت ارتباطات بین پردازشی (IPC) را پنهان می کنند، اما متدهای ContentProvider
که به این درخواست ها پاسخ می دهند - متدهای query()
, insert()
, delete()
, update()
و getType()
- از مجموعه ای از رشته ها در فرآیند ارائه دهنده محتوا فراخوانی می شوند، نه رشته رابط کاربر برای فرآیند. از آنجایی که این روش ها ممکن است از هر تعداد رشته به طور همزمان فراخوانی شوند، آنها نیز باید پیاده سازی شوند تا از نظر thread ایمن باشند.
ارتباطات بین فرآیندی
اندروید مکانیزمی را برای IPC با استفاده از RPC ارائه میکند، که در آن یک متد توسط یک اکتیویتی یا مؤلفههای دیگر برنامه فراخوانی میشود، اما در فرآیند دیگری از راه دور اجرا میشود و هر نتیجهای به تماسگیرنده بازگردانده میشود. این امر مستلزم تجزیه یک فراخوانی متد و دادههای آن به سطحی است که سیستم عامل میتواند آن را درک کند، آن را از فرآیند محلی و فضای آدرس به فرآیند و فضای آدرس راه دور منتقل میکند، و سپس مجدداً جمعآوری میشود و تماس را در آنجا برقرار میکند.
سپس مقادیر برگشتی در جهت مخالف منتقل می شوند. اندروید تمامی کدها را برای انجام این تراکنش های IPC ارائه می کند، بنابراین می توانید بر روی تعریف و پیاده سازی رابط برنامه نویسی RPC تمرکز کنید.
برای اجرای IPC، برنامه شما باید با استفاده از bindService()
به یک سرویس متصل شود. برای اطلاعات بیشتر، به نمای کلی خدمات مراجعه کنید.
هنگامی که یک جزء برنامه شروع می شود و برنامه هیچ مؤلفه دیگری در حال اجرا ندارد، سیستم Android یک فرآیند لینوکس جدید را برای برنامه با یک رشته اجرا شروع می کند. به طور پیش فرض، تمام اجزای یک برنامه کاربردی در یک فرآیند و رشته یکسان اجرا می شوند که به آن نخ اصلی می گویند.
اگر یک مؤلفه برنامه شروع شود و از قبل یک فرآیند برای آن برنامه وجود داشته باشد، زیرا مؤلفه دیگری از برنامه قبلاً شروع شده است، مؤلفه در آن فرآیند شروع می شود و از همان رشته اجرا استفاده می کند. با این حال، میتوانید ترتیبی دهید که مؤلفههای مختلف در برنامه خود در فرآیندهای جداگانه اجرا شوند و میتوانید رشتههای اضافی برای هر فرآیندی ایجاد کنید.
این سند نحوه عملکرد پردازش ها و رشته ها در یک برنامه اندروید را مورد بحث قرار می دهد.
فرآیندها
بهطور پیشفرض، همه اجزای برنامه در یک فرآیند اجرا میشوند و اکثر برنامهها این را تغییر نمیدهند. با این حال، اگر متوجه شدید که باید کنترل کنید که یک جزء خاص به کدام فرآیند تعلق دارد، می توانید این کار را در فایل مانیفست انجام دهید.
ورودی مانیفست برای هر نوع عنصر مؤلفه - <activity>
، <service>
، <receiver>
و <provider>
—از ویژگی android:process
پشتیبانی میکند که میتواند فرآیندی را مشخص کند که جزء اجرا میشود. میتوانید این ویژگی را طوری تنظیم کنید که هر کامپوننت در فرآیند خود اجرا می شود یا به طوری که برخی از اجزای یک فرآیند را به اشتراک می گذارند در حالی که برخی دیگر این کار را نمی کنند.
همچنین میتوانید android:process
طوری تنظیم کنید که اجزای برنامههای مختلف در یک فرآیند اجرا شوند، مشروط بر اینکه برنامهها شناسه کاربری لینوکس یکسانی داشته باشند و با گواهینامههای یکسانی امضا شده باشند.
عنصر <application>
همچنین از ویژگی android:process
پشتیبانی میکند که میتوانید از آن برای تنظیم یک مقدار پیشفرض که برای همه مؤلفهها اعمال میشود، استفاده کنید.
اندروید ممکن است زمانی تصمیم بگیرد که فرآیندی را خاموش کند، زمانی که سایر فرآیندها به منابع نیاز دارند که سریعتر در خدمت کاربر هستند. مؤلفه های برنامه در حال اجرا در فرآیندی که خاموش شده اند، در نتیجه از بین می روند. زمانی که کاری برای آن مؤلفهها وجود داشته باشد، یک فرآیند دوباره شروع میشود.
هنگامی که تصمیم می گیرید کدام فرآیندها را خاموش کنید، سیستم اندروید اهمیت نسبی آنها را برای کاربر می سنجد. به عنوان مثال، در مقایسه با فرآیندی که میزبان فعالیتهای قابل مشاهده است، با سهولت بیشتری فعالیتهای میزبانی فرآیند را که دیگر روی صفحه قابل مشاهده نیستند، خاموش میکند. بنابراین، تصمیم در مورد خاتمه دادن به یک فرآیند به وضعیت اجزای در حال اجرا در آن فرآیند بستگی دارد.
جزئیات چرخه عمر فرآیند و ارتباط آن با حالت های برنامه در فرآیندها و چرخه عمر برنامه مورد بحث قرار گرفته است.
موضوعات
هنگامی که یک برنامه راه اندازی می شود، سیستم یک رشته اجرایی برای برنامه ایجاد می کند که به آن رشته اصلی می گویند. این موضوع بسیار مهم است، زیرا وظیفه ارسال رویدادها به ویجت های رابط کاربری مناسب از جمله رویدادهای ترسیمی را بر عهده دارد. همچنین تقریباً همیشه رشتهای است که در آن برنامه شما با مؤلفههای بستههای android.widget
و android.view
جعبه ابزار Android UI تعامل دارد. به همین دلیل، رشته اصلی گاهی اوقات رشته UI نامیده می شود. با این حال، تحت شرایط خاص، رشته اصلی برنامه ممکن است رشته رابط کاربری آن نباشد. برای اطلاعات بیشتر، حاشیه نویسی موضوع را ببینید.
سیستم یک رشته مجزا برای هر نمونه از یک جزء ایجاد نمی کند. تمام اجزایی که در یک فرآیند اجرا می شوند در رشته UI نمونه سازی می شوند و فراخوانی های سیستم به هر جزء از آن رشته ارسال می شود. در نتیجه، روشهایی که به تماسهای سیستم پاسخ میدهند - مانند onKeyDown()
برای گزارش اقدامات کاربر، یا یک روش بازگشت به تماس چرخه حیات - همیشه در رشته UI فرآیند اجرا میشوند.
به عنوان مثال، وقتی کاربر دکمهای را روی صفحه لمس میکند، رشته رابط کاربری برنامه شما رویداد لمسی را به ویجت ارسال میکند، که به نوبه خود حالت فشرده آن را تنظیم میکند و یک درخواست نامعتبر را در صف رویداد ارسال میکند. رشته UI درخواست را از صف خارج می کند و به ویجت اطلاع می دهد تا خودش را دوباره ترسیم کند.
اگر برنامه خود را به درستی پیاده سازی نکنید، این مدل تک رشته ای زمانی که برنامه شما کار فشرده ای را در پاسخ به تعامل کاربر انجام می دهد، می تواند عملکرد ضعیفی داشته باشد. انجام عملیات طولانی در رشته UI، مانند دسترسی به شبکه یا جستجوهای پایگاه داده، کل UI را مسدود می کند. وقتی رشته مسدود می شود، هیچ رویدادی از جمله رویدادهای ترسیمی قابل ارسال نیست.
از منظر کاربر، به نظر می رسد که برنامه معلق است. حتی بدتر از آن، اگر رشته UI برای بیش از چند ثانیه مسدود شود، گفتگوی " برنامه پاسخ نمی دهد " (ANR) به کاربر نمایش داده می شود. ممکن است کاربر تصمیم بگیرد که برنامه شما را ترک کند یا حتی آن را حذف نصب کند.
به خاطر داشته باشید که جعبه ابزار Android UI امن نیست . بنابراین، رابط کاربری خود را از یک موضوع کارگر دستکاری نکنید. تمام دستکاری ها را در رابط کاربری خود از رشته UI انجام دهید. دو قانون برای مدل تک رشته ای اندروید وجود دارد:
- رشته رابط کاربری را مسدود نکنید.
- به جعبه ابزار Android UI از خارج از رشته UI دسترسی نداشته باشید.
نخ های کارگری
به دلیل این مدل تک رشته ای، برای پاسخگویی رابط کاربری برنامه شما ضروری است که رشته رابط کاربری را مسدود نکنید. اگر عملیاتی دارید که باید آنی انجام دهید، مطمئن شوید که آنها را در پسزمینه یا رشتههای کارگری جداگانه انجام دهید. فقط به یاد داشته باشید که نمیتوانید UI را از هیچ رشتهای غیر از UI یا رشته اصلی بهروزرسانی کنید.
برای کمک به پیروی از این قوانین، Android راههای مختلفی برای دسترسی به رشته رابط کاربری از موضوعات دیگر ارائه میکند. در اینجا لیستی از روش هایی وجود دارد که می تواند کمک کند:
مثال زیر از View.post(Runnable)
استفاده می کند:
کاتلین
fun onClick(v: View) { Thread(Runnable { // A potentially time consuming task. val bitmap = processBitMap("image.png") imageView.post { imageView.setImageBitmap(bitmap) } }).start() }
جاوا
public void onClick(View v) { new Thread(new Runnable() { public void run() { // A potentially time consuming task. final Bitmap bitmap = processBitMap("image.png"); imageView.post(new Runnable() { public void run() { imageView.setImageBitmap(bitmap); } }); } }).start(); }
این پیادهسازی از نظر thread ایمن است، زیرا عملیات پسزمینه از یک رشته جداگانه انجام میشود در حالی که ImageView
همیشه از رشته UI دستکاری میشود.
با این حال، با افزایش پیچیدگی عملیات، این نوع کد می تواند پیچیده و نگهداری آن دشوار باشد. برای مدیریت تعاملات پیچیده تر با یک رشته کاری، ممکن است از یک Handler
در رشته کاری خود برای پردازش پیام های ارسال شده از رشته UI استفاده کنید. برای توضیح کامل نحوه زمانبندی کار بر روی رشتههای پسزمینه و برقراری ارتباط مجدد با رشته رابط کاربری، به نمای کلی کار پسزمینه مراجعه کنید.
روش های ایمن با نخ
در برخی موقعیتها، متدهایی که پیادهسازی میکنید از بیش از یک رشته فراخوانی میشوند، و بنابراین باید به گونهای نوشته شوند که ایمن باشند.
این در درجه اول برای روش هایی که می توان از راه دور فراخوانی کرد، مانند روش هایی در یک سرویس محدود صادق است. هنگامی که یک فراخوانی روی یک متد پیادهسازی شده در یک IBinder
از همان فرآیندی شروع میشود که در آن IBinder
در حال اجرا است، متد در رشته تماسگیرنده اجرا میشود. با این حال، هنگامی که فراخوانی در فرآیند دیگری منشأ میگیرد، این متد در رشتهای انتخاب شده از مجموعه رشتههایی که سیستم در همان فرآیند IBinder
نگهداری میکند، اجرا میشود. در رشته UI فرآیند اجرا نمی شود.
به عنوان مثال، در حالی که متد onBind()
یک سرویس از رشته UI فرآیند سرویس فراخوانی می شود، متدهای پیاده سازی شده در شیئی که onBind()
برمی گرداند، مانند زیر کلاسی که متدهای فراخوانی روش راه دور (RPC) را پیاده سازی می کند، از رشته ها فراخوانی می شوند. در استخر از آنجایی که یک سرویس میتواند بیش از یک کلاینت داشته باشد، بیش از یک رشته Pool میتواند به طور همزمان یک روش IBinder
را درگیر کند، بنابراین روشهای IBinder
باید پیادهسازی شوند تا از نظر thread ایمن باشند.
به طور مشابه، ارائهدهنده محتوا میتواند درخواستهای دادهای را دریافت کند که از فرآیندهای دیگر سرچشمه میگیرند. کلاس های ContentResolver
و ContentProvider
جزئیات نحوه مدیریت ارتباطات بین پردازشی (IPC) را پنهان می کنند، اما متدهای ContentProvider
که به این درخواست ها پاسخ می دهند - متدهای query()
, insert()
, delete()
, update()
و getType()
- از مجموعه ای از رشته ها در فرآیند ارائه دهنده محتوا فراخوانی می شوند، نه رشته رابط کاربر برای فرآیند. از آنجایی که این روش ها ممکن است از هر تعداد رشته به طور همزمان فراخوانی شوند، آنها نیز باید پیاده سازی شوند تا از نظر thread ایمن باشند.
ارتباطات بین فرآیندی
اندروید مکانیزمی را برای IPC با استفاده از RPC ارائه میکند، که در آن یک متد توسط یک اکتیویتی یا مؤلفههای دیگر برنامه فراخوانی میشود، اما در فرآیند دیگری از راه دور اجرا میشود و هر نتیجهای به تماسگیرنده بازگردانده میشود. این امر مستلزم تجزیه یک فراخوانی متد و دادههای آن به سطحی است که سیستم عامل میتواند آن را درک کند، آن را از فرآیند محلی و فضای آدرس به فرآیند و فضای آدرس راه دور منتقل میکند، و سپس مجدداً جمعآوری میشود و تماس را در آنجا برقرار میکند.
سپس مقادیر برگشتی در جهت مخالف منتقل می شوند. اندروید تمامی کدها را برای انجام این تراکنش های IPC ارائه می کند، بنابراین می توانید بر روی تعریف و پیاده سازی رابط برنامه نویسی RPC تمرکز کنید.
برای اجرای IPC، برنامه شما باید با استفاده از bindService()
به یک سرویس متصل شود. برای اطلاعات بیشتر، به نمای کلی خدمات مراجعه کنید.