مجموعه‌ای از سوالات مصاحبه‌های اندروید (بخش اول)


آنچه که در ادامه می‌آید ترجمه ای از مقاله‌ی یک برنامه نویس اندروید به نام خانم Anitaa Murthy است. ایشان طی سال‌ها مجموعه‌ای از سوالات مصاحبه‌های اندروید را جمع آوری کردند که در ادامه با هم خواهیم خواند. بر خلاف اصل مقاله، تعدادی از سوال‌ها را اول آورده‌ام. دوست دارم این سوال‌ها را پیش خودتان پاسخ دهید (سعی کنید واقعا با جملات به آنها پاسخ دهید، نه اینکه در ذهنتان به آن فکر کنید) و به این وسیله دانش خود را به چالش بکشید.

  1. اپلیکیشن چیست؟
  2. کانتکست (Context) چیست؟
  3. می‌دانید ArmV7 چیست؟
  4. چرا bytecode نمی‌تواند در اندروید اجرا شود؟
  5. بیلد تایپ در گردل چیست؟ و برای چه چیزی استفاده می‌شود؟
  6. پروسه بیلد شدن در اندروید را توضیح دهید.
  7. سناریویی را توضیح دهید که در آن ()on Destroy بدون ()on Pause و on Stop در اکتیویتی فراخوانی شود.

چند تای آن‌ها را پاسخ دادید؟

خب اجازه بدهید به سراغ پاسخ‌ها و ادامه سوالات برویم:

1. اپلیکیشن چیست؟

کلاس اپلیکیشن، کلاسی پایه در برنامه اندروید است که شامل همه کامپوننت‌های دیگر مانند اکتیویتی‌ها و سرویس‌ها می‌شود. کلاس اپلیکیشن و یا هر زیر کلاسی از آن قبل از هر کلاس دیگری شروع می‌شوند.

2. کانتکست (Context) چیست؟

کانتکست روح زندگی برنامه اندرویدی است و بدون آن برنامه شما یک کد جاوای ساده است.

کانتکست مهم ترین چیز در توسعه اندروید است و از طریق آن می‌توان اطلاعاتی راجع به اپلیکیشن و اکتیویتی بدست آورد.

با کانتکست می‌توان به ریسورس‌ها، دیتابیس و پرفرنس‌ها و غیره دسترسی پیدا کرد.

کانتکست مثل یک نقطه دسترسی به محیطی هستش که برنامه شما در حال حاضر روی آن در حال اجراست.

کلاس اکتیویتی و کلاس اپلیکیشن هر دو از Context اکستند شده‌اند.

دو نوع کانتکست مهم UI Context و Non-UI Context در دنیای اندروید داریم. بسیار مهمه که تفاوت این دو رو بدونید و اینکه چه موقع از هرکدام استفاده کنید که دچار مشکلات حافظه نشید.

3. می دانید ARMv7 چیست؟

در اندروید سه نوع معماری CPU داریم. ARMv7 بهینه‌ترین آن‌هاست که در مصرف باتری بهینه‌سازی شده است. ARM64 نسخه تکامل یافته‌تری است که از پردازش 64 بیتی برای محاسبات قدرتمندتری استفاده می‌کند. پردازنده ARMx86 از دوتای دیگر قدرتمندتر است اما استفاده کمتری دارد چون اصلا مصرف باتری خوبی ندارد.

4. چرا bytecode نمی تواند در اندروید اجرا شود؟

زیرا اندروید بجای JVM (ماشین مجازی جاوا) تا اندروید ۴.۴ ازDVM (ماشین مجازی Dalvik) استفاده می کرد و از اندروید ۵ از ART (android runtime) استفاده میکند. برای آشنایی بیشتر با دالویک و ART می‌توانید این مقاله را مطالعه بفرمایید.

5. در گردلBuildType چیست؟

بیلد تایپ پراپرتی هایی را تعریف می کند که Gradle هنگام بیلد کردن و پکیج‌بندی کردن برنامه اندروید شما از آن استفاده می‌کند .

