<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های شکیب حبیبی</title>
        <link>https://virgool.io/feed/@shkbhbb</link>
        <description>Senior Android Developer | Kotlin, Java, Flutter | Coroutines, Compose, MVVM, Clean Architecture | Passionate About Creating Innovative Mobile Solutions</description>
        <language>fa</language>
        <pubDate>2026-07-01 07:50:27</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/71211/avatar/CBl4hV.jpg?height=120&amp;width=120</url>
            <title>شکیب حبیبی</title>
            <link>https://virgool.io/@shkbhbb</link>
        </image>

                    <item>
                <title>نکاتی برای شروع دویدن</title>
                <link>https://virgool.io/@shkbhbb/%D9%86%DA%A9%D8%A7%D8%AA%DB%8C-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%B4%D8%B1%D9%88%D8%B9-%D8%AF%D9%88%DB%8C%D8%AF%D9%86-schbahgdbobx</link>
                <description>من حدود ۴ ماه پیش دویدن رو شروع کردم و بعد از ۱۳ هفته به هدف اولم که دویدن ۱۰ کیلومتر بود رسیدم و حتی از اون فراتر رفتم. تو این نوشته می خوام نکاتی رو مطرح کنم که به نظرم در شروع کار می تونه کمکتون کنه. من در بخش هایی تجربیات خودم رو مطرح می کنم که ممکنه خیلی قابل تعمیم نباشه ولی دونستنش خالی از لطف نیست.کتاببرای شروع، من خوندن کتاب &quot;راهنمای مقدماتی دویدن&quot; رو خیلی پیشنهاد می کنم. بسیار کتاب مفیدی هست. درباره موضوعاتی مثل جنبه های روانی دویدن، مصدومیت ها، تغذیه، تکنیک دویدن و ... مطالب بسیار مفیدی داره. هر چقدر بیشتر بدونید بهتره.در هفته های اولیه دویدن، ناغافل یکی از مصدومیت های قدیمیم برگشت. من عمیقا ناراحت بودم چون می دونستم این مصدومیت زمین گیرم می کنه. اما به کمک این کتاب تونستم در وهله اول دردش رو مدیریت کنم و در وهله دوم با تغییر تکنیک دویدن کلا از شرش خلاص بشم. اگر راهنمایی های کتاب نبود من قطعا نمی تونستم برنامم رو ادامه بدم. این کتاب یه برنامه ۱۳ هفته ای برای دویدن هم ارائه میده که در بخش بعد بیشتر بهش می پردازم.برنامهمن قبل از شروعِ این برنامه ی ۱۳ هفته ای هم می دویدم. البته اون موقع هدفم متفاوت بود. قبلا من برای دویدن برنامه ای نداشتم و امیدوار بودم در گذر زمان توانایی بدنم به مرور بیشتر بشه. ولی اینطور نشد. بعد از چند هفته دویدن لیترالی هیچ تغییر محسوسی در تواناییم احساس نمی کردم. تا مدت های زیاد روی یه مسافت کوتاه موندم و نتیجه ای نداشت. کتابی که بالاتر معرفی کردم یه برنامه ۱۳ هفته ای (۳ جلسه تمرینی در هفته) داره که برای همه مناسبه. برنامه خیلی سبک شروع میشه و کم کم سنگین تر میشه. من خیلی خیلی زیاد استفاده از این برنامه رو پیشنهاد می کنم(برای خودم از همین برنامه بردم :)). به نظرم نقطه شروع بسیار خوبیه. البته خودتون هم می تونید سرچ کنید و برای خودتون برنامه بچینید ولی من برای شروع انتخابم این نبود و توصیه هم نمی کنم. بعدا که بیشتر به بدن و مرزهاش آگاه شدید شاید بتونید این کار رو انجام بدید.از برنامه جلو نزنیدحتی اگر فکر می کنید توانتون بیشتر از برنامه هست از برنامه جلو نزنید. در کتاب اشاره شده که در دویدن دو سیستم قلبی-عروقی و اسکلتی-ماهیچه ای درگیر هستند. سیستم قلبی-عروقی بسیار قوی هست و توانایی تطبیق پذیریِ بالایی داره. برای همین شما بعد از مدتی دویدن احساس می کنید نفستون می کشه که بیشتر بدوید غافل از اینکه سیستم اسکلتی-ماهیچه ای هنوز خودش رو منطبق نکرده. نتیجه؟ مصدومیت. پس حتما به برنامتون پایبند باشید حتی اگر اوایل براتون ساده هست.اگر خودتون برنامتون رو تنظیم کردید به قانون ۱۰ درصد توجه کنید (من چند جا دیدم ولی منابعش به خاطرم نیست). این قانون میگه که هر هفته نباید بیش از ۱۰ درصد از هفته قبل جلو برید. حالا این ۱۰ درصد می تونه مسافت باشه یا زمان. خیلی مراقب باشید که هیجان اولیه باعث مصدومیتتون نشه. توانایی دویدن مسافت های طولانی یک روزه به دست نمیاد. بهش به عنوان هدف چند ماهه نگاه کنید.اهداف واقع بینانههدف های واقع بینانه برای خودتون ترسیم کنید. اینکه مثلا من بگم ۳ ماه دیگه می خوام برم نیم ماراتون بدوم قطعا معقول نیست. هدف نادقیق اولا سبب میشه که شما بیش از حد به خودتون فشار بیارید، که می تونه سبب مصدومیتتون بشه، و از طرفی بعد از یک مدت که می بینید هدف در دسترستون نیست دلسرد میشید. به بدنتون گوش کنیددر موقع دویدن خیلی به بدنتون توجه کنید. مدام از خودتون سوال بپرسید که چطور دارید نفس می کشید؟ ماهیچه ها چقدر تحت فشار هستند؟ سرعت رو باید زیاد کنید، کم کنید یا نگه دارید؟ چقدر انرژی دارید؟ بعضی روزها بدنتون بهتون پیام میده که توانش محدودتره. ممکنه این موضوع به خاطر خواب، تغذیه یا مشغله فکری باشه. به بدنتون گوش کنید و سعی کنید مرزهاش رو پیدا کنید. طبیعتا در این مسیر قرار هست بهش فشار بیاد ولی نه فشار اضافه بر توانش چون اون سبب آسیب دیدگی میشه. پرسشنامهقبل از شروع تمرین خوبه که به ۷ سوال مطرح شده در پرسشنامه PAR-Q جواب بدید. سوالات رو می تونید اینجا پیدا کنید. اگر به یک سوال یا بیشتر جواب &quot;بله&quot; دادید پیش از شروع تمرین حتما با دکتر مشورت کنید. این پرسشنامه برای افراد بین ۱۵ تا ۶۹ سال هست.آب بخوریدحتما در حین تمرینات آب بخورید. هر ۲۰ تا ۲۵ دقیقه برای من زمان مناسبی هست. قمقمه های سنگین یا بزرگ که راحت تو دست جا نمیشه رو با خودتون نبرید چون دست و پا گیرن. می تونید از قمقمه های مخصوص دویدن  استفاده کنید ولی خوب قیمتشون زیاده و من پیشنهاد نمی کنم.قمقمه مخصوص دویدنمن دنبال یک بطری بودم که راحت توی دستم جا بشه و همینطور جنسش مثل بطری های آب معدنی نباشه که راحت با فشار دادن بره تو و فرمش رو از دست بده. در نهایت به بطری های ۲۰۰ میلی لیتری شیرکاکائو عالیس رسیدم. یکیش رو خریدم. از نوشیدن شیرکاکائوش نهایت لذت رو بردم :) بعد جلد روش رو کندم و شستمش. هر از چند مدت هم خوبه بطری رو عوض کنید. شاید خیلی مناسب چند بار استفاده نباشه. ولی در کل هم حجم آبی که نگه میداره برای من کافیه، هم خیلی راحت تو دستم جا میشه و هم جنسش طوری نیست که با هر فشاری فرمش رو از دست بده.گرم کردن/سرد کردناهمیت گرم/سرد کردن رو همیشه تو زنگ ورزش مدرسه بهمون می گفتن و هیچ موقع هم هیچ کس گوش نمیداد. ولی واقعا مهمه. یه نکته ای که من نمی دونستم و زمان اشتباهی انجامش میدادم حرکت کششی بود. من تو همون حرکت های اولیه گرم کردن می رفتم سراغ کششی که انتخاب درستی نیست. حرکات کششی بهتره در پایان تمرین، زمانی که بدن گرمه، به عنوان سرد کردن انجام بشن. اپلیکیشنبرای دویدن من دو تا اپلیکیشن رو پیشنهاد می کنم.اپ اول strava هست. یک شبکه اجتماعی برای ورزشکاران. راستش من از این امکان شبکه اجتماعیش اصلا استفاده نکردم ولی جالب به نظر میرسه. مثل اینکه چالش یا برنامه های مختلف هم به کمکش انجام میشه. ولی خارج از اون یک بخش record داره که وقتی gps رو روشن کنید تمرینتون رو دنبال می کنه و در پایان اطلاعات جالبی بهتون میده. اطلاعاتی مثل مسافتی که دویدید، سرعت میانگین، مدت زمان تمرین، آنالیز سرعت و ... در نهایت هم اگر دوست داشتید می تونید تمرینتون رو با عکس های خوشکل به اشتراک بذارید.اپ دوم Interval Timer - X هست. تو خیلی از برنامه ها همیشه interval وجود داره. یعنی شما مثلا باید ۳ دقیقه بدوید و ۲ دقیقه استراحت کنید و این کار رو ۱۰ بار تکرار کنید. گاهی برنامه منظم نیست. مثلا اول باید ۱۵ دقیقه بدوید، بعدش ۳ دقیقه استراحت کنید و دوباره ۱۰ دقیقه بدوید. اینکه در طی دویدن مدام گوشی دستتون باشه و ساعت رو چک کنید آزار دهنده هست. برای این منظور من چند تا برنامه مختلف رو تست کردم که هر کدوم مشکل یا محدودیتی داشتن ولی این برنامه به نظرم عالیه. راحت برنامتون رو ست می کنید و هدفون رو میذارید تو گوشتون. خودش دیگه تغییرات رو خبر میده.لباس و کفشدویدن امکانات خیلی زیادی نمی خواد ولی اینطور هم نیست که هر چیزی دم دستتون بود بپوشید و برید به امان خدا. باید پوشش مناسب داشته باشید. به نظر من داشتن لباس مناسب از همون روز اول مهمه. لباس شما نباید نخی باشه. لباس باید جنسی داشته باشه که بدن رو خشک نگه داره. لباس های نخی به سرعت خیس میشن و کل آب رو به بدن منتقل می کنن. الیاف مصنوعی مثل پلی استر برای این موضوع مناسب هستند. در فصل سرد سال حتما خوب خودتون رو گرم نگه دارید ولی نه بیش از حد مثلا ۱۰ لایه لباس نپوشید :) اینطور آب بدنتون سریع تر از دست میره. پس حتما یه تیشرت مناسب برای تمرینتون تهیه کنید.کفش مهمه. خیلی هم مهمه ولی نه در اول راه. کفشِ دویدن با بقیه کفش ها فرق می کنه. زمان دویدن حدود ۳ برابر فشار بیشتری به پاها وارد میشه. یه کفش خوب باید بتونه تا حد زیادی این فشارها رو کم کنه. یا اینکه کفش های خوب دویدن پا رو فیکس تر نگه می دارن که از چرخیدن پا و آسیب دیدگی تا جای ممکن جلوگیری بشه. ولی برای شروع، هر کفشی دارید استفاده کنید(طبیعتا منظورم کفش پاشنه بلند نیست:)). جلوتر که رفتید اگر دیدید به دویدن علاقه دارید می تونید برید کفش مناسب تر تهیه کنید.من در طی ۱۳ هفته برنامم از کفش قدیمیم که مخصوص راه رفتن بود استفاده کردم. کفش های مخصوص راه رفتن دقیقا بر خلاف کفش های دو، کفی باریکی دارن. حقیقت اینکه خیلی هم اذیت نشدم. تا همین اواخر که ۱۲ کیلومتر رو دویدم و انقدر به پام فشار اومد تا ۳ روز فقط خوابیده بودم :) الان تازه می خوام کفش جدید بگیرم. برای خرید کفش عجله نکنید چون کفشِ دو خوب خیلی ارزون نیست. استراحتمن قبلا فکر می کردم وقتی بین تمرین هام یک روز استراحت می کنم ضعیفم یا دارم تقلب می کنم :) واقعا اینطور نیست. استراحت احتمالا به اندازه تمرین مهمه. چون فرصتی هست که بدن خودش رو بازیابی می کنه. اگر این فرصت رو به بدن ندید با مصدومیت جوابتون رو میده. تغذیهخیلی مهمه که وقتی تمرین می کنید انرژی کافی داشته باشید و گرنه تمرینتون با کیفیت نخواهد بود. خیلی ها قبل از تمرین، با یه فاصله ای حدود ۲۰ تا ۳۰ دقیقه، خوراکی هایی مثل موز، کره بادوم زمینی و قهوه می خورن. من به این جمع سیب رو هم اضافه می کنم. اینکه بقیه این ها رو می خورن به این معنی نیست که شما هم باید همین ها رو بخورید :) تجربه کنید و ببینید چه چیزی بیشتر از همه برای شما جواب میده. من حتی شنیدم افرادی ترجیح میدن با معده خالی تمرین کنن و کیفیت بیشتری براشون داره. تجربه کنید و سبک خودتون رو پیدا کنید. توجه کنید که با معده پر به سراغ دویدن نرید چون اذیت میشید. خوبه که تمرینتون ۲ تا ۳ ساعت از وعده اصلی، مثل ناهار، فاصله داشته باشه.ایمنیاول ایمنی بعد تمرین :) اگر شب می دوید سعی کنید جاهای خیلی خلوت نرید. اگر در کوچه یا خیابون می دوید سعی کنید خلاف جهت ماشین ها حرکت کنید که روشون اشراف کامل داشته باشید. خوبه که همیشه یه برگه تو جیبتون باشه که یه شماره تماس ضروری روش نوشته باشید برای زمانی که خدایی نکرده به هر دلیلی حالتون بد شد و از هوش رفتید.رانیوبرای خیلی ها یکی از سخت ترین کارها حفظ کردن انگیزشون در حین ورزشه. برای همین خوبه به دنبال چیزهایی باشید که بتونن مداوم انگیزه رو بهتون برگردونن. من جدیدا با پادکستی به نام runyou آشنا شدم که یک زوج دونده تولیدش می کنن و چقدر زیبا و انرژی بخشه. من در هنگام شنیدن بعضی قسمت هاش، مخصوصا اون هایی که مهمان دارن، از گفتگو ها ذوق مرگ میشدم :) پادکست رو می تونید اینجا پیدا کنید.شروع کنیددر نهایت اینکه شروع کنید. تا شروع نکنید هیچ اتفاقی نمی افته. برای خودتون مانع تراشی نکنید. به ساده ترین شکل ممکن شروع کنید.نیازی نیست از روز اول برید تو فلان پارک بدوید. برید تو کوچه خیابون بدوید که دم دستتونه. نیاز نیست بهترین کفش یا لباس رو داشته باشید. هر چیزی دارید بپوشید و برید بدوید.نیاز نیست هر روز ساعت ۶ صبح پاشید. هر موقع از روز که وقت داشتید برید بدوید.برای خودتون مانع نتراشید. فقط شروع کنید. از اون به بعد، جادوی دویدن شما رو به پیش می بره.</description>
                <category>شکیب حبیبی</category>
                <author>شکیب حبیبی</author>
                <pubDate>Tue, 09 Nov 2021 18:26:02 +0330</pubDate>
            </item>
                    <item>
                <title>چگونه Performance اپلیکیشن اندرویدی را افزایش دهیم؟ / بخش دوم: Memory</title>
                <link>https://virgool.io/@shkbhbb/%DA%86%DA%AF%D9%88%D9%86%D9%87-performance-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF%DB%8C-%D8%B1%D8%A7-%D8%A7%D9%81%D8%B2%D8%A7%DB%8C%D8%B4-%D8%AF%D9%87%DB%8C%D9%85-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-memory-r8nyqaifbzfa</link>
                <description>چقدر به مشکلات performance برنامه ای که نوشتید آگاه هستید؟ آیا می دانید کدام متد بیش از حد زمان بر است؟ inflate شدن کدام UI بیش از حد انتظاراتان طول می کشد؟ چه قسمت هایی از برنامه Memory leak دارد؟ کجا از ساختار داده مناسب استفاده نکردید؟ کجا با مشکل overdraw روبرو هستید و می توانید آن را بهبود دهید؟ آیا می دانید چرا برنامه لَگ دارد؟ چرا زمانی که برنامه شما اجرا می شود، کلا عملکرد سیستم افت می کند؟همانطور که مشخص است performance برای برنامه اندروید جنبه های مختلفی دارد. توانایی سخت افزاری گوشی های موبایل نسبت به لپ تاپ یا کامپیوتر بسیار کمتر است و سیستم resource کمتری در اختیار دارد در نتیجه استفاده درست از آن resource محدود، برای داشتن performance مناسب، بسیار مساله ی مهمی است.با اینکه performance جنبه های مختلفی دارد ولی شاید به شکل کلی بتوان آن را به:سی پی یو (CPU)حافظه (Memory)گرافیک (GPU)تقسیم کرد. در بخش اول به CPU پرداختیم. در این مقاله تمرکزمان بر روی Memory است و آن را از جنبه های مختلف بررسی می کنیم. ابتدا به سراغ مواردی می رویم که عمومی تر هستند و سپس به سراغ Profiler می رویم تا در یافتن و حل مشکلات از آن کمک بگیرم. قبل از اینکه ادامه دهیم خوب است با کارکرد کلی Memory در اندروید آشنا شویم. این ارائه به شکل مختصر و مفید به این موضوع پرداخته است. پیشنهاد می کنم حتما آن را ببینید.ساختار داده ی مناسبشاید عنوان عجیب به نظر بیاید ولی می توانیم با انتخاب ساختار داده مناسب به شکل مطلوب تری از حافظه استفاده کنیم.بهتر است به جای HashMap از ArrayMap استفاده کنیم(اگر تعداد داده هایمان کم، حدود ۱۰۰، است). اینجا دلیل این قضیه توضیح داده شده است.اگر می خواهید از HashMap استفاده کنید و جنس Key از primitive type ها است بهتر است از SpareArray استفاده کنید(اگر تعداد داده هایمان کم، حدودد ۱۰۰، است). اینجا دلیل این قضیه توضیح داده شده است.تا جای ممکن از Enum استفاده نکنید و آن را با Constant های ساده جایگزین کنید.  اینجا دلیل این قضیه توضیح داده شده است.از ساختار داده بهینه تر اسفاده کنید. مثلا استفاده درست StringBuilder به جای String می تواند کار GC را بسیار کمتر کند(در ادامه این مسئله را در قالب مثال بررسی می کنیم).کم کردن حجم اپلیکیشنحجم اپلیکیشن چه ربطی به Memory دارد؟ با اجرا شدن اپلیکیشن، فایل های dex مربوط به آن، برای اجرای برنامه، به Memory منتقل می شوند. در واقع همیشه بخشی از Memory که به برنامه ی شما اختصاص داده می شود توسط کدها، ریسورس ها و ... اشغال شده و بخشی از آن در طول کار کردن با برنامه به صورت داینامیک اختصاص داده می شود. پس با کم کردن حجم اپلیکیشن در عمل فضای کمتری از Memory اشغال می شود که قطعا performance بهتری را به همراه خواهد داشت. اینکه چطور حجم apk را کم کنیم از بحثمان خارج است ولی ارائه های مختلفی در Google I/O پیرامون این قضیه وجود دارد. مموری پروفایلر (Memory Profiler) دو موردی که تا اینجا بررسی کردیم، عمومی بودند (البته این چیزی از ارزش ها و اهمیت آن ها کم نمی کند :)). در ادامه بیشتر به مواردی می پردازیم که به شیوه پیاده سازی ما بر می گردند. سعی می کنیم به کمک Memory Profiler مشکل را پیدا کرده و سپس راه حلی برای آن بیابیم.گاربج کالکتور (GC)عملیات GC زمان بر نیست ولی اگر زیاد و بیجا کال شود می تواند تاثیر مشهودی بر عملکرد اپلیکیشن داشته باشد(اینجا توضیح مختصری داده شده است). در این مقاله به الگوریتم GC نمی پردازیم(اگر مایل بودید یکی از الگوریتم ها اینجا توضیح داده شده است) بلکه فقط به طور کلی می خواهیم بدانیم GC کِی شروع به کار می کند.اندروید به هر برنامه ای که اجرا می شود، فضایی در حافظه اختصاص می دهد که به آن Heap می گویند. طبیعتا این فضا محدود است. زمانی که شما از تمام این فضا استفاده کردید و نیاز به فضای بیشتری داشتید، پیش از آنکه حافظه بیشتری به شما اختصاص داده شود، ابتدا GC شروع به کار می کند. در نتیجه ی عملکرد GC ممکن است فضای مورد نیاز شما خالی شده باشد. در غیر این صورت اندروید فضای بیشتری به شما اختصاص می دهد. با توجه به این عملکرد، اگر ما فضایی که در اختیار داریم را به سرعت با آبجکت هایی پر کنیم که همگی قابلیت حذف شدن دارند، سبب کال شدن GC به تعداد زیاد در بازه زمانی کم می شویم و طبیعتا این موضوعی است که به دنبال جلوگیری از آن هستیم. به سراغ یه مثال برویم.public class MainActivity extends AppCompatActivity {

  private String numSequence = &amp;quot&amp;quot

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    for (int i = 0; i &lt; 50000; i++) {
      numSequence = numSequence.concat(String.valueOf(i));
    }
  }
}همانطور که در کد بالا مشخص است در یک loop مقدار یک String را آپدیت می شود. به نظر شما این کد مشکلی دارد؟ چه مشکلی؟ابتدا برنامه را به کمک Profiler اجرا می کنیم. سپس تب Profiler (از نوار پایین) را باز می کنیم و روی بخش مربوط به Memory کلیک می کنیم. در مرحله بعد گزینه سوم یعنی Record Java/Kotlin Allocations را انتخاب کرده و روی Record کلیک می کنیم(از بخش اول می دانیم که برای بررسی دقیق جزییات نیاز داریم یک بازه زمانی را Record کنیم). پس از مدتی که مطمئن شدیم متد مربوطه اجرا شده فرآیند را Stop می کنیم.خروجی مشابه شکل زیر خواهد بود.هر کدام از آن آیکون های سفیدِ سطل آشغال، نماینده یک عملیات GC هستند!!! با یک loop ساده ما چقدر سیستم را برای پاک کردن حافظه تحت فشار گذاشتیم. اگر اعدادِ پایین عکس را ببینید، برای String، تعداد ۱۷۷۸۷۳ Allocation و ۱۵۴۷۷۵ Deallocation ثبت شده است. یعنی اکثر فضایی که برای String اشغال کردیم به سرعت آزاد شده. حال کد را به شکل زیر تغییر می دهیم.public class MainActivity extends AppCompatActivity {

  private String numSequence;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    StringBuilder builder = new StringBuilder();
    for (int i = 0; i &lt; 50000; i++) {
      builder.append(i);
    }
    numSequence = builder.toString();
  }
}خروجی اینگونه خواهد بود.همان عملیات، در زمان کمتر و بدون GC!!!مگر چه تغییری در کد ایجاد شد؟ صرفا در loop به جای String از StringBuilder استفاده شده است. همانطور که می دانید String در جاوا Immutable است. در واقع با هر Contact کردن، یک String جدید ایجاد می شود. و از طرفی String قبلی هم بلا استفاده است. نتیجه همان عملکرد انقلابی GC خواهد بود. اما کلاس StringBuilder در جاوا Mutable است. در نتیجه، با توجه به مسئله، خیلی بهینه تر عمل می کند.مموری لیک (Memory Leak)هرگاه صحبت از Memory است، نام Memory Leak می درخشد :) احتمالا تا به حال حداقل یکبار اسمش به گوشتان خورده است. شاید شایع ترین مشکلی باشد که اپلیکیشن های اندروید با آن دست و پنجه نرم می کنند. به کمک ورژن جدید Memory Profiler می توانیم Memory Leak ها را پیدا کنیم. اگر با Memory leak آشنا نیستید شاید این تعریف ویکیپدیا ذهنتان را واضح تر کند.In computer science, a memory leak is a type of resource leak that occurs when a computer program incorrectly manages memory allocations in a way that memory which is no longer needed is not released. A memory leak may also happen when an object is stored in memory but cannot be accessed by the running code.باز به سراغ کد می رویم. در ادامه دو Memory Leak ایجاد می کنیم و آن ها را برطرف می کنیم. اینطور هم با مصادیق  Memory Leak و هم با Memory Profiler بیشتر آشنا می شویم. پروژه ای که می خواهیم بررسی کنیم دو Activity دارد. Activity اول خالی است و صرفا یک Intent به Activity دوم دارد. public class MainActivity extends AppCompatActivity {
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Intent intent = new Intent(this, SecondActivity.class);
    startActivity(intent);
  }
}در Activity دوم هم یک AsyncTask را شروع می کنیم که به شکل Inner Class تعریف شده است. در Async Task چند loop تو در تو، صرفا به عنوان یک عملیات زمان بر، نوشته شده است.public class SecondActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);
    
    MyAsyncTask myAsyncTask = new MyAsyncTask();
    myAsyncTask.execute();
  }

  public class MyAsyncTask extends AsyncTask&lt;Void, Void, Void&gt; {

    @Override
    protected Void doInBackground(Void... voids) {
      for (int b = 0; b &lt; 100000; b++) {
        for (int a = 0; a &lt; 100000; a++) {
          for (int i = 0; i &lt; 100000; i++) {
            StringBuilder stringBuilder = new StringBuilder();
            for (int j = 0; j &lt; 100; j++) {
              stringBuilder.append(j);
            }
          }
        }
      }
      return null;
    }
  }
}حال برنامه را به کمک Profiler اجرا می کنیم. پس از اجرای برنامه، طبق کد، از MainActivity به SecondActivity خواهیم رفت و AsyncTask شروع به کار می کند. حال دکمه back را می زنیم که به MainActivity برگردیم. در این مرحله Profiler را باز می کنیم، روی بخش Memory کلیک می کنیم، گزینه اول یعنی Capture Heap Dump را انتخاب کرده و در نهایت روی Record را آغاز می کنیم.این گزینه، وضعیت فعلی Heap را به ما نشان می دهد. پیش از آنکه سراغ بررسی آن برویم شما انتظار دارید SecondActivity در Heap وجود داشته باشد یا خیر؟ چرا؟ یک بار سناریو را مرور کنید. نتایج Profiler چه چیزی به ما نشان می گویند؟اوپس! Memory Leak !چرا؟ مگر پس از زدن back در سناریو، SecondActivity بسته نشده است؟ پس چرا در حافظه باقی مانده و Memory Leak ایجاد کرده؟ جواب این سوال در طراحی ما نهفته است. ما AsyncTask را به شکل Inner Class تعریف کردیم. این سبب شده بین SecondActivity و  AsyncTask ارتباطی شکل بگیرد. زمانی که ما back زدیم به درستی SecondActivity بسته شد ولی به علت اینکه AsyncTask هنوز مشغول کار بود و با SecondActivity هم ارتباط داشت در عمل SecondActivity تا زمانی که کار AsyncTask تمام شود در حافظه می ماند.برای حل این مشکل به سادگی می توان AsyncTask را به شکل کلاس مستقل تعریف کرد. کد را به شکل زیر تغییر می دهیم.public class SecondActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);

    MyAsyncTask myAsyncTask = new MyAsyncTask();
    myAsyncTask.execute();
  }
}public class MyAsyncTask extends AsyncTask&lt;Void, Void, Void&gt; {

  @Override
  protected Void doInBackground(Void... voids) {
    for (int b = 0; b &lt; 100000; b++) {
      for (int a = 0; a &lt; 100000; a++) {
        for (int i = 0; i &lt; 100000; i++) {
          StringBuilder stringBuilder = new StringBuilder();
          for (int j = 0; j &lt; 100; j++) {
            stringBuilder.append(j);
          }
        }
      }
    }
    return null;
  }
}سناریو قبل را مجدد تکرار کرده و Heap را بررسی می کنیم.تغییرات موفقیت آمیز بود. مشکل Memory Leak حل شد. یک مرحله جلوتر می رویم. یک Interface تعریف می کنیم که بین SecondActivity و AsyncTask ارتباط برقرار کند. کدها به شرح زیر خواهند بود.public interface MyCallback {

  void onTaskStarted();

  void onTaskFinished();
} public class SecondActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_second);

    MyAsyncTask myAsyncTask = new MyAsyncTask(new MyCallback() {
      @Override
      public void onTaskStarted() {
        Toast.makeText(SecondActivity.this, &amp;quotStart&amp;quot, Toast.LENGTH_SHORT).show();
      }

      @Override
      public void onTaskFinished() {
        Toast.makeText(SecondActivity.this, &amp;quotEnd&amp;quot, Toast.LENGTH_SHORT).show();
      }
    });
    myAsyncTask.execute();
  }
}public class MyAsyncTask extends AsyncTask&lt;Void, Void, Void&gt; {

  private MyCallback myCallback;

  public MyAsyncTask(MyCallback myCallback) {
    this.myCallback = myCallback;
  }

  @Override
  protected void onPreExecute() {
    super.onPreExecute();
    myCallback.onTaskStarted();
  }

  @Override
  protected Void doInBackground(Void... voids) {
    for (int b = 0; b &lt; 100000; b++) {
      for (int a = 0; a &lt; 100000; a++) {
        for (int i = 0; i &lt; 100000; i++) {
          StringBuilder stringBuilder = new StringBuilder();
          for (int j = 0; j &lt; 100; j++) {
            stringBuilder.append(j);
          }
        }
      }
    }
    return null;
  }

  @Override
  protected void onPostExecute(Void unused) {
    super.onPostExecute(unused);
    myCallback.onTaskFinished();
  }
}سناریو قبل را تکرار می کنیم. آیا Memory Leak رخ می دهد؟ چرا؟بله مجددا در دام Memory Leak افتادیم. در واقع MyCallback بین SecondActivity و AsyncTask ارتباط بر قرار می کند و مانع پاک شدن آن از Memory می شود. برای حل این مشکل دو راه عمومی وجود دارد. یکی cancel کردن AsyncTask در متد OnDestroy (که باید در AsyncTask هم چک شود) و دیگری استفاده از WeakReference. مثلا بدین شکل:private WeakReference&lt;MyCallback&gt; myCallbackWeakReference;