1. بیلد تایپ تعریف می‌کند که چگونه یک ماژول بیلد شود، برای مثال ProGuard اجرا شود یا خیر.

2. و Flavour محصول تعریف می کند که چه چیزی بیلد شود، مثلا اینکه چه ریسورس‌های در بیلد وجود داشته باشد.

3. گردل یک خصوصیت بیلد برای هر ترکیب ممکن از بیلد تایپ‌ها و Flavourهای محصول ایجاد می‌کند.

6. فرآیند بیلد در اندروید را توضیح دهید؟

1. اولین قدم پوشه ریسورس‌ها (/res) را با استفاده از ابزار aapt (android asset packaging tool) کامپایل می‌کند. همه‌ی این ریسورس ها به یک دونه فایل کلاس R.java کامپایل می‌شوند. این کلاس فقط شامل یک سری ثابت است.

2. قدم دوم همه‌ی سورس کدهای جاوا توسط javac به فایل‌های .class کامپایل می‌شوند. سپس این کلاس‌ها توسط ابزار "dx" که در SDK tools قراردارد، به بایت کد Dalvik تبدیل می‌گردند، و خروجی آن کلاس .dex است.

3. مرحله‌ی آخر ساخت apk است که همه این ورودی‌ها تبدیل به یک فایل apk(android packaging key) می‌شود.

7. معماری یک اپلیکیشن اندروید چیست؟

معماری اپلیکیشن اندروید شامل کامپوننت‌های زیر است:

1. سرویس‌ها: برای انجام عملیات بکگراند استفاده می‌شود.

2. اینتنت: کانکشن داخلی بین اکتیویتی‌ها برقرار می‌کند و همچنین برای ارسال داده بکار می‌رود.

3. ریسورس‎ها: مانند استرینگ‌ها و تصاویر

4. ابزارهای آگاه‌سازی: شامل نور، صدا، آیکون، نوتیفیکیشن، دیالوگ و تست

5. کانتنت پروایدر: برای اشتراک گذاری داده بین اپلیکیشن ها کاربرد دارد.

8. اکتیویتی در اندروید را توصیف کنید؟

اکیتیویتی ها اساسا کانتینر یا پنجره ای برای رابط کاربری (UI) هستند.

9. چرخه حیات یک اکتیویتی

شامل متدهای زیر است:

  • متد onCreate : این متد مربوط به زمانی ست که view برای اولین بار ایجاد می‌شود. در این متد ما view ها را ایجاد می‌کنیم و داده‌ها را از باندل می‌خوانیم.
  • متد on Start : وقتی اکتیوتی در معرض دید کاربر قرار می‌گیرد فراخوانی می‌شود. حالا اگر اکتیویتی روی صفحه گوشی کاربر قابل مشاهده شود یعنی در فورگراند باشد بعد از آن on Resume فراخوانی میشه اما اگر اکتیویتی hide شود آنگاه متد on Stop فراخوانی خواهد‌ شد.
  • متد on Resume: وقتی اکتیوتی قابل تعامل با کاربر باشد، فراخوانی می‌شود. در این مرحله اکتیویتی بالای استک اکتیویتی قرار می‌گیرد.
  • متد on Pause: وقتی اکتیویتی به بکگراند برود فراخوانی می‌شود اما توجه کنید که اکتیویتی هنوز زنده است.
  • متد on Stop: وقتی اکتیویتی دیگر برای کاربر قابل مشاهده نیست، فراخوانی می‌شود.
  • متد on Destroy: وقتی اکتیویتی در حال به پایان رسیدن و مرگ است، فراخوانی می‌شود.
  • متد on Restart: بعد از اینکه اکتیوتی stop شد، درست قبل از شروع مجدد، این متد فراخوانی می‌گردد.

10. تفاوت متد onCreate و on start چیست؟

  • متد onCreate در طول چرخه حیات اکتیویتی فقط یکبار فراخوانی می‌شود. هنگام شروع برنامه یا زمانیکه اکیتیوتی destroy شده و از بین رفته و حالا دوباره ساخته شده است مثلا در زمان تغییر configuration.
  • متد on Start: هر وقتی که اکتیویتی توسط کاربر قابل دیدن شود، فراخوانی می‌شود. معمولا بعد از onCreate و on Restart.

11. یک سناریو بگویید که در آن بدون فراخوانی on Pauseو on Stop فقط onDestroy اکتیویتی کال شود؟

اگر متد finish در تابع onCreate اکتیویتی فراخوانی شود سیستم متد onDestroy را مستقیماً فراخوانی می‌کند.




12. چرا شما setContentView را در متد onCreate اکتیویتی انجام می‌دهید؟

از آنجایی که متد onCreate اکتیویتی فقط یکبار فراخوانی می‌شود، باید بیشتر مقداردهی های اولیه نیز همین جا انجام شود. اصلا کار بهینه ای نیست که setContentView را در توابع on Resume یا on Start انجام دهیم، چون این توابع چندین بار فراخوانی می‌شوند و setContentView عمل سنگینی است.

13. متدهای onRestoreInstanceState و onSaveInstanceState در اکتیویتی چه کاری انجام می‌دهند؟

  • متد onRestoreInstanceState: وقتی اکتیویتی نابود شده مجددا ساخته می شود، می‌توانیم وضعیت ذخیره شده را از باندلی که به این متد پاس داده شده است، بازیابی نماییم. هر دو متد onCreate و onRestoreInstanceState باندل مشابهی دریافت می‌کنند، اما چون متد onCreate هر وقت که سیستم نمونه‌ی جدیدی از اکتیویتی شما می‌سازد و یا وقتی که نمونه‌ی قبلی را بازسازی می‌کند فراخوانی می‌شود باید قبلا از خواندن داده ها از باندل، نال نبودن آن‌ها را چک کنید. اگر باندل نال بود مشخص می‌شود که سیستم نمونه‌ی جدید از اکتیویتی ساخته است و این فراخوانی بخاطر بازسازی نمونه قبلی که destroy شده، نبوده است.
  • متد onSaveInstanceState : این متد برای ذخیره‌ی داده‌ها قبل از pause شدن اکتیویتی استفاده می‌شود.

14. از Launch مود در اندروید چی می‌دانید؟

وقتی یک اکتیویتی قرار است اجرا شود، سیستم از روی لانچ مود می‌فهمد که باید نمونه‌ی جدید بسازد یا از نمونه قبلی استفاده کند و یا حتی برای نمونه‌های دیگر موجود در Stack اکتیویتی چه اتفاقی خواهد افتاد.

بیاییم با استفاده از تصاویر برگرفته از این مقاله ببینیم لانچ مودها چه تفاوت‌هایی باهم دارند. و در ابتدا یک تصویر راهنما ببینیم:

رنگ سبز یعنی نمونه‌ای از اکتیوتی به تازگی ایجاد شده است و رنگ زرد به این معنی ست که نمونه اکتیویتی قبلا وجود داشته و فقط متد onNewIntent فراخوانی شده است.
رنگ سبز یعنی نمونه‌ای از اکتیوتی به تازگی ایجاد شده است و رنگ زرد به این معنی ست که نمونه اکتیویتی قبلا وجود داشته و فقط متد onNewIntent فراخوانی شده است.


  • مود Standard: مود استاندارد، نمونه ای از اکتیویتی در Taskای که از آن شروع شده است، ایجاد می‌کند. می‌تواند چند نمونه از اکتیوتی ایجاد شود و به تسک‌های مشابه یا متفاوت افزوده شود.

مثال : فرض کنید که استک اکتیویتی به این صورت باشد: A->B->C->D حالا ما اکتیویتی B را با لانچ مود استاندارد، لانچ می کنیم.