public MyAsyncTask(MyCallback myCallback) {
  myCallbackWeakReference = new WeakReference&lt;&gt;(myCallback);
}اگر به موضوع علاقه داشتید در این ویدیو مثال دیگری از MemoryLeak مطرح شده و برای پیدا کردن آن هم از Memory Profiler استفاده شده است.جمع بندیTools not Rules این جمله ای است که چندین بار در ارائه های مربوط به Performance به گوشم خورد. به نظر من خیلی جمله ی دقیقی است. در عمل قانونِ دقیق و مشخصی برای مباحث Performance وجود ندارد ولی اگر  شناخت خوبی از ابزارها داشته باشیم، می توانیم به کمک آن ها مشکلات را پیدا و حل کنیم. البته ابزارها همه چیز نیستند. هر چه دانش درست تر و عمیق تری نسبت به بخش های مختلف اندروید و زبان داشته باشیم، می توانیم تحلیل درست تری از اطلاعات داشته باشیم. پس علاوه بر یادگیریِ استفاده از ابزارها، باید دانش فنی خود را هم تقویت کنیم.</description>
                <category>شکیب حبیبی</category>
                <author>شکیب حبیبی</author>
                <pubDate>Sun, 17 Oct 2021 10:24:46 +0330</pubDate>
            </item>
                    <item>
                <title>چگونه Performance اپلیکیشن اندرویدی را افزایش دهیم؟ / بخش اول: CPU</title>
                <link>https://virgool.io/@shkbhbb/%DA%86%DA%AF%D9%88%D9%86%D9%87-performance-%D8%A7%D9%BE%D9%84%DB%8C%DA%A9%DB%8C%D8%B4%D9%86-%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF%DB%8C-%D8%B1%D8%A7-%D8%A7%D9%81%D8%B2%D8%A7%DB%8C%D8%B4-%D8%AF%D9%87%DB%8C%D9%85-%D8%A8%D8%AE%D8%B4-%D8%A7%D9%88%D9%84-cpu-m8zykfhcudxw</link>
                <description>چقدر به مشکلات performance برنامه ای که نوشتید آگاه هستید؟ آیا می دانید کدام متد بیش از حد زمان بر است؟ inflate شدن کدام UI بیش از حد انتظاراتان طول می کشد؟ چه قسمت هایی از برنامه Memory leak دارد؟ کجا از ساختار داده مناسب استفاده نکردید؟ کجا با مشکل overdraw روبرو هستید و می توانید آن را بهبود دهید؟ آیا می دانید چرا برنامه لَگ دارد؟ چرا زمانی که برنامه شما اجرا می شود، کلا عملکرد سیستم افت می کند؟ همانطور که مشخص است performance برای برنامه اندروید جنبه های مختلفی دارد. توانایی سخت افزاری گوشی های موبایل نسبت به لپ تاپ یا کامپیوتر بسیار کمتر است و سیستم resource کمتری در اختیار دارد در نتیجه استفاده درست از آن resource محدود، برای داشتن performance مناسب، بسیار مساله ی مهمی است. با اینکه performance جنبه های مختلفی دارد ولی شاید به شکل کلی بتوان آن را به:سی پی یو (CPU)حافظه (Memory)گرافیک (GPU)تقسیم کرد. در این مقاله تمرکزمان بر روی CPU است و در مقالات بعد، به دو مورد دیگر خواهم پرداخت. اندروید پروفایلر (Android Profiler)ما برای اینکه جواب سوالاتمان راجع به performance را پیدا کنیم نیاز به داده داریم. Android Profiler این داده ها را جمع آوری کرده و در اختیار ما قرار می دهد. در ادامه با اینکه چطور می توانیم از داده هایی که در اختیارمان قرار گرفته به مشکلات پی ببریم آشنا می شویم.پیش از آنکه جلوتر رویم به این سه نکته توجه کنید:اگر فکر می کنید در ادامه قرار است یک راه حل مشخص و جادویی برای بهبود performance برنامه تان معرفی شود سخت در اشتباهید. در ادامه با یک مثال سعی می کنیم با استفاده از ابزارهایی که در اختیار داریم مشکلات را پیدا کنیم. برای اینکه performance برنامه تان را بهبود ببخشید باید با این ابزارها سر و کله بزنید و مشکلات مخصوص خودتان را پیدا کنید.مثال هایی که در ادامه می آید صرفا به هدف یادگیری است. پس فکر نکنید که فلان بخش که واضح بود و برای تشخیص مشکل اصلا نیاز به Android Profiler نبود. این صرفا یک مثال برای بیان روش است و می توانید در پروژه شما خیلی پیچیده تر باشد.برای بدست آوردن اطلاعات بهتر و دقیق تر پیشنهاد می کنم حتما برنامه را روی موبایل واقعی اجرا کنید. گاهی اطلاعاتی که توسط emulator ارائه می شود آن دقت را ندارد (در ادامه گاهی برای ضبط راحت تر از genymotion استفاده شده است) اندروید پروفایلر از طریق منو پایین صفحه در دسترس است.برای اینکه به جزییات اطلاعات CPU دسترسی داشته باشید باید یک بازه زمانی را Record کنید. حال می توانید تنظیم کنید که بلافاصله بعد از باز شدن اپ Record شروع شود یا اینکه هر موقعی که خودتان خواستید Record را شروع کنید. در هر دو مورد مشخص کردن پایان Record با شماست. توجه داشته باشید که معمولا طول Record نباید خیلی زیاد باشد چرا که حجم اطلاعات دریافتی بسیار زیاد خواهد بود و تحلیل آن ها سخت تر می شود ضمن اینکه اندروید استدیو هم گاهی بسیار کند می شود. ما از روش اول (شروع Record به محض باز شدن برنامه) استفاده می کنیم. فارغ از اینکه از چه روشی استفاده کنید می توان یکی از ۴ متد زیر را انتخاب کرد. متد اول(Sample Java Methods)متد دوم(Trace Java Methods)متد سوم(Sample C/C++ Functions)متد چهارم(Trace System Calls)توضیحات هر متد در اینجا موجود است. به شکل کلی برای اینکه با CPU Profiler بیشتر آشنا شوید خواندن این داکیومنت می تواند مفید باشد.برای پیدا کردن مشکل، استفاده از متد آخر پیشنهاد شده است زیرا تاثیر کمی بر روی Performance دارد و از طرفی اطلاعات کم ولی مکفی برای حدس زدن مشکل در اختیار ما قرار می دهد. زمانی که به بخش خاصی مشکوک شدیم برای بررسی جزییات دقیق تر به سراغ متد سوم می رویم. چالش اول: Startupهمانطور که در گیف بالا مشخص است بین زمانی که من روی اپ کلیک می کنم تا زمانی که صفحه اول برنامه را می بینم یک صفحه سفید نمایش داده می شود. این احتمالا به این معنی است که پروسه startup برنامه دچار مشکل است و جایی سبب کندی شده است. در این مرحله به کمک CPU Profiler به دنبال مشکل خواهیم گشت. در ابتدا  تنظیمات مربوطه را انجام می دهیم که با شروع برنامه، Record  هم شروع شود و از متد Trace System Calls استفاده می کنیم. مراحل در گیف بالا مشخص است ولی به منظور سهولت در پیگیری به ترتیب آن ها را در زیر می نویسم:از بخش app بالا (Run/Debug Configuration) گزینه Edit Configuration را انتخاب می کنیم.از منو Profiling گزینه start this recording on startup را فعال می کنیم.گزینه cpu activity را انتخاب کرده و سپس از drop down گزینه Trace system calls را انتخاب می کنیم.با انتخاب Ok تغییرات را ذخیره می کنیم.گزینه Profile را انتخاب می کنیم تا برنامه اجرا شود.پس از چند ثانیه زمانی که گزینه stop نمایش داده شد و صفحه اول برنامه نمایش داده شده بود گزینه stop را انتخاب می کنیم.بعد از چند ثانیه شما به خروجی record دسترسی دارید. قسمتی که ما بیشتر با آن کار داریم بخش threads و زیر مجموعه ای است که پکیج برنامه خودمان را نمایش می دهد. این دیتا در این شرایط قابل استفاده نیست برای همین باید به کمک گزینه های زوم بالا سمت راست یا کلید های a w s d  زوم را بیشتر کنیم تا دیتا خواناتر باشد. بعد از کمی زوم کردن و اسکرول به سمت چپ من به این بخش رسیدم.همانطور که مشخص است پروسه bindApplication حدود ۱.۲۴ ثانیه زمان نیاز داشته است. این عدد شک برانگیز است. به طور عادی نباید این میزان طول بکشد. حالت Trace system calls اطلاعات بیشتری در اختیار ما قرار نمی دهد که بتوانیم دقیق تر بررسی کنیم به همین منظور متد را به Sample C/C++ Functions تغییر می دهیم و مجدد برنامه را اجرا می کنیم تا با اطلاعاتی که بدست آوردیم به بررسی دقیق تر بپردازیم.پس از اجرای برنامه در همان نگاه اول متوجه تفاوت حجم اطلاعات می شوید. از منو سمت راست گزینه flame chart را انتخاب کنید و سپس bindApplication را در آن سرچ کنید.همانطور که مشخص شده آن قسمتی که مربوط به سرچ شما می شود bold شده است. اگر کمی بیشتر زوم کنیم متوجه می شویم که متد com.shkbhbb.performancetest.MyApp.OnCreate حدود ۹۰۰.۶۳ میلی ثانیه زمان به خود اختصاص داده است یعنی حدود ۷۰ درصد زمان bindApplication !! وقتی به سراغ متد onCreate در کلاس MyApp می رویم با کد زیر مواجه می شویم.public class MyApp extends Application {

  public static String ID = &amp;quot&amp;quot

  @Override
  public void onCreate() {
    super.onCreate();

    for (int i = 0; i &lt; 20000; i++) {
      ID = ID.concat(String.valueOf(i));
    }
  }
}مشخصا این loop زمانبر در کلاس application سبب کند شدن startup برنامه ما شده است. با قرار دادن این loop در یک thread مجزا مشکل startup مرتفع می شود. پس از اعمال این تغییر زمان bindApplication به ۲۹۷ میلی ثانیه کاهش پیدا کرد. (همیشه برای بدست آوردن زمان از Trace system calls استفاده کنید)چالش دوم: بهینه بودن پیاده سازی UIشمایل صفحه اول برنامه و کد آن به شرح زیر است.&lt;?xml version=&amp;quot1.0&amp;quot encoding=&amp;quotutf-8&amp;quot?&gt;
&lt;LinearLayout xmlns:android=&amp;quothttp://schemas.android.com/apk/res/android&amp;quot
  android:layout_width=&amp;quotmatch_parent&amp;quot
  android:layout_height=&amp;quotmatch_parent&amp;quot
  android:orientation=&amp;quotvertical&amp;quot&gt;

  &lt;LinearLayout
    android:layout_width=&amp;quotmatch_parent&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:layout_weight=&amp;quot1&amp;quot
    android:orientation=&amp;quothorizontal&amp;quot&gt;

    &lt;View
      android:layout_width=&amp;quot0dp&amp;quot
      android:layout_height=&amp;quotwrap_content&amp;quot
      android:layout_weight=&amp;quot1&amp;quot
      android:background=&amp;quot@color/green_4c&amp;quot /&gt;

    &lt;LinearLayout
      android:layout_width=&amp;quot0dp&amp;quot
      android:layout_height=&amp;quotmatch_parent&amp;quot
      android:layout_weight=&amp;quot1&amp;quot
      android:orientation=&amp;quotvertical&amp;quot&gt;

      &lt;View
        android:layout_width=&amp;quotmatch_parent&amp;quot
        android:layout_height=&amp;quot0dp&amp;quot
        android:layout_weight=&amp;quot1&amp;quot
        android:background=&amp;quot@color/red&amp;quot /&gt;

      &lt;View
        android:layout_width=&amp;quotmatch_parent&amp;quot
        android:layout_height=&amp;quot0dp&amp;quot
        android:layout_weight=&amp;quot2&amp;quot
        android:background=&amp;quot@color/colorPrimaryDark&amp;quot /&gt;

      &lt;View
        android:layout_width=&amp;quotmatch_parent&amp;quot
        android:layout_height=&amp;quot0dp&amp;quot
        android:layout_weight=&amp;quot3&amp;quot
        android:background=&amp;quot@color/black&amp;quot /&gt;
    &lt;/LinearLayout&gt;
  &lt;/LinearLayout&gt;

  &lt;LinearLayout
    android:layout_width=&amp;quotmatch_parent&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:layout_weight=&amp;quot1&amp;quot
    android:orientation=&amp;quotvertical&amp;quot&gt;

    &lt;LinearLayout
      android:layout_width=&amp;quotmatch_parent&amp;quot
      android:layout_height=&amp;quot0dp&amp;quot
      android:layout_weight=&amp;quot1&amp;quot&gt;

      &lt;View
        android:layout_width=&amp;quot0dp&amp;quot
        android:layout_height=&amp;quotmatch_parent&amp;quot
        android:layout_weight=&amp;quot1&amp;quot
        android:background=&amp;quot@color/colorPrimaryDark&amp;quot /&gt;

      &lt;View
        android:layout_width=&amp;quot0dp&amp;quot
        android:layout_height=&amp;quotmatch_parent&amp;quot
        android:layout_weight=&amp;quot1&amp;quot
        android:background=&amp;quot@color/blue_03&amp;quot /&gt;

      &lt;View
        android:layout_width=&amp;quot0dp&amp;quot
        android:layout_height=&amp;quotmatch_parent&amp;quot
        android:layout_weight=&amp;quot1&amp;quot
        android:background=&amp;quot@color/yellow&amp;quot /&gt;
    &lt;/LinearLayout&gt;

    &lt;LinearLayout
      android:layout_width=&amp;quotmatch_parent&amp;quot
      android:layout_height=&amp;quot0dp&amp;quot
      android:layout_weight=&amp;quot1&amp;quot
      android:gravity=&amp;quotcenter&amp;quot&gt;

      &lt;View
        android:layout_width=&amp;quot100dp&amp;quot
        android:layout_height=&amp;quot100dp&amp;quot
        android:background=&amp;quot@color/colorAccent&amp;quot /&gt;
    &lt;/LinearLayout&gt;

  &lt;/LinearLayout&gt;