اکتیویتی B با لانچ مود استاندارد در مانیفست تعریف شده است. بعد از اجرای آن با اینکه یک نمونه از آن قبلا در استک وجود داشته است، یک نمونه‌ی جدید ساخته شده است.
اکتیویتی B با لانچ مود استاندارد در مانیفست تعریف شده است. بعد از اجرای آن با اینکه یک نمونه از آن قبلا در استک وجود داشته است، یک نمونه‌ی جدید ساخته شده است.



  • مود SingleTop: مود SingleTop مشابه مود Standard است بجز اینکه اگر یک نمونه از اکتیویتی بالای استک وجود داشته باشد، آنگاه نمونه ای جدیدی از آن اکتیویتی ساخته نخواهد شد و intent به نمونه اکتیویتی که بالای استک است ارسال خواهد شد، در غیر اینصورت نمونه‌ی جدیدی از اکتیویتی در بالای استک قرار خواهد گرفت.

مثال 1: فرض کنید استکی به این صورت داریم: A->B->C->D

اکتیویتی D با لانچ مود SingleTop در مانیفست تعریف شده است. چون قبلا یک نمونه از اکتیویتی D در بالای استک وجود داشته است، نمونه‌ی جدیدی ایجاد نمیشود و متد onNewIntent در اکتیویتی D فراخوانی می‌شود.
اکتیویتی D با لانچ مود SingleTop در مانیفست تعریف شده است. چون قبلا یک نمونه از اکتیویتی D در بالای استک وجود داشته است، نمونه‌ی جدیدی ایجاد نمیشود و متد onNewIntent در اکتیویتی D فراخوانی می‌شود.


مثال 2: استک اکتیویتی به این صورت است: A->B->C

 اکتیویتی D با لانچ مود SingleTop در مانیفست تعریف شده است. نمونه‌ی جدیدی از اکتیویتی D در استک قرار می‌گیرد.
اکتیویتی D با لانچ مود SingleTop در مانیفست تعریف شده است. نمونه‌ی جدیدی از اکتیویتی D در استک قرار می‌گیرد.

مثال 3: استک اکتیویتی به این صورت است: A->B->D->C

 اکتیویتی D با لانچ مود SingleTop در مانیفست تعریف شده است. یک نمونه از اکتیویتی D قبلا در استک وجود داشته اما چون بالای استک نبوده است یک نمونه جدید از اکتیویتی D ایجاد می‌شود.
اکتیویتی D با لانچ مود SingleTop در مانیفست تعریف شده است. یک نمونه از اکتیویتی D قبلا در استک وجود داشته اما چون بالای استک نبوده است یک نمونه جدید از اکتیویتی D ایجاد می‌شود.


  • مود SingleTask: مود تک وظیفه‌ای یا SingleTask به این صورت است که فقط یک نمونه از اکتیویتی می‌تواند وجود داشته باشد مشابه الگوی سینگلتون.

سه حالت خواهیم داشت. حالت اول) نمونه‌ای از این اکتیویتی در استک نداشته باشیم، که نمونه ی جدید بالای استک ایجاد خواهد شد. حالت دوم) نمونه از این اکتیویتی از قبل بالای استک وجود دارد، نمونه‌ی جدید ساخته نمی‌شود و فقط Intent به تابع onNewIntent تحویل داده خواهد شد. حالت سوم) نمونه‌ای از این اکتیویتی وجود داشته باشد اما بالای استک نباشد. در این حالت همه نمونه های بالای این نمونه نابود خواهند شد تا این نمونه بالای استک باشد و Intent به تابع onNewIntent تحویل داده خواهد شد.

مثال 1: فرض کنید یک استک داریم به این صورت: A->B->C->D

اکتیویتی C با لانچ مود SingleTask در مانیفست تعریف شده است. همان گونه که انتظار داشتیم اکتیویتی D نابود شده است و متد onNewIntent در نمونه قدیمی اکتیویتی C فراخوانی شده است.(حالت سوم)
اکتیویتی C با لانچ مود SingleTask در مانیفست تعریف شده است. همان گونه که انتظار داشتیم اکتیویتی D نابود شده است و متد onNewIntent در نمونه قدیمی اکتیویتی C فراخوانی شده است.(حالت سوم)


مثال 2: استک اکتیویتی به این صورت است: A->B

چون قبلا نمونه‌ای از اکتیویتی C در استک نداشتیم، یک نمونه‌ی جدید از آن ساخته می‌شود.
چون قبلا نمونه‌ای از اکتیویتی C در استک نداشتیم، یک نمونه‌ی جدید از آن ساخته می‌شود.


  • مود SingleInstance: مود تک نمونه‌ای یا SingleInstance مشابه تک وظیفه‌ای است اما سیستم، اکتیویتی با لانچ مود SingleInstance را در Task موجودی که اکتیویتی دیگری در آن وجود دارد، قرار نمی‌دهد. چنین اکتیویتی همیشه در یک Task جداگانه اجرا خواهد شد.

مثال: فرض کنید یک استک اکتیویتی به این صورت داریم: A->B->C->D

اگر اکتیویتی E را با لانچ مود SingleInstance اجرا کنید استک اکتیویتی جدید به این صورت خواهد بود:

Task 1: A->B->C->D

Task 2: E

اکتیویتی E با لانچ مود SingleInstance در مانیفست تعریف شده است. همان طور که می‌بینیم نمونه‌ی اکتیویتی E در task دیگری ایجاد شده است.
اکتیویتی E با لانچ مود SingleInstance در مانیفست تعریف شده است. همان طور که می‌بینیم نمونه‌ی اکتیویتی E در task دیگری ایجاد شده است.

مثال 2: اکتیویتی F بعد از اکتیویتی E اجرا خواهد شد. استک اکتیویتی به صورت زیر خواهد بود.

همان طور که می‌بینیم نمونه‌ی اکتیویتی F وارد تسک دیگر شد و نمونه‌ی اکتیویتی E هنوز در تسک خود تنهاست.
همان طور که می‌بینیم نمونه‌ی اکتیویتی F وارد تسک دیگر شد و نمونه‌ی اکتیویتی E هنوز در تسک خود تنهاست.

مثال3:استک اولیه به این صورت است که دو Task داریم. Task اول شامل A->B و Task دوم شامل E

حالا دوباره اکتیویتی E اجرا می‌شود. چه اتفاقی خواهد افتاد؟

متد onNewIntent در نمونه‌ی اکتیویتی E فراخوانی شده است و نمونه‌ی جدیدی ایجاد نشده است.
متد onNewIntent در نمونه‌ی اکتیویتی E فراخوانی شده است و نمونه‌ی جدیدی ایجاد نشده است.


برای مشاهده مثال‌های تصویری و جا افتادن بهتر موضوع می توانید این مقاله و این مقاله را مطالعه نمایید.

15. وقتی کاربر اسکرین را rotate می‌کند، اکتیویتی چگونه پاسخ می‌دهد؟

وقتی اسکرین rotate می‌شود، نمونه فعلی اکتیویتی نابود می‌شود و نمونه‌ی جدیدی از اکتیویتی در rotation جدید ایجاد خواهد شد. و متد onRestart فراخوانی می‌شود. بقیه متدهای لایف سایکل اکتیویتی مشابه زمانی ‌که اکتیویتی جدید ساخته می‌شود، اجرا خواهند شد.

16. در زمان rotate صفحه چگونه از reloading و resetting داده جلوگیری کنیم؟