&lt;/LinearLayout&gt;همانطور که مشخص است این صفحه به کمک linear layout پیاده سازی شده است. طبیعتا به علت محدودیت های این layout  مجبور به استفاده تو در توی آن شده ام. من حدس می زنم احتمالا اگر به کمک constraint layout این صفحه را بازنویسی کنم performance بهتری خواهد داشت چرا که می توانم آن را flat پیاده سازی کنم. برای این کار اول به کمک profiler میزان زمانی که برای inflate شدن پیاده سازی فعلی نیاز است را پیدا می کنم. همانطور که در تصویر زیر مشخص است این پروسه حدود ۱۳ میلی ثانیه زمان به خود اختصاص داده است.حال پیاده سازی را به شکل زیر تغییر می دهم.&lt;?xml version=&amp;quot1.0&amp;quot encoding=&amp;quotutf-8&amp;quot?&gt;
&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&amp;quothttp://schemas.android.com/apk/res/android&amp;quot
  xmlns:app=&amp;quothttp://schemas.android.com/apk/res-auto&amp;quot
  android:layout_width=&amp;quotmatch_parent&amp;quot
  android:layout_height=&amp;quotmatch_parent&amp;quot&gt;

  &lt;View
    android:layout_width=&amp;quot0dp&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:background=&amp;quot@color/green_4c&amp;quot
    app:layout_constraintBottom_toTopOf=&amp;quot@id/half_horizontal_guide&amp;quot
    app:layout_constraintLeft_toLeftOf=&amp;quotparent&amp;quot
    app:layout_constraintRight_toLeftOf=&amp;quot@id/half_vertical_guide&amp;quot
    app:layout_constraintTop_toTopOf=&amp;quotparent&amp;quot /&gt;

  &lt;View
    android:id=&amp;quot@+id/red_v&amp;quot
    android:layout_width=&amp;quot0dp&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:background=&amp;quot@color/red&amp;quot
    app:layout_constraintBottom_toTopOf=&amp;quot@id/primary_dark_v&amp;quot
    app:layout_constraintLeft_toRightOf=&amp;quot@id/half_vertical_guide&amp;quot
    app:layout_constraintRight_toRightOf=&amp;quotparent&amp;quot
    app:layout_constraintTop_toTopOf=&amp;quotparent&amp;quot
    app:layout_constraintVertical_weight=&amp;quot1&amp;quot /&gt;

  &lt;View
    android:id=&amp;quot@+id/primary_dark_v&amp;quot
    android:layout_width=&amp;quot0dp&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:background=&amp;quot@color/colorPrimaryDark&amp;quot
    app:layout_constraintBottom_toTopOf=&amp;quot@id/black_v&amp;quot
    app:layout_constraintLeft_toRightOf=&amp;quot@id/half_vertical_guide&amp;quot
    app:layout_constraintRight_toRightOf=&amp;quotparent&amp;quot
    app:layout_constraintTop_toBottomOf=&amp;quot@id/red_v&amp;quot
    app:layout_constraintVertical_weight=&amp;quot2&amp;quot /&gt;

  &lt;View
    android:id=&amp;quot@+id/black_v&amp;quot
    android:layout_width=&amp;quot0dp&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:background=&amp;quot@color/black&amp;quot
    app:layout_constraintBottom_toTopOf=&amp;quot@id/half_horizontal_guide&amp;quot
    app:layout_constraintLeft_toRightOf=&amp;quot@id/half_vertical_guide&amp;quot
    app:layout_constraintRight_toRightOf=&amp;quotparent&amp;quot
    app:layout_constraintTop_toBottomOf=&amp;quot@id/primary_dark_v&amp;quot
    app:layout_constraintVertical_weight=&amp;quot3&amp;quot /&gt;


  &lt;View
    android:id=&amp;quot@+id/primary_v&amp;quot
    android:layout_width=&amp;quot0dp&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:background=&amp;quot@color/colorPrimary&amp;quot
    app:layout_constraintBottom_toTopOf=&amp;quot@id/quarter_horizontal_guide&amp;quot
    app:layout_constraintLeft_toLeftOf=&amp;quotparent&amp;quot
    app:layout_constraintRight_toLeftOf=&amp;quot@id/blue_v&amp;quot
    app:layout_constraintTop_toBottomOf=&amp;quot@id/half_horizontal_guide&amp;quot /&gt;

  &lt;View
    android:id=&amp;quot@+id/blue_v&amp;quot
    android:layout_width=&amp;quot0dp&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:background=&amp;quot@color/blue_03&amp;quot
    app:layout_constraintBottom_toTopOf=&amp;quot@id/quarter_horizontal_guide&amp;quot
    app:layout_constraintLeft_toRightOf=&amp;quot@id/primary_v&amp;quot
    app:layout_constraintRight_toLeftOf=&amp;quot@id/yellow_v&amp;quot
    app:layout_constraintTop_toBottomOf=&amp;quot@id/half_horizontal_guide&amp;quot /&gt;

  &lt;View
    android:id=&amp;quot@+id/yellow_v&amp;quot
    android:layout_width=&amp;quot0dp&amp;quot
    android:layout_height=&amp;quot0dp&amp;quot
    android:background=&amp;quot@color/yellow&amp;quot
    app:layout_constraintBottom_toTopOf=&amp;quot@id/quarter_horizontal_guide&amp;quot
    app:layout_constraintLeft_toRightOf=&amp;quot@id/blue_v&amp;quot
    app:layout_constraintRight_toRightOf=&amp;quotparent&amp;quot
    app:layout_constraintTop_toBottomOf=&amp;quot@id/half_horizontal_guide&amp;quot /&gt;

  &lt;View
    android:layout_width=&amp;quot100dp&amp;quot
    android:layout_height=&amp;quot100dp&amp;quot
    android:background=&amp;quot@color/colorAccent&amp;quot
    app:layout_constraintBottom_toBottomOf=&amp;quotparent&amp;quot
    app:layout_constraintLeft_toLeftOf=&amp;quotparent&amp;quot
    app:layout_constraintRight_toRightOf=&amp;quotparent&amp;quot
    app:layout_constraintTop_toBottomOf=&amp;quot@id/quarter_horizontal_guide&amp;quot /&gt;

  &lt;androidx.constraintlayout.widget.Guideline
    android:id=&amp;quot@+id/half_vertical_guide&amp;quot
    android:layout_width=&amp;quotwrap_content&amp;quot
    android:layout_height=&amp;quotwrap_content&amp;quot
    android:orientation=&amp;quotvertical&amp;quot
    app:layout_constraintGuide_percent=&amp;quot.5&amp;quot /&gt;

  &lt;androidx.constraintlayout.widget.Guideline
    android:id=&amp;quot@+id/half_horizontal_guide&amp;quot
    android:layout_width=&amp;quotwrap_content&amp;quot
    android:layout_height=&amp;quotwrap_content&amp;quot
    android:orientation=&amp;quothorizontal&amp;quot
    app:layout_constraintGuide_percent=&amp;quot.5&amp;quot /&gt;

  &lt;androidx.constraintlayout.widget.Guideline
    android:id=&amp;quot@+id/quarter_horizontal_guide&amp;quot
    android:layout_width=&amp;quotwrap_content&amp;quot
    android:layout_height=&amp;quotwrap_content&amp;quot
    android:orientation=&amp;quothorizontal&amp;quot
    app:layout_constraintGuide_percent=&amp;quot.75&amp;quot /&gt;

&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;پس از تغییر دوباره به کمک profiler زمان inflate شدن را بررسی می کنم. در کمال تعجب زمان به حدود ۱۹ میلی ثانیه افزایش پیدا کرد !!پس به کمک profiler می توانیم پیاده سازی های مختلف را امتحان کنیم (در صورت نیاز) تا به بهترین پیاده سازی برای نیاز خودمان برسیم. این فقط مربوط به xml  نیست و در مورد کد java/kotlin هم قطعا صادق است. جمع بندیبرای حل مشکل performance راه حل ثابتی وجود ندارد بلکه باید به کمک ابزار های موجود سعی کنیم مشکلات را پیدا کنیم و مطمئن شویم راه حل جایگزینی که اجرا می کنیم واقعا مفیدتر و بهینه تر می باشد. CPU profiler در این مسیر بسیار به کمک ما می آید. به این نکته توجه داشته باشید که لزوما هر پروسه ی طولانی ای مشکل performance ندارد. دنبال پروسه هایی باشید که بیشتر از انتظار شما زمان بر بوده اند. همیشه هم دنبال تغییرات performance عجیب و غریب نباشید. اگر هر متد چند میلی ثانیه بهینه تر شود می تواند تاثیر بزرگی در بهبود کیفیت داشته باشد و برعکس عدم بهینه بودن های کوچک زمانی که در کنار هم قرار می گیرند می توانند تاثیر مشهودی بر عملکرد برنامه داشته باشند.امیدوارم این مطلب برای شما مفید بوده باشد</description>
                <category>شکیب حبیبی</category>
                <author>شکیب حبیبی</author>
                <pubDate>Sat, 16 Oct 2021 11:44:50 +0330</pubDate>
            </item>
                    <item>
                <title>اندروید و Nosql</title>
                <link>https://virgool.io/@shkbhbb/%D8%A7%D9%86%D8%AF%D8%B1%D9%88%DB%8C%D8%AF-%D9%88-nosql-vybjjtwraqea</link>
                <description>حتما همه تون تا حالا عنوان Nosql به گوشتون خورده. موضوعی که جدید نیست ولی این روزها تبش داغ تره. خیلی از برنامه نویسانِ گران قدر صرفا به علت دهن پرکن یا باحال بودن و بدون توجه به کاربرد، امکانات یا موقعیت ازش استفاده می کنن و سرشون هم بالا هست. اگر با Nosql آشنا نیستید این ارائه آقای Martin Fowler می تونه معرفی خوبی باشه.چرا Nosql؟من برای اینکه برم سمت nosql دو دلیل داشتم:پرفورمنس بهترسازگاری بیشترقبل از اینکه دلایلم رو توضیح بدم باید به یک نکته اشاره کنم. وقتی به صورت کلی پیرامون دیتابیس صحبت می کنیم موضوعاتی مثل transaction یا ACID یا scaling خیلی مهم و قابل بحث هستند ولی این رو در نظر بگیرید تقریبا هیچ کدوم در زمینه دیتابیس اندروید موضوعیت ندارند. ما معمولا سمت دیتابیس اندروید کار  critical ای رو انجام نمیدیم یا حجم دیتاها از حدی بیشتر نمیشه چرا که در این صورت با توجه به گستردگی دیوایس ها، اپلیکیشن ممکنه به صورت جدی با مشکل پرفورمنس روبرو بشه. پس این موضوعات در حوزه دیتابیس مهم، و حتی خیلی مهم، هستند ولی به شکل به خصوص در دیتابیس اندروید خیر. بیشتر کاربرد دیتابیس در اندروید، نگه داشتن دیتا برای ارائه آفلاین به یوزر و پرفورمنس بهتر هست.تیم بک اند شرکت ما از دیتابیس Nosql استفاده می کنه. طبیعتا خروجی کار اون ها، که در قالب api به دست ما میرسه، با همین مایندست هست. هماهنگ کردن همچین خروجی ای با sql هزینه خواهد داشت. فارغ از هزینه هر تغییر کوچکی که در مدل ایجاد میشه ممکنه سبب تغییر ساختاری سمت اندروید بشه. همچنین با توجه به اینکه سمت اندروید کوئری هایی که من روی دیتابیس می زنم پیچیدگی خاصی نداره عملا پرداخت این هزینه بی فایده هست. با در نظر گرفتن ساختار دیتایی که بک اند در اختیار ما قرار میده و همینطور موقعیت های سمت اندروید، دیتابیس Nosql سازگاری خیلی بیشتری با نیازهای ما داره.همینطور که اشاره کردم ما کوئری های خیلی پیچیده ای نداریم ولی از اون طرف تعداد عملیات write و مخصوصا read زیادی داریم. یکی از فیچر های اپلیکیشن ما chat هست به همین دلیل حجم زیادی پیام در دیتابیس ذخیره و خوانده میشه. نتیجتا سرعت برای ما عنصر مهمی هست. اینکه Nosql از sql سریع تره گزاره درستی نیست چون خیلی به شرایط، ساختار داده و ... بستگی داره. اگر شما کوئری های سنگینی دارید احتمالا sql خیلی بهینه تر از Nosql عمل می کنه. ولی در شرایط ما Nosql گزینه ی بهینه تر هست. گزینه هابا یک سرچ ساده متوجه میشید که تعداد زیادی گزینه برای Nosql سمت اندروید وجود داره ولی تعداد گزینه هایی که بشه بهشون اعتماد کرد زیاد نیست. من در نهایت به دو گزینه رسیدم:Object boxRealmبرای انتخاب گزینه مناسب تر یک سری ملاک تعریف کردم:سرعت: من یکی از اهدافم برای استفاده از Nosql سرعت بود پس این آیتم برام ارزش بالایی داره. سرعت read و access و update از write برای من اهمیت بیشتری داره.داکیومنت: دیتابیس چیزی نیست که هر بار راحت بشه تغییرش داد. برای همین مهم هست که یه داکیومنت خوب و کامل داشته باشه که بشه همون اول همه امکاناتش رو دید و بررسی کرد.کامیونیتی: برخوردن به مشکل در کار اجتناب ناپذیره ولی وجود یک کامیونیتی خوب خیلی می تونه در حل مشکلات کمک کننده باشه.حجم: ما به علتِ بیزینسی، نیاز داریم حجم اپلیکیشن تا جای ممکن پایین باشه پس اینکه این لایبرری ها چه حجمی به apk اضافه می کنند هم مهم هست.فعال بودن: قطعا هیچ لایبرری کامل نیست ولی مهم هست که فعال باشه و امکانات بیشتری ارائه بده. برای دیتابیس این موضوع برای من مهم هست.تغییر: حالت ایده آل برای من اینه که با کمترین تغییر بتونم دیتابیس جدید رو جایگزین Room کنم. امکان observe: با توجه به نحوه استفاده من از دیتابیس امکان observe کردن دیتا خیلی خیلی برام مهم هست. هر چقدر این بخش دقیق تر پیاده شده باشد برای من مناسب تره. مثلا در Room شما امکان observe کردن یک entity رو دارید ولی نمی تونید جزییات تغییر رو داشته باشید. مثلا بهتون خبر میده دیتا تغییر کرده ولی نمیگه چه چیزی اضافه، حذف یا آپدیت شده. گزینه اولاولین ادعایی که Object box مطرح می کنه که در نگاه اول دل آدم رو می بره سرعتشه. اون ها ادعا می کنن ۱۰ برابر سریع تر از گزینه های sql ای هستن. برای اینکه ادعاشون رو ثابت کنن یه پروژه open source دارن که اومدن performance خودشون، realm و sqlite رو مقایسه کردن. پروژه رو می تونید اینجا ببینید.نتایج CRUD روی ۱۰۰۰۰۰ entity رو در Object box و Realm و Roomمن اپشون رو نصب کردم و واقعا سرعت متفاوتی داره. خیلی متفاوت. در همه زمینه ها. انقدر سرعت بالایی دارن که نیازی به استفاده async ازش وجود نداره! شما می تونید تو main thread باهاش کار کنید! البته اگر بخواید می تونید با استفاده از Rx ازش به شکل async هم استفاده کنید ولی تو داکیومنتش مثال هایی که داره تقریبا همگی sync هستند. یک نکته مثبت دیگه راجع به object box سیستم query ایش بود. به نظر میاد میشه کوئری ها با سطح معقولی از پیچیدگی رو به راحتی پیاده کرد و خوبیش این بود که compile time چک هم داشت.اما همه چیز راجع به object box انقدر مثبت نبود که ای کاش می بود ):مشکل اول: داکیومنتشون لزوما آپدیت نیست. مثلا توی این ایشو یه فیچر مهم اضافه کردن ولی من نتونستم تو داکیومنتشون پیداش کنم. مشکل دوم: بررسی فیچرهای مهمی که تعداد قابل توجهی بهشون نیاز دارن با فاصله زمانی خیلی طولانی انجام شده. مثلا این ایشو که اتفاقا خیلی هم مهم هست و در ادامه راجع بهش صحبت می کنم سال ۲۰۱۷ مطرح شده ولی هنوز اضافه نشده. ۴ سال! با بررسی گیت هاب به نمونه های مشابه برخورد می کنید.مشکل سوم: جنس آیدی باید فقط long باشه. برای من که جنس آیدی هام string هست خیلی این قضیه می تونه مهم باشه و کار رو سخت بکنه. اینطور در عمل نمی تونم از یه حجم خوبی از امکاناتش استفاده کنم چون آیدی لوکال و سرور با هم فرق می کنن. از طرفی اگر بخوام یه hash بنویسم که مثلا آیدی های string رو به long تبدیل کنه هزینه و مشکل خودش رو داره.مشکل چهارم: برای استفاده از object box باید مدلم رو تغییر میدادم. مثلا برای تعریف روابط One-to-Many یا Many-to-Many باید از کلاس های generic خودش استفاده می کردم که خیلی مورد نظرم نبود. ترجیح میدادم بشه این کار رو با annotation جلو برد. البته این موضوع رو احتمالا میشد با دیزاین پترن DTO برطرف کرد ولی در کل نیاز به تغییر زیادی داشت.مشکل پنجم: من وقتی داشتم object box رو به بخشی از پروژه اضافه می کردم به مشکلی برخوردم اما تقریبا نتیجه ی تمام سرچ ها به document اشون می رسید. این قضیه کمی نگران کننده بود چون به نظر می اومد community خوبی نداره و ممکنه روزی دست آدم رو بذاره تو پوست گردو.پی نوشت: سید محمد حسین جعفری در یکی از رویداد های لاگ کت ارائه خوبی داره با عنوان Android Offline که به object box هم اشاره می کنه. شاید برای آشنایی اولیه دیدن این ارائه هم مفید باشه.گزینه دومراجع به پرفورمنس realm افسانه های زیادی وجود داره :) مثلا تو این مقاله یه bench mark از چند تا دیتابیس و ORM آورده شده. با توجه به این نتایج realm پرفورمنس خوبی داره و حتی در read با اختلاف گزینه برتر هست ولی می تونید برید کامنت هاش رو بخونید. برو بچه های object box اومدن گفتن که این دروغی بیش نیست و ما از همه خفن تریم :)یا مثلا تو این مقاله سازنده ORMLite حسابی توپیده بهشون و گفته bench mark هاشون درست نیست. بنده خدا از خودش هم حسابی مایه گذاشته.لینکگفته غیر ممکنه که سرعت insert توی ORMLite (که خودش نوشته) از greenDAO انقدر بیشتر باشه :) به این مورد به عنوان نمونه ای از درست نبودن دیتا اشاره کرده.یا مثلا تو این مقایسه در عمل realm در هیچ حوزه ای در برابر حتی گزینه های sql ای حرفی برای گفتن نداره. آدم نمی دونه حرف کدوم رو باور کنه. من از همون پروژه object box استفاده کردم. برنامه رو روی گوشی خودم تست کردم و میشه گفت realm تقریبا در اکثر پرامترها نسبت به room و greenDAO برتری داشت غیر از یک مورد مهم که در ادامه بهش می پردازم. من با فرض اینکه realm از لحاظ پرفورمنس حداقل از room بدتر نیست به بررسی بیشترش پرداختم. اولین نکته ای که نظر رو جلب می کنه اینه که توسط mongoDB توسعه داده شده که این خیلی می تونه نکته مهمی باشه چون که mongoDB یکی از گزینه های خیلی معروف و پرکاربرد حوزه Nosql هست و حتما این تیم با همه نیازهای یه دیتابیس Nosql آشنا هست. اما مشکلاتی وجود داشت که باعث شد من خیلی جلوتر نرم. مشکل اول: realm نسبت به همه رقباش حجم دیتابیسش خیلی بیشتره. قطعا این موردی هست که خیلی برای کاربر نمی تونه جذاب باشه.مشکل دوم: ظاهرا realm نسبت به بقیه مصرف ram بیشتری داره. با توجه به اینکه ما گوشی های زیادی رو ساپورت می کنیم که ممکنه ram بالایی هم نداشته باشن این مورد هم خیلی تو ذوق زد.مشکل سوم: با اینکه توی تستی که من انجام دادم سرعت realm از room بیشتر بود ولی یک مشکل خیلی بزرگ داشت. access time اش نسبت به همه خیلی بیشتر بود و با توجه به اینکه من تعداد دسترسیم به دیتابیس زیاده این خیلی می تونه تاثیر گذار باشه. یعنی اگر من ۱۰۰۰ تا entity رو بخوام یکجا وارد دیتابیس کنم احتمالا realm نسبت به room بهتر عمل می کنه ولی وقتی بخوام ۱۰۰۰ تا entity رو مجزا وارد کنم access time خودش رو می تونه نشون بده.مشکل چهارم: realm حدود ۲ تا ۳ مگابایت حجم اپلیکیشن رو بالا می بره که رقم خیلی قابل توجهی هست. خودشون پیشنهاد دادن که برای برطرف کردن این مشکل می تونید برای معماری های مختلف خروجی مجزا بگیرید ولی اون خودش یه پیچیدگی جدید اضافه می کنه. مشکل پنجم: وظیفه بستن کانکشن دیتابیس با من بود. این یک مقدار می تونه تریکی باشه مخصوصا برای اپ ما که دسترسی به دیتابیس زیاد و از جاهای مختلفی هست که گاها ربطی به ui هم ندارن و زمان بسته بودن برنامه اتفاق می افتن. اینکه بستن کانکشن دست ما باشه می تونه احتمال memory leak رو بالاتر ببره.جمع بندیدر نهایت هیچ کدوم از این دو گزینه نتونستن نظر من رو جلب کنن(یا شاید بهتر بگم با نیازهای من سازگار نبودن). اگر اپلیکیشن تون با دیتابیس زیاد کار می کنه، مشکلی با اینکه جنس آیدی long باشه ندارید، می خواید از Nosql استفاده کنید و در اول راه هستید فکر می کنم object box می تونه گزینه مناسبی باشه. سرعتش به شکل چشم گیری بیشتره و امکانات قابل قبولی هم داره. من هیچ برتری در realm ندیدم نسبت به بقیه. فقط اینکه امکان encryption داره که فکر نمی کنم خیلی به کار بیاد ولی اگر برای شما مهمه حتما یه نگاه بهش بندازید.  </description>
                <category>شکیب حبیبی</category>
                <author>شکیب حبیبی</author>
                <pubDate>Wed, 29 Sep 2021 21:01:46 +0330</pubDate>
            </item>
                    <item>
                <title>دوره کارآموزی؛ مسیر یادگیری</title>
                <link>https://virgool.io/@shkbhbb/%D8%AF%D9%88%D8%B1%D9%87-%DA%A9%D8%A7%D8%B1%D8%A2%D9%85%D9%88%D8%B2%DB%8C-%D9%85%D8%B3%DB%8C%D8%B1-%DB%8C%D8%A7%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-cujgd8xkjusb</link>
                <description>منبعمقدمهحدود ۱ سال و اندی پیش، در حالی که در شرکت به عنوان تنها برنامه نویس اندروید مشغول به کار بودم، با توجه به زیاد شدن حجم کارها، شرکت بر آن شد که فردی را به عنوان کارآموز بخش اندروید استخدام کنیم. این همان موقعیتی بود که من دنبالش بودم. من همیشه عاشق آموزش دادن بودم. در اولین شرکتی که مشغول به کار شدم افتخار همکاری با مرد بزرگی به اسم اشکان حصارکی را داشتم که برای بخش موبایل یک دوره آموزشی طراحی کرده بود و افراد را در جهت توانمند شدن همراهی می کرد. من بسیار از آن سیستم لذت بردم و یاد گرفتم، برای همین تصمیم گرفتم این ایده را در شرکت فعلی هم پیاده کنم. زمانی که ایده ام را، طراحی یک برنامه آموزشی، با مدیریت مطرح کردم بسیار مورد استقبال قرار گرفت. برای آنکه افکار و خواسته هایمان همسو شود چندین بار راجع به نیازهای خودمان، نیازهای کارآموز، تجربیات خودمان از کار در شرکت های دیگر و محیط ایده آلی که مد نظرمان هست صحبت کردیم. نتیجه ماجرا این شد که من برای کارآموزی اندروید یک دوره طراحی کردم و سعی کردم نیازهای شرکت در آن دیده شود و از طرفی کارآموز به شکل منظم و با برنامه بتواند مسیر یادگیریش را طی کند. انگیزه نوشتن این مطلب در واقع بیان تجربیاتم از این مسیر است. ابتدا شیوه دوره و سپس نکاتی که به نظرم باید منتور و کارآموز رعایت کنند را بیان می کنم. مواردی که ذکر می شوند لزوما درست نیستند و من صرفا تفکرات و تجربیات خودم را بیان می کنم و قطعا شنیدن تجربه شما عزیزان سبب مسرت و یادگیری بنده است.دوره آموزشیهدفاول از هر چیز مهم است که هدفمان را از طراحی دوره بدانیم وگرنه قطعا به بیراهه می رویم. همانطور که قبلا ذکرش رفت ما دنبال دوره ای بودیم که نیازهای شرکت و کارآموز در آن دیده شود. کارآموز نیاز دارد که به شکل منظم و با همراهی، مسیر یادگیری را طی کند تا در نهایت بتواند نیازمندی ها و مهارت های اولیه کار در گروه را بدست آورد. از طرفی شرکت هم نیازهایی دارد. اول اینکه بتواند مهارت و توانایی های کارآموز را مورد ارزیابی قرار دهد. دوم اینکه بتواند یک محیط یادگیری برای کارآموز ایجاد کند. سوم اینکه بتواند کارآموز را محک بزند. چهارم اینکه دانش کسب شده توسط کارآموز در راستای دانش مورد نیاز برای پروژه ها باشد. البته که قطعا نیازها برای دو سمت بیشتر از این ها است و حتی نیازها فرد به فرد و شرکت به شرکت متفاوت هستند اما این ها مواردی بودند که ما به صورت کلی دنبالشان بودیم.کلیت دورهدوره شامل ۴ فاز است. در هر فاز یک سری فعالیت عملی به عنوان خروجی مد نظر است و در پایان ۴ فاز کارآموز یک پروژه واقعی به اشتراک گذاری عکس و متن را پیاده سازی کرده است. نسخه مینیمالیستی و ترکیبی توییتر و اینستاگرام.چرا در هر فاز بخشی از یک پروژه انجام می شود تا درنهایت پروژه به نتیجه برسد؟ دلیل اول اینکه فرد با نوشتن یک پروژه واقعی اعتماد به نفس بالایی بدست می آورد موردی که نوشتن تمرین های کوچک و مجزا در اختیارش نمی گذارد. دلیل دوم اینکه با پروسه واقعی تولید یک نرم افزار تا حدودی آشنا می شود و با چالش های واقعی دست و پنجه نرم می کند.فازهاهر فاز شامل ۵ قسمت است. بخش اول شامل لیستی از موضوعات و تکنولوژی هایی است که کارآموز باید در آن فاز یاد بگیرد.بخش دوم شامل لیستی از منابع پیشنهادی است. منابع گاهی بخشی از یک کتاب، یک مقاله یا یک لینک یوتیوب هستند. نکته مهم اینکه این ها صرفا منابع پیشنهادی هستند و وظیفه پیدا کردن منابع خوب و یادگیری بر عهده خود کارآموز است قطعا در این مسیر می تواند از تجربیات بقیه، از جمله منتور، استفاده کند. (در فاز آخر منبعی ارایه نمی شود و وظیفه یافتن و ارائه یک منبع خوب برای هر موضوع با کارآموز است)بخش سوم شامل سناریویی است که باید پیاده سازی شود. علاوه بر توضیح دقیق سناریو و نیازمندی ها، در قسمت هایی که نیاز است ui و یا api هم ارائه می شود. این سناریو ها در نهایت منجر به ساخت اپلیکیشن نهایی می شوند.بخش چهارم شامل یک سری سوال است که فرد باید به شکل مکتوب به آنها جواب بدهد. یکی از مهمترین نکات در زمینه یادگیری سوال است. سوال ها در ذهن ما قلاب می شوند و کمک می کنند که فرآیند یادگیری بهتر اتفاق بی افتد. در واقع سعی من بر این بود که سوال ها یک مسیر آموزشی غیر مستقیم را برای کارآموز ترسیم کنند. سوالات بیشتر پیرامون مفاهیم و یا بخش های اصلی تکنولوژی هستندقسمت پنجم شامل یک فعالیت عملی است. این فعالیت می تواند شامل نوشتن یک مقاله (مثلا موضوع یکی از مقالات &quot;راهکارهای async در اندروید و مقایسه و کاربرد آنها&quot; بود)، تولید یک ویدیو آموزشی یا یک ارائه برای بقیه تیم باشد. پیش نیاز همه این فعالیت ها این است که کارآموز مطلب را دقیق فرا گرفته باشد. پس به طور غیر مستقیم به کارآموز کمک می کند در موضوعاتی که مهم تر هستند عمیق تر شود. ضمن اینکه همه این فعالیت ها، مهارت های فرد را افزایش می دهند. مهارت جستجو، تحلیل، مقایسه، ارائه و یا آموزش. که همه مهارت های مهمی در حوزه کار هستند. در نهایت این بخش هم به تقویت اعتماد به نفس فرد کمک می کند.نقطه پایانی هر فازهر فاز یک due date دارد که انتظار می رود کارآموز تا زمان مشخص شده بتواند خروجی کارهای خود، سناریو پیاده سازی شده، جواب سوالات و نتیجه فعالیت عملی، را به منتور ارایه دهد. در این مرحله منتور کدها، سوالات و نتیجه فعالیت را به دقت بررسی می کند و بازخورد های مربوطه را در اختیار کارآموز قرار می دهد. بعد از یک یا چند پروسه رفت و برگشت و تایید همه بخش ها، کارآموز وارد فاز بعد می شود.موضوعات انتخابیتعداد موضوعات و تکنولوژی هایی که در حوزه اندروید (و بقیه حوزه ها) وجود دارد خیلی خیلی زیاد هستند. کارآموز قرار نیست همه موارد را یاد بگیرد (البته که افراد ٍexpert هم خیلی ها را بلد نیستند) پس باید با آن مفاهیم و تکنولوژی هایی آشنا شود  که در پروژه یا پروژه های شرکت مورد استفاده قرار می گیرد، مثلا rx java‌ بسیار library قوی ای است ولی اگر به هر دلیل در پروژه ها از آن استفاده نمی شود و قرار نیست استفاده بشود، لزومی ندارد در لیست موضوعات آموزشی باشد. من حتی سعی کردم دیزاین هم مطابق پروژه باشد، مثلا اگر ما در پروژه زیاد از bottom sheet‌ استفاده می کنیم قطعا این موضوع بخشی از طرح ها هست. انتخاب موضوع پروژه، دیزاین و تکنولوژی ها خیلی وابسته به شرکت و پروژه ها است.موقعیت های واقعیقطعا همیشه شرایط کاری باب میل ما نیست. گاهی UI ناقص است، گاهی API مشکل دارد. گاهی نیازمندی ها واضح نیست و گاهی ابر و باد و مه و خورشید و فلک به کار می افتند که پروژه به deadline نرسد. معمولا کارآموز ها این موارد را تجربه نمی کنند و برای اولین بار در پروژه واقعی با آن روبرو می شوند که ممکن است فشار مضاعفی بر آن ها وارد کند. برای همین خوب است که تعمدا اکثر این اتفاقات را در دوره بگنجانید. مثلا یکی از API ها را به گونه ای طراحی کنید که response مورد انتظار را بر نگرداند، بخشی از دیزان را جوری طراحی کنید که همه نیازمندی ها را پوشش ندهد یا اینکه یکی از deadline ها را فشرده تر در نظر بگیرید. این کار دو مزیت دارد. اول اینکه فرد شرایط و مشکلات واقعی را تجربه می کند و دوم اینکه ما در هر موقعیت عملکرد کارآموز را می توانیم ارزیابی کنیم و راجع به آن مشورت کنیم. به هیچ عنوان هدف ما از این کار مچ گیری یا اعمال فشار بر کارآموز نیست. برای مثال اگر کارآموز کار را با همان API نیمه کاره تمام کرد برایش توضیح می دهیم که API مشکل داشته است و ما انتظار داریم در این مواقع مشکل گزارش شود نه اینکه به هر شکلی کار انجام شود. یا اینکه متوجه می شویم که deadline خیلی به کارآموز استرس وارد می کند که می شود راجع به این قضیه مشورت کرد و تجربیات را انتقال داد.سخنی با منتوراولین صفت مهم برای منتور، داشتن روحیه یادگیری است. باید همیشه در ذهنمان باشد که کارآموز یک لیوان خالی نیست که بخواهیم آن را پر کنیم. باید همیشه آماده یادگیری باشیم. می توان هم نکات فنی جدید یاد گرفت و هم نکات اخلاقی، همه چیز به نگرش ما بر می گردد. اگر نگرشی غیر از این داشته باشیم احتمالا نگاهی از بالا به پایین به کارآموز خواهیم داشت که بسیار ناخوشایند و گونه ای از بی احترامی است. باید به یاد داشته باشیم که ما انسان بهتری نیستیم صرفا در یک حوزه یا تکنولوژی محدود و خاص، اطلاعات بیشتری داریم.یک منتور وظیفه اش آموزش دادن نیست، صرفا به علت تجربه بیشتری که دارد می تواند افراد تازه کارتر را در مسیر یادگیری همراهی کند. پس قرار نیست افراد را حمل کنیم، بنا بر همراهی است.دادن بازخوردهای دقیق و شفاف بسیار به رشد افراد کمک می کند. متاسفانه به علت شرایط فرهنگی، ما معمولا این کار را انجام نمی دهیم. سعی می کنیم اگر موردی هم به چشممان می خورد بیان نکنیم که نکند طرف مقابل ناراحت شود. یا اینکه از آن طرف بوم می افتیم و همه چیز را بدون هیچ مقدمه ای، بی پرده، بیان می کنیم. به نظر من هیچ کدام سازنده نیستند. بازخورد باید به شکل دقیق، صریح و با رعایت اصول اخلاقی بیان شود.نکته آخر اینکه من سعی می کردم تا جای ممکن به سوالات فنی جواب ندهم و به جای آن key word بدهم یا راهنمایی کنم که چه موضوعاتی باید جستجو شود. ما و کارآموز فکر می کنیم که جواب دادن به سوالات بسیار کار خوبی است و در جهت یادگیری است اما من فکر می کنم این افراد را وابسته می کند و انتظار دارند همیشه ما جوابی برای سوالاتشان داشته باشیم. من همیشه تلاشم بر این بوده تا جای ممکن سوالات فنی را جواب ندهم و به جای آن به فرد کمک کنم خودش جواب را پیدا کند چون بر این باورم این کار علاوه بر اینکه یادگیری بهتری برای فرد به همراه دارد، فرد را مستقل تر می کند. البته که من در تجربه خودم تا روز آخر نتوانستم چرایی جواب ندادنم را درست به کارآموز عزیز منتقل کنم که قطعا مشکل از شیوه بیان من بوده است. ولی حتما باید علت این کار را توضیح دهیم تا افراد بدانند این عدم پاسخگویی به علت بی تفاوتی ما نیست.سخنی با کارآموزبله قربان گو نباشید. اگر تسکی به شما داده می شود علت آن را جویا شوید. شما این حق را دارید چرایی انجام کار محول شده به خودتان را جویا شوید. اگر می بینید نیازمندی منطقی نیست، مطرح کنید. اگر پیشنهادی برای بهبود دیزاین دارید مطرح کنید. هر مشکل، نظر و یا انتقادی داشتید حتما مطرح کنید. همانطور که قبلا ذکرش رفت، زبان و شیوه بیان مهم است.تا جایی که می توانید سوال بپرسید. اول از همه سوالتان را از گوگل بپرسید. اگر کامل همه چیز را زیر و رو کردید و نتیجه ای حاصل نشد، از همکارانتان بپرسید. پیشنهاد من این است که به دنبال گرفتن جواب نهایی از افراد نباشید بلکه از افراد بخواهید به شما یک منبع خوب معرفی کنند یا اینکه key word های برای سرچ به شما معرفی کنند.در نهایت اینکه به کاری که می کنید متعهد باشید. اگر وظیفه ای به شما محول شده است آن را به طور کامل (تا حد توان) و به موقع انجام دهید و اگر به هر دلیل مشکلی در جهت رسیدن به هر کدام از این دو است آن را با منتور مطرح کنید که بتوانید با مشورت همدیگر مجددا برنامه ریزی کنید.سخن پایانیممنون که این مطلب را خواندید. امیدوارم حس تلف شدن وقت نداشته باشید. در آخر ذکر این نکته لازم است که قطعا همیشه یک سیستم نمی تواند همه جا و برای همه کس مناسب باشد. گاهی نیاز است تغییراتی در سیستم ایجاد کنیم ولی باید به سیستم اعتماد کنیم و اگر فردی در چهارچوب آن قرار نمی گیرد ممکن است مناسب سبک کاری شرکت ما نباشد. این به معنی به درد نخور بودن آن فرد نیست. صرفا نیازمندی های شرکت با ویژگی های فرد همسو نیست. همین و بس.</description>
                <category>شکیب حبیبی</category>
                <author>شکیب حبیبی</author>
                <pubDate>Sun, 06 Dec 2020 19:03:40 +0330</pubDate>
            </item>
                    <item>
                <title>کارِ بیشتر، متمرکز تر و شاداب تر با pomodoro</title>
                <link>https://virgool.io/@shkbhbb/pomodoro-technique-cztjwja3dfqp</link>
                <description>چطور می تونم ساعت بیشتری در طول روز کار کنم؟ چطور میزان تمرکزم و زیادتر کنم؟ چطور از خستگی کار کم کنم؟ این ها سوالاتی هستند که مدت هاست ذهن من رو درگیر کردند. معمولا با همه راجع به جواب های احتمالی مشورت می کنم یا راجع بهشون سرچ و مطالعه می کنم. مدتی قبل در یکی از جستجو ها به یک تکنیک بسیار ساده برخوردم. تکنیک pomodoro.در نگاه اول به نظرم تکنیک جالبی اومد و کمی بیشتر راجع به بازخورد های بقیه جستجو کردم . بازخورد ها هم به طرز عجیبی خوب بودند اما من این روش رو عملی نکردم چون به نظرم خیلی غیر بهینه می اومد.اصلا pomodoro چی هست؟۱- یک تسک رو مشخص می کنید (ترجیحا بشه تو زمان کم انجامش داد)۲- ۲۵ دقیقه به شکل متمرکز کار می کنید۳- ۵ دقیقه استراحت می کنید (مدت استراحت بعد از ۴ دور به جای ۵ دقیقه ۱۵ دقیقه هست)چیزی که اول کار کمی برای من غیر منطقی به نظر می اومد، مدت زیاد استراحت بود. چند مدت پیش یکی از اپیزود های پادکست Bplus را گوش می دادم که عنوانش Deep work بود (شنیدنش را حتما پیشنهاد می کنم) و بعد از اون تصمیم گرفتم هر چقدر هم که به نظرم غیر منطقی باشه این روش رو برای مدت کمی امتحان کنم. برای این کار هم یک اپ pomodoro  رو گوشیم نصب کردم.نتایج فراتر از حد انتظاراتم بود!- تونستم به عنوان فریلنسر چند روز پشت سر هم ۱۰ ساعت کار کنم.- میزان تمرکزم در حین کار بسیار بالا رفت.- از همه عجیب تر اینکه میزان خستگی من خیلی کمتر شد. بر خلاف قبل بعد از ۱۰ ساعت کار توان ذهنی و جسمی لازم رو برای ادامه کار رو داشتم.- استراحت ها باعث شد که چشمم استراحت بیشتری داشته باشد و فشار خیلی کمتری متحمل بشود.بر خلاف چیزی که در نگاه اول به نظر میرسه، خروجی این روش خیلی بیشتر روش های عادی هست که ما دولوپر ها پیش می گیریم. یک تسک رو شروع می کنیم و تا تموم نشه ول کن ماجرا نیستیم. این روش ها در کوتاه مدت و طولانی مدت در نهایت به ضرر ما خواهد بود.این روش قرار نیست برای همه جواب بده و یا اینکه معجره کنه. در نهایت رقم زننده همه تغییرات ما و میزان تعهدمون هستیم. برای مثال من در طول روز بالغ بر ۱۰۰۰۰۰۰۰۰۰ بار سایت ورزش۳ رو چک می کنم ولی متعهد شدم در طول ۲۵ دقیقه کار به هیچ وجه این کار رو نکنم. سرعت پاسخگویی من تو تلگرام از سرعت نور بیشتر بود ولی با خودم متعهد شدم در تایم تمرکز اصلا تلگرامم رو چک نکنم.چند کاری که من برای اثر بخشی بیشتر انجام دادم:- نوتیفیکیشن های گوشی و لپ تاپ رو خاموش کردم. فقط نوتیفیکیشن اسلک در هر دو روشن هست. - سعی می کنم در تایم استراحتم با لوازم الکترونیک کار نکنم و یا اخبار نخونم یا هر چیز دیگه ای که بتونه ذهن رو درگیر یا خسته کنه. - یه کول پد با قابلیت تنطیم ارتفاع و یک مانیتور گرفتم که به گردنو کمر و چشمم کمتر فشار بیاد.- در استراحت های طولانی (۱۵ دقیقه ای) سعی می کنم ورزش انجام بدم.من از این روش لذت بردم و به هر کس هم معرفی کردم خیلی راضی بوده. اصلا به همین دلیل تصمیم گرفتم اینجا بنویسم که تعداد افراد بیشتری بتونن باهاش آشنا بشن و امتحانش کنن ببینن به دردشون می خوره یا نه.اگر این روش رو امتحان کردید خوشحال میشم نتیجش یا ابتکاراتتون رو باهام به اشتراک بذارید. راستی اون زمان ۲۵،۵ و ۱۵ دقیقه لزوما نباید ثابت بمونن. بعد از مدتی که سیستم دستتون اومد می تونید شخصی سازیش کنید. فقط یادتون باشه یهو تایم کارتون رو نکنید ۱ ساعت که اون دیگه pomodoro نیست :)نکته آخر اینکه این روش لزومابرای کار نیست اون رو برای هر کاری که نیار به تمرکز داره مثل، درس خوندن، کتاب خوندن، ویدیو آموزشی دیدن و ... می تونید استفاده کنید.امیدوارم این مطلب براتون مفید بوده باشه.شکیب ایر د بست</description>
                <category>شکیب حبیبی</category>
                <author>شکیب حبیبی</author>
                <pubDate>Sat, 05 Oct 2019 17:41:01 +0330</pubDate>
            </item>
            </channel>
</rss>