اساسی‌ترین رویکرد استفاده از ترکیب ویو مدل ها و تابع onSaveInstanceState خواهد بود. چگونه؟

  • می دانیم ویومدل‌ها LifeCycle-Aware (از لایف سایکل آگاهی دارند) هستند. یعنی اگر اکتیویتیِ صاحب ویومدل بخاطر تغییر تنظیمات نابود شود، ویو مدل نابود نخواهد شد و فقط به نمونه‌ی جدید اکتیویتی دوباره متصل می‌شود. به عبارت دیگر اگر اسکرین سه بار rotate شود و اکتیویتیِ صاحب ویومدل سه بار نابود شود، سه نمونه‌ی جدید از اکتیویتی ایجاد خواهد شد اما فقط یک نمونه از ویومدل ایجاد شده است.
  • بنابراین بهترین رویکرد این است که داده‌ها را در کلاس ویومدل ذخیره کنیم و می‌توانیم از متد onSaveInstanceState اکتیویتی برای حفظ داده‌های کوچک Uiای استفاده کنیم.
  • برای مثال، فرض کنید یک صفحه جستجو داریم و کاربر یک عبارت را برای جستجو در ادیت تکست وارد کرده است. نتایج جستجو در یک ریسایکلر ویو نمایش داده می‌شود. کاربر صفحه را rotate می‌کند. برای حفظ داده‌های لیست و عبارت درون ادیت تکست چه خواهید کرد؟

بهترین روش برای جلوگیری از ریست شدنِ لیست، ذخیره‌ی داده‌های لیست در ویو مدل و عبارت جستجو شده در تابع onSaveInstanceState می‌باشد.

17. وقتی یک اکتیویتی جدید از طریق Intent ایجاد می‌شود، دو راه برای پاک کردن بک استک اکتیویتی پیشنهاد کنید.

راه اول) استفاده از فلگ FLAG_ACTIVITY_CLEAR_TOP

راه دوم) استفاده از ترکیب فلگ‌های FLAG_ACTIVITY_CLEAR_TASK و FLAG_ACTIVITY_NEW_TASK

18. چه تفاوتی بین FLAG_ACTIVITY_CLEAR_TASK و FLAG_ACTIVITY_CLEAR_TOP وجود دارد؟

  • فلگ FLAG_ACTIVITY_CLEAR_TASK: همان طور که از نامش پیداست تمام نمونه‌های درون تسک را پاک خواهد کرد حتی اگر از اکتیویتی فعلی نمونه‌هایی وجود داشته باشد، آن‌ها هم پاک خواهد شد. خلاصه همه چی پاک می‌شود و نمونه‌ی جدید، روت تسک خواهد بود و به همین دلیل حتما باید این فلگ با ترکیب فلگ FLAG_ACTIVITY_NEW_TASK استفاده شود.
  • فلگ FLAG_ACTIVITY_CLEAR_TOP : هنگام ست شدن این فلگ برای اجرای اکتیویتی دو حالت خواهیم داشت. حالت 1) نمونه‌ی قدیمی از این اکتیویتی در لیست تسک وجود دارد، بنابراین تمام نمونه های روی آن حذف خواهد شد و نمونه قدیمی root لیست خواهد شد. حالت 2) اگر نمونه‌ی قدیمی وجود نداشته باشد، یک نمونه‌ی جدید ایجاد می شود و root لیست خواهد شد. استفاده از ترکیب این فلگ با FLAG_ACTIVITY_NEW_TASK ضروری نیست اما یک good practice محسوب می‌گردد.

19. کانتنت پروایدرها را شرح دهید.

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

برای دسترسی به داده‌های درون کانتنت پروایدر باید از یک شی ContentResolver درون کانتکست اپلیکیشن خودتان استفاده کنید تا با ارائه دهنده داده ارتباط برقرار نمایید.

20. دسترسی به داده از طریق کانتنت پروایدر چگونه است؟

  • اول مطمئن شوید که مجوز لازم برای خواندن داده را دارید.
  • سپس متد getContentResolver در شی کانتکست را برای دسترسی به شی ContentResolver فراخوانی نمایید.
  • با ContentResolver.query یک کوئری برای دسترسی به داده‌ها ایجاد کنید.
  • این متد یک cursor را برمی‌گرداند و می‌توانید داده‌ها را از این کرسر بخوانید.




در این جا بخش اول سوالات مصاحبه‌های اندرویدی به پایان رسید. انشالله در بخش‌های بعدی این مقاله ادامه سوالات را باهم خواهیم دید و پاسخ خواهیم داد.

در بخش دوم سوالاتی در خصوص سرویس و ناهمزمانی در اندروید مطرح خواهد شد.