<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>آموزش‌های تکمیلی - اندروید استودیو</title>
	<atom:link href="https://android-studio.ir/category/supplementary/feed/" rel="self" type="application/rss+xml" />
	<link>http://android-studio.ir/category/supplementary/</link>
	<description>آموزش برنامه نویسی اندروید</description>
	<lastBuildDate>Tue, 09 Feb 2021 10:07:40 +0000</lastBuildDate>
	<language>fa-IR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>۲۱ روش افزایش سرعت بیلد Gradle در اندروید استودیو</title>
		<link>https://android-studio.ir/speed-up-android-studio-gradle-build/</link>
					<comments>https://android-studio.ir/speed-up-android-studio-gradle-build/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Tue, 09 Feb 2021 10:07:40 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=195134</guid>

					<description><![CDATA[<p>احتمالا با مشاهده تصویر بالا متوجه شده‌اید که در این مبحث می‌خواهیم راجع به چه موضوعی صحبت کنیم. بسته به میزان حجم یک پروژه اندرویدی، فرایند بیلد شدن آن بین چند ثانیه تا چندین دقیقه می‌تواند زمان بر باشد. بنابراین اجرای روش‌های افزایش سرعت بیلد Gradle در اندروید استودیو می‌تواند تا چندین برابر، مدت زمان [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/speed-up-android-studio-gradle-build/">۲۱ روش افزایش سرعت بیلد Gradle در اندروید استودیو</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/speed_up_android_studio_gradle_build.png" alt="21 روش افزایش سرعت بیلد Gradle در اندروید استودیو" /></p>
<p>احتمالا با مشاهده تصویر بالا متوجه شده‌اید که در این مبحث می‌خواهیم راجع به چه موضوعی صحبت کنیم. بسته به میزان حجم یک پروژه اندرویدی، فرایند بیلد شدن آن بین چند ثانیه تا چندین دقیقه می‌تواند زمان بر باشد. بنابراین اجرای روش‌های افزایش سرعت بیلد Gradle در اندروید استودیو می‌تواند تا چندین برابر، مدت زمان این فرایند طولانی و خسته کننده را کاهش دهد.<br />
بهینه سازی صحیح اندروید استودیو و بیلد سیستم آن یعنی گریدل، می‌تواند یک فرآیند چند دقیقه‌ای بیلد را به چند ثانیه کاهش دهد! پس توصیه می‌کنم این قسمت از سری مباحث <a href="http://android-studio.ir/" target="_blank" rel="noopener">آموزش برنامه نویسی اندروید</a> را با دقت و تا انتها مطالعه کنید.</p>
<div class="title-box">
<b>آنچه در این آموزش می‌خوانید</b></p>
<ul>
<li><a href="#reasons">دلایل کاهش سرعت بیلد شدن پروژه اندرویدی</a></li>
<li><a href="#how-to-speed-up-android-studio-gradle-build">روش‌های افزایش سرعت بیلد Gradle در اندروید استودیو</a>
<ul>
<li><a href="#latest-gradle">استفاده از جدیدترین نسخه پلاگین Gradle</a></li>
<li><a href="#latest-java">استفاده از آخرین نسخه Java</a></li>
<li><a href="#offline-gradle">فعال کردن حالت آفلاین Gradle</a></li>
<li><a href="#migrate-to-linux">مهاجرت به لینوکس!</a></li>
<li><a href="#library-dynamic-version">عدم استفاده از ورژن‌های داینامیک کتابخانه‌ها</a></li>
<li><a href="#libraries">استفاده حداقلی از کتابخانه‌ها</a></li>
<li><a href="#apply-changes">اعمال تغییرات بجای اجرای دوباره هنگام تست و دیباگ</a></li>
<li><a href="#improve-gradle">انجام تغییرات در Gradle</a></li>
<li><a href="#disable-png-crunching">غیر فعال کردن PNG Crunching</a></li>
<li><a href="#avoid-legacy-multidex">جلوگیری از Legacy Multidex</a></li>
<li><a href="#avoid-compiling-unnecessary-resources">جلوگیری از کامپایل منابع غیر ضروری</a></li>
<li><a href="#dynamic-variables">عدم استفاده از مقادیر داینامیک</a></li>
<li><a href="#disable-crashlytics">غیر فعال کردن Crashlytics</a></li>
<li><a href="#disable-multi-apk">غیر فعال کردن Multi APK</a></li>
<li><a href="#preDexLibraries">جلوگیری از کامپایل مداوم کتابخانه‌ها</a></li>
<li><a href="#profile-android-project">ساخت پروفایل برای بیلد و بررسی آن</a></li>
<li><a href="#android-modular-project">طراحی ماژولار پروژه</a></li>
</ul>
</li>
</ul>
</div>
<h2 id="reasons">دلایل کاهش سرعت بیلد شدن پروژه اندرویدی</h2>
<p>به نام خدا. عموما وقتی صحبت از کندی سرعت اجرای یک نرم افزار به میان می‌آید در اولین قدم انگشت اتهام به سمت سخت افزار رایانه رفته و پایین بودن کانفیگ سخت افزاری سیستم به عنوان علت اصلی بروز مشکل بیان می‌شود.<br />
قطعا سخت افزار نقش تعیین کننده‌ای در سرعت اجرای ابزار و نرم افزارها به عهده دارد اما این همه‌ی ماجرا نیست. بهینه سازی ابزار مورد استفاده نیز می‌تواند تا حد زیادی و در مواقعی تا چند برابر سرعت انجام عملیات را افزایش دهد بدون آنکه نیازی به ارتقاء سخت افزار و صرف هزینه‌های گزاف وجود داشته باشد.</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/gradle_slow_build_speed_reasons.png" alt="دلایل کاهش سرعت بیلد شدن پروژه اندرویدی" /></p>
<p>حتی یک PC یا Laptop قدرتمند و با کانفیگ بالا هم از این قاعده مستثنی نبوده و در صورت عدم بهینه سازی ابزار مورد استفاده، هنگام رندر شدن یک پروژه گرافیکی سنگین یا بیلد شدن پروژه اندرویدی در اندروید استودیو با کاهش بازدهی و افزایش زمان مواجه خواهیم شد.<br />
بنابراین به عنوان یک برنامه نویس و توسعه دهنده اندروید اگر یک سیستم با مشخصات سخت افزاری بالا در اختیار داریم نباید این حس به ما القاء شود که نیازی به بهینه سازی نیست!<br />
در این جلسه سعی می‌کنم مهمترین مواردی که در افزایش سرعت بیلد Gradle در اندروید استودیو موثر هستند را معرفی کنم.</p>
<h2 id="how-to-speed-up-android-studio-gradle-build">روش‌های افزایش سرعت بیلد Gradle در اندروید استودیو</h2>
<p>هر کدام از مواردی که در ادامه ذکر می‌شود درصد متفاوتی در کاهش نهایی زمان بیلد شدن پروژه اندرویدی سهم خواهد داشت بنابراین اکتفا کردن به یک یا چند مورد، تاثیر حداکثری را به همراه نخواهد داشت.<br />
بنابراین درخواست می‌کنم تمامی آیتم‌ها را به دقت و با حوصله کافی مطالعه و اجرا کنید تا بتوانید حداکثر بهره وری را از سیستم خود کسب کرده و در نهایت، کمترین زمان را برای بیلد شدن پروژه‌های اندرویدی خود هدر دهید.</p>
<h3 id="latest-gradle">استفاده از جدیدترین نسخه پلاگین Gradle</h3>
<p>به مرور زمان نسخه‌های جدیدتری از پلاگین گریدل منتشر می‌شود که تعدادی از مشکلات و باگ‌ها رفع شده و نسبت به نسخه‌های قبل بهینه تر هستند که در نتیجه مقداری از زمان انتظار ما برای بیلد شدن پروژه‌های اندرویدی کاسته می‌شود.<br />
البته عمدتا این افزایش بازدهی به قدری ملموس نیست که بخواهد به یکباره زمان بیلد را ۵۰ درصد کاهش دهد اما بهرحال بی تاثیر هم نیست. ضمن اینکه با گذشت زمان و رشد قابلیت‌ها و امکانات ابزارهای هوشمند و بویژه <a href="http://android-studio.ir/introduction-android-operating-system/" target="_blank" rel="noopener">سیستم عامل اندروید</a>، حجم کتابخانه‌ها، پلاگین‌ها و کدهای پروژه اندرویدی نیز افزایش یافته و به نوعی این افزایش سرعت بیلد گریدل و حجم کدها باعث همپوشانی یکدیگر می‌شود.</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/gradle_incremental_compile_chart.png" alt="افزایش سرعت کامپایل با بروزرسانی Gradle" /><figcaption>افزایش سرعت بیلد در نسخه‌های جدید Gradle</figcaption></figure>
<p>بر اساس چارت فوق که در <a href="https://gradle.org/whats-new/gradle-5/" target="_blank" rel="noopener">مستندات وب سایت Gradle</a> منتشر شده، در کامپایل یک پروژه جاوایی که حاوی ۱۰۰۰ ماژول بوده، مدت زمان بیلد از حدود ۴ ثانیه در گریدل ۲٫۱۴ به ۲٫۵ ثانیه در گریدل ۵ کاهش یافته است. یعنی چیزی حدود ۳۰ درصد.<br />
برای بروزرسانی Gradle نیاز به انجام کار اضافی نیست. با <a href="http://android-studio.ir/نصب-اندروید-استودیو/" target="_blank" rel="noopener">نصب نسخه جدید اندروید استودیو</a> و یا بروزرسانی آن چنانچه نسخه جدیدی از گریدل و البته سازگار با آن نسخه از اندروید استودیو منتشر شده باشد به صورت خودکار دریافت و جایگزین نسخه قدیمی خواهد شد.<br />
اما چنانچه قصد دارید بدون بروزرسانی اندروید استودیو از آخرین نسخه گریدل استفاده کنید لازم است نسخه پلاگین در build.gradle (Project) در بلاک dependencies بروز شود:</p>
<pre class="brush: java; title: ; notranslate">
dependencies {
    classpath &quot;com.android.tools.build:gradle:4.1.1&quot;
}
</pre>
<p>در زمان تهیه این آموزش Gradle plugin 4.1.1 روی اندروید استودیو من فعال است که البته به من پیشنهاد می‌دهد نسخه ۴٫۱٫۲ را نصب کنم:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/android_studio_gradle_plugin_update.png" alt="بروزرسانی پلاگین گریدل در اندروید استودیو" /><figcaption>بروزرسانی پلاگین گریدل در اندروید استودیو</figcaption></figure>
<p>پلاگین را به ۴٫۱٫۲ ارتقاء داده و پروژه را Sync می‌کنم. بلافاصله اندروید استودیو شروع به دانلود گریدل ۶٫۵ می‌کند:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/android_studio_gradle_update.png" alt="بروزرسانی گریدل در اندروید استودیو" /><figcaption>بروزرسانی گریدل در اندروید استودیو</figcaption></figure>
<p>یعنی برای استفاده از نسخه جدید پلاگین گریدل، خود گریدل نیز باید ابتدا بروز شود. لینک فایل گریدل در gradle-wrapper.properties ذخیره می‌شود که در صورت نیاز می‌توان به صورت دستی هم آنرا تغییر داد:</p>
<p><span class="filename">gradle-wrapper.properties</span></p>
<pre class="brush: java; title: ; notranslate">
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
</pre>
<div class="alert alert-warning">
<span class="notice">تذکر:</span> اگر در حال مطالعه این آموزش هستید یعنی تجربه کار با اندروید استودیو و مشکلات مربوط به تحریم را دارید. چنانچه از ابزاری مانند FOD برای دور زدن تحریم و <a href="http://android-studio.ir/how-to-bypass-software-sanctions/" target="_blank" rel="noopener">فعال کردن پروکسی روی اندروید استودیو</a> استفاده می‌کنید به دلیل اینکه این سرویس‌ها صرفا آدرس‌های تحریم شده را از پروکسی خود عبور می‌دهند و در حال حاضر سرویس Gradle بر روی IP ایران تحریم نیست، لازم است قبل از شروع دانلود، پروکسی را غیر فعال کنید.<br />
البته شاید بعد از دریافت و نصب گریدل، اندروید استودیو بخواهد فایل‌های دیگری دریافت کند که مشمول تحریم بوده و لازم باشد مجدد پروکسی را فعال نمایید!
</div>
<p>برای کسب اطلاع دقیق از جزئیات مربوط به نسخه‌های گریدل و پلاگین‌های سازگار با آن به صفحه <a href="https://developer.android.com/studio/releases/gradle-plugin" target="_blank" rel="noopener">Android Gradle plugin</a> مراجعه کنید.</p>
<h3 id="latest-java">استفاده از آخرین نسخه Java</h3>
<p>در زمان تهیه این آموزش Java 8 آخرین نسخه از جاوا است. جاوا ۸ (با عنوان ۱٫۸) نسبت به نسخه‌های قبلی خود سرعت بیشتری دارد و اگر هنوز از جاوا ۱٫۶ یا ۱٫۷ استفاده می‌کنید حتما آن را به ۱٫۸ یا جدیدترین نسخه موجود ارتقا دهید.<br />
البته اگر به یاد داشته باشید در نسخه‌های ابتدایی محیط توسعه اندروید استودیو لازم بود JDK را به صورت دستی روی سیستم نصب کرده و سپس محل نصب را به آن معرفی کنیم. اما در نسخه‌های اخیر، تیم توسعه دهنده اندروید استودیو این زحمت را هم از روی دوش برنامه نویس‌های اندرویدی برداشته و یک نسخه از OpenJDK را درون IDE تعبیه کرده‌اند.<br />
بنابراین با بروزرسانی اندروید استودیو مطمئن هستیم که آخرین نسخه از JDK را استفاده می‌کنیم. با اینحال برای اطمینان از نصب آخرین نسخه جاوا کافیست بلاک compileOptions در build.gradle (Module:app) را بررسی کنیم:</p>
<pre class="brush: java; title: ; notranslate">
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
</pre>
<p>در کد فوق نسخه ۸ جاوا قید شده. همچنین در محل نصب JDK توسط Command line می‌توان با دستور java -version نسخه جاوای موجود را بررسی کرد.<br />
اگر اندروید استودیو را در مسیر پیش فرض نصب کرده‌اید محلی که می‌توان نسخه جاوا را چک کرد به صورت زیر است:</p>
<p style="text-align: left; direction: ltr;">
C:\Program Files\Android\Android Studio\jre\bin
</p>
<p>در سیستم عامل ویندوز در این پنجره روی صفحه shift و راست کلیک کرده و گزینه Open PowerShell window here یا Open Command line window here را انتخاب می‌کنم تا پنجره خط فرمان باز شود:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/use_latest_jdk_in_android_studio.png" alt="بررسی نسخه جاوای نصب شده روی سیستم" /><figcaption>بررسی نسخه جاوای نصب شده روی سیستم</figcaption></figure>
<p>ملاحظه می‌کنید با نوشتن دستور java -version نسخه ۱٫۸ نمایش داده شد.</p>
<h3 id="offline-gradle">فعال کردن حالت آفلاین Gradle</h3>
<p>یکی دیگر از گزینه‌های افزایش سرعت بیلد Gradle در اندروید استودیو آفلاین کردن گریدل است. چنانچه گریدل در حالت پیش فرض خود یعنی Online قرار داشته باشد ممکن است در حین فرایند بیلد پروژه دانلود تعدادی فایل را آغاز کند که همین امر موجب افزایش زمان بیلد می‌شود. بهتر است فقط زمانی گریدل را در حالت آنلاین قرار دهیم که می‌خواهیم یک کتابخانه جدید به پروژه اضافه کنیم که قبلا استفاده نشده و در کش گریدل موجود نیست.<br />
کتابخانه‌هایی که قبلا یکبار در همین پروژه یا پروژه‌ای دیگر استفاده شده و به صورت آنلاین دریافت شده‌اند تا زمانی که به صورت دستی حذف نشود در کش موجود بوده و بدون نیاز به اتصال مجدد قابل استفاده خواهد بود (مگر آنکه بخواهیم نسخه جدیدتر آن کتابخانه را نصب کنیم).<br />
برای اینکار کافیست در نوار سمت راست اندروید استودیو و یا منوی View > Tool windows گزینه Gradle و سپس Toggle Offline Mode را انتخاب کنید:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/enable_android_studio_gradle_offline_mode.png" alt="فعال کردن حالت آفلاین Gradle در اندروید استودیو" /><figcaption>فعال کردن حالت آفلاین Gradle در اندروید استودیو</figcaption></figure>
<h3 id="migrate-to-linux">مهاجرت به لینوکس!</h3>
<p>قبلا هم می‌دانستم اندروید استودیو در اجرا و همچنین بیلد شدن پروژه‌ها در توزیع‌های لینوکسی محبوب مانند Ubuntu سرعت بیشتری نسبت به ویندوز دارد. اما برای اطمینان بیشتر عبارت <span dir="ltr">&#8220;Does Android Studio run faster on Linux?&#8221;</span> را گوگل کردم تا تجربه سایر افراد را در این زمینه بدانم. تقریبا همه آنها از افزایش سرعت حتی تا %۵۰ بعد از مهاجرت به Ubuntu را اعلام کرده بودند. برای نمونه می‌توانید <a href="https://www.quora.com/Does-Android-Studio-run-faster-on-Ubuntu-or-Windows" target="_blank" rel="noopener">این بحث موجود در Quara</a> را بررسی کنید.<br />
البته برای کسی مثل من شاید مهاجرت از ویندوز به لینوکس سخت باشد اما بهرحال یکی از گزینه‌ها بود و لازم میدانستم به آن اشاره کنم.</p>
<h3 id="library-dynamic-version">عدم استفاده از ورژن‌های داینامیک کتابخانه‌ها</h3>
<p>اگر عادت کرده‌اید هنگام افزودن یک کتابخانه به <a href="http://android-studio.ir/create-android-project-and-its-structure/" target="_blank" rel="noopener">پروژه اندرویدی</a> خود برای سادگی کار بجای درج نسخه دقیق آن، یک &#8220;+&#8221; قرار دهید از امروز اشتباه خود را اصلاح کنید! داینامیک بودن نسخه کتابخانه‌ها باعث می‌شود تا گریدل هر ۲۴ ساعت یکبار به مخزن آنلاین متصل شده تا بررسی کند آیا نسخه جدیدتری منتشر شده یا نه.<br />
برای مثال در زمان نگارش این آموزش، ورژن ۲٫۹٫۰ آخرین نسخه از <a href="http://android-studio.ir/android-retrofit-library/" target="_blank" rel="noopener">کتابخانه Retrofit</a> است.</p>
<pre class="brush: java; title: ; notranslate">
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:+'
}
</pre>
<pre class="brush: java; title: ; notranslate">
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
}
</pre>
<p>در کدهای فوق، مورد اول اشتباه و مورد دوم صحیح است. بگذریم از اینکه در مواردی حتی کتابخانه‌های پیش فرض پروژه هم به همین صورت تعریف شده است:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/avoid_dependency_dynamic_version.png" alt="استفاده از ورژن‌های استاتیک برای کتابخانه‌ها" /><figcaption>استفاده از ورژن‌های استاتیک برای کتابخانه‌ها</figcaption></figure>
<h3 id="libraries">‌استفاده حداقلی از کتابخانه‌ها</h3>
<p>بالا رفتن تعداد کتابخانه‌های اضافه شده به پروژه می‌تواند سرعت بیلد را به همان نسبت کاهش دهد. اگر کتابخانه‌ای را قبلا اضافه کرده‌اید و الان به هر دلیل از استفاده از آن منصرف شده‌اید حتما نسبت به حذف آن از پروژه اقدام کنید.<br />
همچنین برخی کتابخانه‌ها ترکیبی از دو یا چند کتابخانه دیگر هستند. اگر فقط به یکی از زیر مجموعه‌های آن نیاز دارید فقط همان را اضافه کنید تا از اضافه شدن کتابخانه‌های بلا استفاده جلوگیری گردد.<br />
ضمن اینکه توجه داشته باشید یک کتابخانه فقط یکبار به پروژه اضافه شده باشد. برای مثال کتابخانه Retrofit Gson Converter خودش از کتابخانه Gson گوگل استفاده می‌کند بنابراین نیازی نیست در کنار آن، دوباره کتابخانه Gson را تعریف کنیم.<br />
با استفاده از دستور</p>
<pre class="brush: java; title: ; notranslate">
gradlew app:dependencies
</pre>
<p>در Terminal می‌توانید لیست کامل کتابخانه‌های بکار رفته در پروژه را مشاهده کنید:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/list_of_android_project_dependencies_&#038;_libraries.png" alt="‌استفاده حداقلی از کتابخانه‌ها در پروژه اندرویدی" /><figcaption>لیست کتابخانه‌های پروژه در Terminal</figcaption></figure>
<p>نحوه کار با ترمینال در ادامه همین آموزش (ساخت پروفایل برای بیلد و بررسی آن) توضیح داده شده است.</p>
<h3 id="apply-changes">اعمال تغییرات بجای اجرای دوباره هنگام تست و دیباگ</h3>
<p>اگر پروژه روی <a href="http://android-studio.ir/install-genymotion/" target="_blank" rel="noopener">شبیه ساز اندرویدی</a> یا یک دیوایس حقیقی در حال اجرا (Run) است و می‌خواهیم تغییری در کدها اعمال کنیم، بهتر است بجای Run کردن دوباره‌ی پروژه از گزینه Apply Changes استفاده کنیم. با اجرای مجدد پروژه، اپلیکیشن به طور کامل ریستارت شده و به عبارتی یک APK جدید ساخته می‌شود که جایگزین APK ای که در اجرای قبلی روی دیوایس نصب شده خواهد شد. این یعنی اتلاف زمان و منابع.<br />
در صورتی که گزینه Apply Changes صرفا بخشی از برنامه که کدهای آن ویرایش شده را به دیوایسی که پروژه روی آن در حال اجراست ارسال می‌کند و یک APK جدید جایگزین نخواهد شد.</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/using_apply_changes_instead_of_run.png" alt="استفاده از Apply Changes بجای Run" /><figcaption>استفاده از Apply Changes بجای Run</figcaption></figure>
<p>قابلیت Apply Changes در اندروید استودیو ۳٫۵ معرفی و جایگزین Instant Run شد که در نسخه ۲ اضافه شده بود. بر اساس مستندات توسعه دهندگان اندروید استودیو، برخلاف Instant Run که تغییرات انجام شده در پروژه را از طریق بازنویسی بایت کد APK روی دیوایس اعمال می‌کرد، در Apply Changes کلاس‌های موردنظر دوباره تعریف می‌شوند که فرایند بهینه‌تر و کوتاهتری حاصل می‌شود.<br />
البته استفاده از این قابلیت صرفا بر روی دیوایس‌های با Android 8 (API 26) و به بالا قابل انجام است. ضمن اینکه اعمال برخی تغییرات در پروژه‌در در حال اجرا صرفا با ریستارت شدن یا به عبارتی Run کردن دوباره پروژه انجام خواهد شد و Apply Changes کاربردی نخواهد داشت مانند حذف یا اضافه کردن یک متد، فیلد یا کلاس.<br />
توضیحات تکمیلی را در صفحه <a href="https://developer.android.com/studio/run#apply-changes-limitations" target="_blank" rel="noopener">Build and Run</a> مستندات اندروید مطالعه کنید.</p>
<h3 id="improve-gradle">انجام تغییرات در Gradle</h3>
<p>با فعال کردن تعدادی از قابلیت‌های گریدل می‌توان باز هم افزایش سرعت بیلد Gradle در اندروید استودیو را شاهد باشیم. این کدها در فایل gradle.properties اضافه می‌شود:</p>
<p><b>Gradle Daemon:</b> این قابلیت یک فرایند پس زمینه‌ای است و فعال کردن آن باعث می‌شود برای عملیات بیلد پروژه، گریدل حافظه بیشتری در اختیار داشته باشد که به کاهش زمان انجام عملیات منجر خواهد شد.</p>
<pre class="brush: java; title: ; notranslate">
org.gradle.deamon=true
 </pre>
<p>اگر مایلید در این زمینه اطلاعات کاملتری کسب کنید صفحه <a href="https://docs.gradle.org/current/userguide/gradle_daemon.html" target="_blank" rel="noopener">Gradle Daemon</a> را مطالعه کنید.</p>
<p><b>Parallel Build Execution:</b> به طور پیش فرض گریدل تنها یک کار را در آن واحد انجام می‌دهد اما با اضافه کردن خط زیر، گریدل چندین عملیات را همزمان و به طور موازی اجرا خواهد کرد.</p>
<pre class="brush: java; title: ; notranslate">
org.gradle.parallel=true
</pre>
<p>البته این قابلیت تنها هنگامی موثر است که پروژه ماژولار ساخته شده باشد. آشنایی بیشتر با این قابلیت در <a href="https://docs.gradle.org/nightly/userguide/performance.html#parallel_execution" target="_blank" rel="noopener">این صفحه</a>.</p>
<p><b>Configure On Demand:</b> احتمال اینکه این قابلیت مورد استفاده ما قرار گیرد بسیار کم است زیرا صرفا در پروژه‌ای که اصطلاحا Multi-project نامیده می‌شود کاربرد خواهد داشت. پروژه‌ای که خودش دارای چند زیرشاخه مانند mobile و tv باشد.<br />
به بیان ساده تر، هنگامی که بخواهیم این Multi-project را روی یک دستگاه موبایل اجرا کنیم، فقط همین قسمت بیلد شده و قسمت مربوط به tv بیلد نخواهد شد.</p>
<pre class="brush: java; title: ; notranslate">
org.gradle.configureondemand=true
</pre>
<p>اطلاعات بیشتر در این زمینه: <a href="https://docs.gradle.org/current/userguide/multi_project_configuration_and_execution.html#sec:configuration_on_demand" target="_blank" rel="noopener">اینجا</a></p>
<p><b>تخصیص حافظه بیشتر به کامپایلر جاوا:</b> می‌توانیم مقدار حافظه در دسترس برای کامپایلر را افزایش دهیم تا فرایند کامپایل با سرعت بالاتری انجام شود. برای مثال در خط زیر مقدار ۲ گیگابایت (۲۰۴۸ مگابایت) تعیین شده است. </p>
<pre class="brush: java; title: ; notranslate">
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
</pre>
<p>بر اساس مقدار حافظه سیستم می‌توان عدد بزرگتری نیز در نظر گرفت.</p>
<p><b>فعال کردن Gradle build cache:</b> با فعال شدن قابلیت کش، گریدل از خروجی بیلدهای قبلی پروژه استفاده می‌کند که می‌تواند تا حدود ۳ برابر برای یک بیلد کامل (Full build) و ۱۰ برابر برای یک اعمال تغییر کوچک در پروژه (Incremental build) افزایش سرعت و کاهش زمان بیلد را به همراه داشته باشد.</p>
<pre class="brush: java; title: ; notranslate">
org.gradle.caching=true
</pre>
<div class="alert alert-warning">
<span class="notice">نکته:</span> علاوه بر کش بیلد گریدل، خود اندروید نیز یک سیستم کش دارد که تا قبل از انتشار اندروید استودیو ۲٫۳ توسط خط android.enableBuildCache=true فعال می‌شد. اما از این نسخه به بعد این قابلیت به صورت پیش فرض فعال بوده و نیازی به اضافه کردن آن نیست.
</div>
<h3 id="disable-png-crunching">غیر فعال کردن PNG Crunching</h3>
<p>هنگام بیلد شدن پروژه برای کاهش حجم فایل اپلیکیشن چنانچه تصاویری با فرمت PNG در منابع وجود داشته باشد فشرده خواهند شد که این فرایند به مدت زمان بیلد می‌افزاید. مسلما این قابلیت برای نسخه release نهایی که قصد انتشار آن را داریم یک مزیت محسوب می‌شود اما در هنگام تست و عیب یابی پروژه طبیعتا کاهش حجم اپ اهمیتی برای ما نخواهد داشت. لذا با غیر فعال کردن آن تا حدودی از زمان بیلد کاسته خواهد شد.<br />
البته از اندروید استودیو ۳ به بعد این قابلیت به طور پیش فرض برای خروجی debug غیر فعال بوده و صرفا برای نسخه release انجام این کار لازم است. در بلاک release داخل بلاک BuildTypes فایل build.gradle(app) برای ویژگی crunchPngs مقدار false تعریف می‌کنم. اگر به یاد داشته باشید تنظیمات مربوط به <a href="http://android-studio.ir/android-app-source-protect-and-optimize-using-proguard-r8/" target="_blank" rel="noopener">فعال کردن پروگارد (R8)</a> نیز در همین بلاک قرار داشت.</p>
<pre class="brush: java; title: ; notranslate">
buildTypes {

    release {

        crunchPngs false

    }
}
</pre>
<p>البته شاید بهتر باشد بجای استفاده از تصاویر PNG از همان ابتدا از فرمت جدید تصاویر با نام <a href="https://developers.google.com/speed/webp/" target="_blank" rel="noopener">WebP</a> استفاده کنیم تا اولا نیازی به غیر فعال کردن موقت crunchPngs و فعال کردن مجدد آن هنگام release نهایی نداشته باشیم و دوما در همه حالت‌ها چه debug و چه release تست و نهایی، حجم برنامه ما در کمترین حالت ممکن از نظر resource ها قرار بگیرد.<br />
WebP فرمتی است که عمدتا در وب در حال فراگیر شدن بوده و جایگزینی آن با فرمت PNG به مرور در حال افزایش است. اگر با واژه سئو (SEO) آشنا هستید، یکی از آیتم‌های مهم در بهبود وضعیت سئو یک وب سایت، کاهش حجم صفحات و بخصوص تصاویر درون آن به شمار می‌رود.<br />
با اینحال چنانچه به هر دلیلی استفاده از WebP برایتان مقدور نیست، غیر فعال کردن crunchPngs به عنوان راهکار جایگزین در دسترس قرار دارد.</p>
<h3 id="avoid-legacy-multidex">جلوگیری از Legacy Multidex</h3>
<p>قبل از پرداختن به این مورد خلاصه بگویم؛ اگر برای تست و <a href="http://android-studio.ir/run-debug-android-app-on-hardware-device/" target="_blank" rel="noopener">دیباگ پروژه اندرویدی</a> خود صرفا از دیوایس‌های مجازی یا حقیقی با API 21 (Android 5.0) و به بالا استفاده می‌کنید مشکلی وجود ندارد و بدون مطالعه این قسمت از آن عبور کنید.<br />
قبلا در مبحث آشنایی با سیستم عامل اندروید گفتیم که از نسخه ۵٫۰ اندروید، ران تایم ART جایگزین Dalvik شد. محدودیتی که در Dalvik وجود دارد این است که در یک فایل DEX (که بعد از کامپایل پروژه درون APK ساخته می‌شود) حداکثر ۶۴k یا به عبارتی ۶۵۵۳۶ متد می‌تواند ارجاع داده شود.<br />
لذا چنانچه در حال توسعه یک پروژه با مقیاس بزرگ هستیم و مجموع تعداد متدهای تعریف شده در پروژه (شامل کدها، کتابخانه‌ها و&#8230;) بیشتر از این عدد باشد و minSdkVersion هم روی API 20 و یا پایینتر تنظیم شده باشد، لازم است کتابخانه Multidex روی پروژه نصب و فعال شود تا متدها در دو یا چند فایل DEX قرار گیرد.<br />
این آموزش جایی برای پرداختن به نحوه فعالسازی این کتابخانه نیست و چنانچه یکی از ارورهای </p>
<pre class="brush: java; title: ; notranslate">
trouble writing output:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.
</pre>
<p>و یا </p>
<pre class="brush: java; title: ; notranslate">
Conversion to Dalvik format failed:
Unable to execute dex: method ID not in &#x5B;0, 0xffff]: 65536
</pre>
<p>گرفته‌اید صفحه <a href="http://developer.android.com/studio/build/multidex" target="_blank" rel="noopener">فعالسازی Multidex</a> را در مستندات اندروید مطالعه کنید.<br />
بهتر است قبل از فعالسازی Multidex یکبار پروژه را بازنگری کنید. اگر کد یا کتابخانه اضافی هست که امکان حذف آن وجود دارد و با اینکار، تعداد متدها از محدودیت اعلام شده کمتر خواهد شد، نسبت به استفاده از Multidex در اولویت خواهد بود.<br />
فعال بودن این قابلیت نه تنها باعث افزایش سرعت بیلد Gradle در اندروید استودیو نمی‌شود بلکه با کاهش سرعت و افزایش زمان بیلد مواجه خواهیم شد. لذا یا باید minSdkVersion را روی API 21 و به بالا تنظیم کرد و یا اینکه پروژه را روی دیوایس‌های Android 5.0 به بالا اجرا و تست کنیم تا Multidex دخالتی در فرایند بیلد شدن پروژه نداشته باشد. ضمن اینکه نباید از اندروید استودیو نسخه ۲٫۳ و پایینتر از آن استفاده کرد.</p>
<h3 id="avoid-compiling-unnecessary-resources">جلوگیری از کامپایل منابع غیر ضروری</h3>
<p>اگر برنامه‌ای که در حال توسعه آن هستید شامل چند زبان مانند فارسی و انگلیسی بوده و یا برای چندین اندازه صفحه طراحی شده، هنگام بیلد شدن پروژه تمامی این resource ها کامپایل می‌شود. در صورتی که هنگام تست و دیباگ ما فقط به یک زبان و یک اندازه تراکم صفحه نیاز داریم.<br />
بنابراین با محدود کردن این موارد در هنگام تست و دیباگ پروژه می‌توانیم از اتلاف زمان برای کامپایل شدن منابع اضافی و غیر ضرور اجتناب کنیم.<br />
یک بلاک با نام productFlavors در بلاک android در build.gradle(app) اضافه می‌کنم. تنظیماتی که مدنظر دارم فقط باید در هنگام تست پروژه اجرا شود و نه در زمان <a href="http://android-studio.ir/generating-signed-apk/" target="_blank" rel="noopener">گرفتن خروجی APK یا AAB</a> برای انتشار. بنابراین یک بلاک جدید با نام dev در productFlavors تعریف کرده و تنظیمات موردنظرم را داخل آن اضافه می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
android {

    ...

    productFlavors {

        dev {

            resConfigs &quot;fa&quot;, &quot;xhdpi&quot;

        }

    }

}
</pre>
<p>در مثال فوق هنگام Run شدن پروژه و یا گرفتن نسخه debug فقط زبان فارسی و اندازه صفحه (تراکم صفحه) xhdpi کامپایل شده و مابقی موارد چشم پوشی خواهد شد.</p>
<h3 id="dynamic-variables">عدم استفاده از مقادیر داینامیک</h3>
<p>اگر در مواردی مانند تعیین versionCode در پروژه اندرویدی خود از مقادیر داینامیک بجای استاتیک استفاده می‌کنید، در عین حال اینکه از انجام یک کار روتین راحت می‌شوید اما باید بدانید که به مدت زمان بیلد پروژه افزوده خواهد شد.</p>
<pre class="brush: java; title: ; notranslate">
def buildTime = new Date().format('yyMMddHHmm').toInteger()

android {
    
    defaultConfig {
        
        versionCode buildTime

    }

}
</pre>
<p>در کد فوق از کلاس Date جاوا برای تعیین versionCode استفاده شده. بهتر است از انجام این کار اجتناب کرده و کد را به صورت دستی تغییر دهید.</p>
<h3 id="disable-crashlytics">غیر فعال کردن Crashlytics</h3>
<p>اگر برای دریافت گزارشات کرش برنامه خود از ابزار Crashlytics استفاده می‌کنید اما نیازی نمی‌بینید هنگام دیباگ فعال باشد بهتر است این پلاگین در نسخه‌های debug غیر فعال شود تا از تاثیر منفی آن در مدت زمان بیلد شدن پروژه جلوگیری گردد.<br />
برای اینکار خط زیر را در بلاک debug می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
android {
    ...

    buildTypes {

        debug {

            ext.enableCrashlytics = false

        }

    }

}
</pre>
<p>اما اگر نیاز داریم این پلاگین در هنگام دیباگ هم فعال باشد باز هم با جلوگیری از بروز شدن Build id پلاگین Crashlytics در هر بیلد، می‌توان تا حدودی از زمان بیلد را کاهش داد:</p>
<pre class="brush: java; title: ; notranslate">
android {
    ...

    buildTypes {

        debug {

            ext. alwaysUpdateBuildId = false

        }

    }

}
</pre>
<h3 id="disable-multi-apk">غیر فعال کردن Multi APK</h3>
<p>اگر از قابلیت Multi APK برای ساخت چندین نسخه از اپلیکیشن برای دیوایس‌های با مشخصات سخت افزاری متفاوت استفاده می‌کنید، قطعا در هنگام تست و دیباگ نیازی به این قابلیت نیست و بهتر است غیر فعال شود.<br />
کافیست در بلاک debug دو خط زیر تعریف شود تا برای همه سایزهای صفحه نمایش (density) و همچنین معماری‌های CPU (abi) فقط یک APK ساخته شود:</p>
<pre class="brush: java; title: ; notranslate">
buildTypes {
    ...
    debug {
        splits.abi.enable = false
        splits.density.enable = false
    } 
}
</pre>
<h3 id="preDexLibraries">جلوگیری از کامپایل مداوم کتابخانه‌ها</h3>
<p>طبیعتا در حین توسعه یک اپلیکیشن بارها و بارها نیاز به اصلاح و تغییر کدها و تست مجدد آن روی دیوایس اندرویدی داریم. اما کتابخانه‌هایی که در پروژه استفاده شده بدون تغییر هستند و واقعا نیازی نیست با هر بار بیلد شدن پروژه، کتابخانه‌های داخل پروژه نیز دوباره بیلد شده و درون فایل DEX در کنار دیگر کدهای کامپایل شده قرار گیرد.<br />
با افزودن بلاک dexOptions به بلاک android در build.gradle(app) و تعیین مقدار true برای ویژگی preDexLibraries از این پس کتابخانه‌ها فقط یکبار کامپایل شده و در دفعات بعد فقط کدهای پروژه مجدد کامپایل خواهند شد.</p>
<pre class="brush: java; title: ; notranslate">
android {

  ...    

    dexOptions { 

        preDexLibraries true

    }

}
</pre>
<p>البته دقت داشته باشید این قابلیت فقط در تغییرات جزئی (Apply Changes) موجب افزایش سرعت بیلد Gradle در اندروید استودیو خواهد شد ولی در بیلدهای کامل می‌تواند باعث کاهش سرعت بیلد گریدل شود. بنابراین بهتر است هنگام بیلدهای کامل، این قابلیت غیر فعال (false) باشد.</p>
<h3 id="profile-android-project">ساخت پروفایل برای بیلد و بررسی آن</h3>
<p>یکی از امکانات دیگری که Gradle در اختیار توسعه دهندگان قرار داده، ساخت Profile برای هر بیلد است. با فعال کردن پروفایل، بعد از هر خروجی APK ای که از پروژه می‌گیریم، یک گزارش جامع و کامل پیرامون مراحل بیلد و اینکه هر کدام چه مدت زمانی از فرایند کامل بیلد را به خود اختصاص داده در قالب یک فایل HTML در مسیر Project Directory > build > reports > profile ذخیره می‌شود.<br />
برای فعال کردن profile دو راه وجود دارد:<br />
<b>۱٫ تعریف دستور <span dir="ltr">&#8211; -profile</span> در تنظیمات کامپایلر اندروید استودیو:</b> کافیست یکبار دستور <span dir="ltr"><code>--profile</code></span> را در قسمت Command-line Options در مسیر Settings > Build, Execution, Deployment > Compiler تعریف کنیم:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/add_--profile_command_to_command-line_options.png" alt="ساخت پروفایل برای بیلد و بررسی آن" /><figcaption>اضافه کردن دستور <span dir="ltr">&#8211;profile</span> به تنظیمات کامپایلر اندروید استودیو</figcaption></figure>
<p>حالا با هربار اجرای پروژه روی شبیه ساز یا دیوایس واقعی و یا گرفتن APK نسخه debug یا release یک پروفایل با پسوند html در پوشه profile ذخیره خواهد شد:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/android_studio_gradle_build_profile_reports.png" alt="لیست گزارش‌های پروفایل بیلد گریدل" /><figcaption>لیست گزارش‌های پروفایل بیلد گریدل</figcaption></figure>
<p>با باز کردن هر فایل در مرورگر، تمامی جزئیات بیلد در ۵ دسته بندی متفاوت نمایش داده می‌شود:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/android_studio_gradle_build_profile_report.png" alt="پروفایل گزارش بیلد گریدل" /><figcaption>پروفایل گزارش بیلد گریدل</figcaption></figure>
<p>در هر قسمت، عدد زیر Duration زمانِ مربوط به یک بخش از فرایند بیلد را نشان می‌دهد. با بررسی همه قسمت‌ها شامل Summary، Configuration، Dependency Resolution، Artifact Transforms و Task Execution می‌توانیم تشخیص دهیم چه مواردی بیشترین زمان بیلد را به خود اختصاص می‌دهد و کدامیک را می‌توان بهینه و یا کاملا حذف کرد.<br />
<b>۲٫ اضافه کردن <span dir="ltr">&#8211; -profile</span> به دستورات در خط فرمان:</b> اگر عادت به استفاده از دستورات دارید، برای اضافه کردن پروفایل به بیلد باید <span dir="ltr"><code>--profile</code></span> به انتهای دستور اضافه شود:</p>
<pre class="brush: java; title: ; notranslate">
gradlew assembleDebug --profile
</pre>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/speed/add_--profile_to_gradlew_command.png" alt="اضافه کردن profile به دستور دیباگ در ترمینال" /><figcaption>اضافه کردن profile به دستور دیباگ در ترمینال</figcaption></figure>
<p>ملاحظه می‌کنید پس از اجرای فرمان در Terminal اندروید استودیو، بیلد پروژه انجام شده و در انتهای آن لینک فایل html پروفایل هم نمایش داده شده است.</p>
<div class="alert alert-warning">
<span class="notice">نکته:</span> اگر تمایل دارید با ترمینال آشنا شوید و گزینه Terminal در نوار پایین اندروید استودیو فعال نبود، در منوی View > Tool Windows آنرا پیدا کنید. همچنین اگر بجای ترمینال اندروید استودیو از Command-line ویندوز استفاده می‌کنید، پس از بار کردن آن در پوشه پروژه، دستور را به این صورت وارد کنید:</p>
<pre class="brush: java; title: ; notranslate">
./gradlew assembleDebug --profile
</pre>
</div>
<h3 id="android-modular-project">طراحی ماژولار پروژه</h3>
<p>Modular که در فارسی ماژولار نامیده می‌شود بیانگر سبکی از طراحی و ساخت است که یک پروژه بزرگ از ترکیب چندین واحد کوچکتر تشکیل می‌شود که هر واحد را یک ماژول می‌نامیم.<br />
تقسیم بندی برنامه به چند واحد (ماژول) علاوه بر مزایایی مانند توسعه و عیب یابی و بروزرسانی ساده تر و سریع تر و پیچیدگی کمتر، باعث می‌شود تا در فرایند تست و عیب یابی اپ، بیلد سیستم Gradle تنها ماژولی را کامپایل کند که ویرایش و اصلاح شده و مابقی ماژول‌هایی که ویرایش نشده‌اند برای بیلدهای بعدی کش شود که می‌توان کاهش زمان بیلد گریدل را به مقدار زیادی در بیلدهای آتی شاهد بود.<br />
با توجه به مفصل بودن طراحی ماژولار، به همین توضیحات مختصر اکتفا می‌کنم. اگر مایل هستید در مورد آن بیشتر مطالعه کنید از گوگل کمک بگیرید یا مقاله <a href="https://medium.com/google-developer-experts/modularizing-android-applications-9e2d18f244a0" target="_blank" rel="noopener">Modularizing Android Applications</a> را مطالعه کنید.<br />
در این آموزش ۲۱ عامل و روش افزایش سرعت و کاهش زمان بیلد Gradle در اندروید استودیو را بررسی کردیم. امیدوارم مورد مهم دیگری از قلم نیفتاده باشد. چنانچه یک یا چند روش دیگر را تجربه و یا در جایی دیگر مطالعه کرده‌اید، در قسمت دیدگاه‌های همین مبحث در وب سایت اطلاع دهید تا در بروزرسانی بعدی اضافه شود.<br />
موفق و پیروز باشید.</p>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://androidstudio.googleblog.com/2017/06/android-studio-30-canary-5-is-now.html" target="_blank" rel="noopener">https://androidstudio.googleblog.com/2017/06/android-studio-30-canary-5-is-now.html</a><br />
<a href="https://developer.android.com/studio/build/optimize-your-build" target="_blank" rel="noopener">https://developer.android.com/studio/build/optimize-your-build</a>
</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۲۱<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۱ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/SpeedUp_Gradle_Build.zip" class="button green edd-submit">دانلود رایگان با حجم ۱ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/SpeedUp_Gradle_Build.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/speed-up-android-studio-gradle-build/">۲۱ روش افزایش سرعت بیلد Gradle در اندروید استودیو</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/speed-up-android-studio-gradle-build/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>فعال کردن امکان نصب برنامه روی کارت حافظه</title>
		<link>https://android-studio.ir/enable-android-app-install-on-sd-card/</link>
					<comments>https://android-studio.ir/enable-android-app-install-on-sd-card/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Wed, 13 Jan 2021 17:46:21 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=194561</guid>

					<description><![CDATA[<p>حتما می‌دانید در سیستم عامل اندروید، اپلیکیشن‌ها به صورت پیش فرض روی حافظه داخلی دستگاه نصب می‌شوند. ممکن است برخی از کاربران بخصوص آنهایی که دستگاه با سخت افزار ضعیف و حافظه داخلی پایین در اختیار دارند، تمایل داشته باشند برخی از برنامه‌ها را روی کارت حافظه یا همان حافظه خارجی دستگاه خود نصب کنند. [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/enable-android-app-install-on-sd-card/">فعال کردن امکان نصب برنامه روی کارت حافظه</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/sd/enable_install_on_sd_card_for_our_android_app.png" alt="آموزش فعال کردن امکان نصب برنامه روی کارت حافظه" /></p>
<p>حتما می‌دانید در سیستم عامل اندروید، اپلیکیشن‌ها به صورت پیش فرض روی حافظه داخلی دستگاه نصب می‌شوند. ممکن است برخی از کاربران بخصوص آنهایی که دستگاه با سخت افزار ضعیف و حافظه داخلی پایین در اختیار دارند، تمایل داشته باشند برخی از برنامه‌ها را روی کارت حافظه یا همان حافظه خارجی دستگاه خود نصب کنند. بنابراین فعال کردن امکان نصب برنامه اندرویدی روی کارت حافظه ممکن است عاملی باشد برای جذب بیشتر مخاطب برای اپلیکیشن ما.<br />
در این آموزش به نحوه فعال کردن قابلیت نصب اپلیکیشن بر روی حافظه خارجی دستگاه‌های اندرویدی می‌پردازیم.</p>
<div class="title-box">
<b>آنچه در این آموزش می‌خوانید</b></p>
<ul>
<li><a href="#why-install-app-on-external-storage">در چه مواردی نصب برنامه روی کارت حافظه ضرورت دارد؟</a></li>
<li><a href="#define-app-install-location">تعیین محل نصب اپلیکیشن در سیستم عامل اندروید</a>
<ul>
<li><a href="#when-shouldnt-install-app-on-sd-card">در چه مواردی نباید برنامه روی حافظه خارجی نصب شود؟</a></li>
</ul>
</li>
</ul>
</div>
<h2 id="why-install-app-on-external-storage">در چه مواردی نصب برنامه روی کارت حافظه ضرورت دارد؟</h2>
<p>به نام خدا. در حال حاضر شاید به سختی بتوان یک موبایل یا تبلت اندرویدی را در بازار پیدا کرد که حافظه داخلی یا به عبارتی RAM آن کمتر از ۱۶ گیگابایت باشد. هنوز یادم نرفته که اولین گوشی اندرویدی من فقط ۴ گیگابایت حافظه داخلی داشت و دائما با مشکل کمبود حافظه برای نصب برنامه‌ها مواجه بودم.<br />
هرچند با بالا رفتن ظرفیت حافظه داخلی دیوایس‌ها نیاز کمتری به فعال کردن امکان نصب برنامه اندرویدی روی کارت حافظه احساس می‌شود با این حال هنوز هم دیوایس‌های قدیمی با حافظه کمتر از ۸ گیگابایت در دست افراد دیده می‌شود و فعال بودن این قابلیت روی برنامه ما می‌تواند نقش بیشتری در مدیریت حافظه دستگاه این دسته از افراد داشته باشد.<br />
بخصوص در برنامه‌های سنگین و مهمتر از همه در بازی‌ها که معمولا حجم بالاتری را اشغال می‌کنند، این قابلیت بیش از پیش می‌تواند برای ما به عنوان توسعه دهنده و برنامه نویس اندرویدی در دستور کار قرار گیرد.<br />
در ادامه مبحث به نحوه فعالسازی و همچنین جزئیات این قابلیت می‌پردازیم.</p>
<h2 id="define-app-install-location">تعیین محل نصب اپلیکیشن در سیستم عامل اندروید</h2>
<p>توسعه دهندگان <a href="http://android-studio.ir/introduction-android-operating-system/" target="_blank" rel="noopener">سیستم عامل اندروید</a> با انتشار اندروید ۲٫۲ (API 8) قابلیت جدیدی را در اختیار برنامه نویسان قرار دادند که به سادگی می‌توان تنظیمات مربوط به محل نصب برنامه را درون مانیفست تعیین کرد. با توجه به اینکه زمان زیادی از معرفی اندروید ۲٫۲ گذشته و بعید است دستگاهی با اندروید پایینتر از ۴ فعال باشد، از بابت سازگاری این قابلیت با دستگاه‌های موجود در اختیار کاربران جای نگرانی نیست.<br />
برای اینکار کافیست ویژگی android:installLocation را به همراه یک مقدار، درون تگ <manifest> فایل AndroidManifest.xml پروژه اضافه کنیم:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/sd/add_installLocation_attribute_to_android_project_manifest.png" alt="تعریف ویژگی installLocation در مانیفست" /><figcaption>تعریف ویژگی installLocation در مانیفست</figcaption></figure>
<p><span class="filename">AndroidManifest.xml</span></p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    package=&quot;ir.android_studio.sdcard&quot;
    android:installLocation=&quot;auto&quot;&gt;

    . . .

&lt;/manifest&gt;
</pre>
<p>همانطور که در تصویر مشاهده می‌کنید برای این ویژگی سه مقدار مشخص شده که بسته به نیازی که داریم می‌توان یک مورد را انتخاب کرد. کاربرد هریک از این ۳ آیتم به شرح زیر است:<br />
<b>Auto:</b> چنانچه برای ویژگی installLocation مقدار auto را انتخاب کنیم، برنامه اندرویدی ما ممکن است روی حافظه خارجی (کارت SD) نصب شود اما تضمینی وجود نداشته و سیستم عامل برای انتخاب محل مناسب برای نصب برنامه، ملاک‌های متعددی را بررسی می‌کند. یکی از این آیتم‌ها، میزان فضای باقیمانده از حافظه داخلی است. یعنی چنانچه حافظه داخلی پر شده و کارت حافظه هم روی دستگاه موجود باشد، به احتمال زیاد برنامه روی حافظه خارجی نصب خواهد شد.<br />
همچنین بعد از نصب برنامه، کاربر می‌تواند در قسمت مدیریت برنامه‌ها در سیستم عامل خود، محل قرارگیری اپلیکیشن را از حافظه داخلی به خارجی و یا بلعکس تغییر دهد. اگر برنامه روی حافظه داخلی نصب شده باشد، کاربر می‌تواند با استفاده از گزینه Move to SD Card آنرا به حافظه خارجی دستگاه منتقل کند.<br />
<b>preferExternal:</b> با تعریف این مقدار برای ویژگی installLocation در مانیفست پروژه اندرویدی، به سیستم عامل اعلام می‌کنیم که ترجیح (prefer) می‌دهیم اپ ما روی حافظه خارجی نصب شود. اما از واژه &#8220;ترجیح&#8221; می‌توان فهمید که ضمانتی در کار نیست و اینکه برنامه ما روی حافظه خارجی نصب شود یا داخلی باز هم به تصمیم سیستم عامل بستگی دارد.<br />
برای مثال چنانچه حافظه خارجی ظرفیت لازم برای قرارگیری فایل برنامه را نداشته باشد، عمل نصب روی حافظه داخلی انجام خواهد شد.<br />
<b>internalOnly:</b> همانطور که از نام آن پیداست، با تعیین این مقدار، امکان نصب اپلیکیشن فقط روی حافظه داخلی امکان پذیر خواهد بود و در آینده هم کاربر امکان انتقال آن به حافظه خارجی را نخواهد داشت.<br />
بنابراین برای فعال کردن امکان نصب برنامه اندرویدی روی کارت حافظه لازم است مقدار preferExternal را انتخاب کنیم. لازم به تاکید است که ضمانتی در خصوص نصب برنامه روی حافظه خارجی وجود ندارد.</p>
<div class="alert alert-warning">
<span class="notice">نکته:</span> در برخی از موبایل‌ها یا تبلت‌های اندرویدی، قابلیت نصب برنامه روی حافظه خارجی حتی با وجود فعال بودن preferExternal در برنامه، توسط سازنده دستگاه غیر فعال شده و حتی پس از نصب روی حافظه داخلی هم امکان انتقال آن به حافظه خارجی وجود ندارد.<br />
البته در تعدادی از دستگاه‌ها، در قسمت ویژه توسعه دهندگان (Developer Options) گزینه‌ای با نام Force allow apps on external وجود دارد که با فعال کردن آن، این امکان میسر خواهد بود:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/sd/force_allow_apps_on_external.png" alt="گزینه Force allow apps on external در Developer Options" /><figcaption>گزینه Force allow apps on external در Developer Options</figcaption></figure>
<p>اما توجه داشته باشید برای کاربران به عنوان مخاطب عام برنامه نمی‌توان روی این گزینه حساب کرد.
</p></div>
<p>لازم به ذکر است با تعیین فضای خارجی به عنوان محل نصب برنامه، فقط فایل APK روی حافظه خارجی قرار گرفته و مابقی موارد مانند داده‌ها، دیتابیس برنامه و سایر موارد باز هم روی حافظه داخلی ذخیره می‌شود.</p>
<h3 id="when-shouldnt-install-app-on-sd-card">در چه مواردی نباید برنامه روی حافظه خارجی نصب شود؟</h3>
<p>چنانچه به هر دلیلی حتی برای چند لحظه حافظه خارجی از دستگاه جدا شود، می‌تواند برخی از عملکردهای برنامه ما را تحت تاثیر قرار داده و باعث بروز مشکل شود.<br />
برای مثال قبلا با <a href="http://android-studio.ir/android-service/" target="_blank" rel="noopener">کاربرد Service ها در اندروید</a> آشنا شدیم. چنانچه در اپ از سرویس‌ها استفاده کرده باشیم، هنگام جدا شدن کارت حافظه از دستگاه، سرویس متوقف شده و با اتصال مجدد آن به دستگاه، سرویس به صورت خودکار مجدد راه اندازی نمی‌شود.<br />
همچنین در صورت استفاده از AlarmManager در برنامه، با جدا شدن کارت حافظه آلارمی که توسط این متد ثبت کرده‌ایم لغو شده و با اتصال مجدد حافظه خارجی، به صورت خودکار فعال نخواهد شد.<br />
به عنوان آخرین مثال، چنانچه بخواهیم با استفاده از <a href="http://android-studio.ir/android-broadcastreceiver-component/" target="_blank" rel="noopener">Broadcast Receiver</a> ها رویداد ACTION_BOOT_COMPLETED را شنود کنیم، با توجه به اینکه پیغام رویداد بوت شدن دستگاه قبل از شناسایی کارت حافظه توسط سیستم عامل ارسال می‌شود، امکان شنود این رویداد در برنامه ما فراهم نخواهد بود.<br />
موفق و پیروز باشید</p>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://developer.android.com/guide/topics/data/install-location" target="_blank" rel="noopener">https://developer.android.com/guide/topics/data/install-location</a>
</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۵<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۱ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/Install_on_sd_card.zip" class="button green edd-submit">دانلود رایگان با حجم ۱ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/Install_on_sd_card.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/enable-android-app-install-on-sd-card/">فعال کردن امکان نصب برنامه روی کارت حافظه</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/enable-android-app-install-on-sd-card/feed/</wfw:commentRss>
			<slash:comments>12</slash:comments>
		
		
			</item>
		<item>
		<title>بهینه کردن و محافظت از سورس برنامه با ProGuard/R8</title>
		<link>https://android-studio.ir/android-app-source-protect-and-optimize-using-proguard-r8/</link>
					<comments>https://android-studio.ir/android-app-source-protect-and-optimize-using-proguard-r8/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Sat, 02 Jan 2021 09:56:56 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=194343</guid>

					<description><![CDATA[<p>یکی از مشکلاتی که اپلیکیشن‌های اندرویدی و صاحبان آن را تهدید می‌کند، سرقت کدهای سورس برنامه توسط سایر افراد است که اصطلاحا دیکد (Decode) نامیده می‌شود. روش مهندسی معکوس باعث می‌شود هر شخص دیگری بتواند کدهای برنامه شما را مشاهده کرده و از آنها استفاده کند. در این آموزش به معرفی روش‌ها و ابزار لازم [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/android-app-source-protect-and-optimize-using-proguard-r8/">بهینه کردن و محافظت از سورس برنامه با ProGuard/R8</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/android_app_protect_source_and_optimizin_using_proguard_r8.png" alt="محافظت از سورس برنامه اندرویدی در برابر دیکد شدن و بهینه کردن آن با ProGuard/R8" /><br />
یکی از مشکلاتی که اپلیکیشن‌های اندرویدی و صاحبان آن را تهدید می‌کند، سرقت کدهای سورس برنامه توسط سایر افراد است که اصطلاحا دیکد (Decode) نامیده می‌شود. روش مهندسی معکوس باعث می‌شود هر شخص دیگری بتواند کدهای برنامه شما را مشاهده کرده و از آنها استفاده کند.<br />
در این آموزش به معرفی روش‌ها و ابزار لازم برای محافظت از سورس برنامه اندرویدی و جلوگیری از دیکد شدن آن توسط فعال کردن ProGuard (پروگارد) و البته جایگزین جدید آن در اندروید استودیو با نام R8 می‌پردازیم. همچنین در مورد بهینه سازی کدهای پروژه اندرویدی و کاهش حجم برنامه که از دیگر وظایف R8 است نیز صحبت خواهیم کرد.</p>
<div class="title-box">
<b>آنچه در این آموزش می‌خوانید:</b></p>
<ul>
<li><a href="#protect-android-app">محافظت از سورس برنامه اندرویدی و جلوگیری از دیکد</a></li>
<li><a href="#what-is-proguard">پروگارد (ProGuard) چیست؟</a>
<ul>
<li><a href="#proguard-tasks">ProGuard چه وظایفی را بر عهده دارد؟</a></li>
</ul>
</li>
<li><a href="#R8-replaced-with-proguard">ابزار R8 جایگزینی شایسته برای ProGuard</a>
<ul>
<li><a href="#proguard-vs-r8">مقایسه R8 با ProGuard</a></li>
<li><a href="#R8-advantages">مزایای کامپایلر R8</a></li>
</ul>
</li>
<li><a href="#enable-R8-proguard-in-android-studio">فعال کردن ProGuard / R8 در اندروید استودیو</a>
<ul>
<li><a href="#R8-fullMode">فشرده سازی بیشتر با فعال کردن fullMode در R8</a></li>
<li><a href="#R8-rules">تعریف قوانین سفارشی در R8</a></li>
<li><a href="#R8-important-rules">قوانین R8 / ProGuard</a></li>
</ul>
</li>
<li><a href="#other-tools">ابزار و روش‌های دیگر جهت محافظت از سورس برنامه اندرویدی</a></li>
</ul>
</div>
<h2 id="protect-android-app">محافظت از سورس برنامه اندرویدی و جلوگیری از دیکد</h2>
<p>به نام خدا. قطعا عبارت &#8220;مهندسی معکوس&#8221; را بارها شنیده و خوانده‌اید. مهندسی معکوس یا Reverse Engineering در حوزه صنعت به اینصورت است که یک سخت افزار که قبلا توسط یک شخص/گروه/نهاد و یا دولت ساخته شده توسط شخص یا گروهی دیگر بررسی شده و نمونه مشابه آن با هزینه و صرف وقت کمتر تولید می‌شود.<br />
اما این اتفاق صرفا محدود به ابزار صنعتی و نظامی نیست و در همه حوزه‌ها شاهد وقوع آن هستیم. از جمله در زمینه IT و نرم افزارهایی که برای پلتفرم‌ها و سیستم عامل‌های مختلف توسط گروه‌های متعددی منتشر شده‌اند.<br />
قبلا در مبحث <a href="http://android-studio.ir/introduction-android-operating-system/" target="_blank" rel="noopener">آشنایی با سیستم عامل اندروید</a> گفتیم که برنامه‌های اندرویدی هم مانند هر پلتفرم دیگری برای آنکه قابلیت اجرا روی دستگاه را داشته باشد ابتدا باید کامپایل شوند. زمانی که ما از پروژه خود <a href="http://android-studio.ir/generating-signed-apk/" target="_blank" rel="noopener">خروجی APK یا AAB در اندروید استودیو</a> می‌گیریم عملیات کامپایل شدن سورس برنامه انجام می‌شود.<br />
پس از Compile شدن پروژه اندرویدی، ساختار کدها دگرگون شده و کدها مانند قبل خوانا نیست اما نه به این معنی که قابل برگشت نباشد! برنامه‌های جاوا را می‌توان به سادگی دیکامپایل (Decompile) کرد. کافیست عبارت Java decompiler را در گوگل جستجو کنید.<br />
دیکامپایل کردن برنامه‌های اندروید هم به همین سادگی انجام می‌شود. ابزار معروفی با نام JADX که به صورت رایگان و منبع باز در مخزن گیت هاب در دسترس همگان قرار دارد. حتی شخصی که بخواهد اپلیکیشن شما را دیکامپایل کند نیازی به راه اندازی JADX روی رایانه خود را نیز نداشته و در وب سایتی مانند javadecompilers.com می‌تواند فایل APK اپ را بارگزاری کرده و نسخه دیکد شده را در ظرف چند ثانیه تحویل بگیرد.<br />
البته که نسخه دیکد شده‌ی دریافتی از JADX آماده ایمپورت در <a href="http://android-studio.ir/نصب-اندروید-استودیو/" target="_blank" rel="noopener">محیط توسعه اندروید استودیو</a> نیست اما بهرحال کلاس‌ها و کدهای جاوا قابل دسترسی هستند و استفاده یا بهتر است بگوییم سوء استفاده از آنها را تسهیل می‌کند.<br />
ضمن اینکه لازم به ذکر است با توجه به ماهیت زبان جاوا، ما هیچگاه نمی‌توانیم جلوی دیکد شدن کدها و کلاس‌های جاوا را بگیریم و صرفا باید سعی کنیم خوانایی و درک کدها پس از دیکد شدن برای افراد سخت تر شود. کدهای جاوا پس از دیکامپایل، به byte code ها تبدیل می‌شود نه کدهای native بنابراین دیکد کردن آن به سادگی امکانپذیر است.<br />
در مواردی مانند پرداخت درون برنامه‌ای که اپلیکیشن کد یکتایی را از مارکت اندرویدی برای صحت سنجی سابقه خرید کاربر دریافت می‌کند، هکر می‌تواند این کد را غیر فعال کرده و بدون پرداخت هزینه به شما از امکانات برنامه استفاده کند.<br />
بدترین اتفاق می‌تواند این باشد که شخصی برنامه شما را دیکامپایل کرده و با صرف هزینه و زمانی بسیار کمتر از آنچه شما بهایش را پرداخته‌اید، برنامه‌ای مشابه اپلیکیشن شما ساخته و با نام خودش در مارکت‌ها منتشر کند و حتی با نمایش تبلیغات و یا پرداخت درون برنامه‌ای به کسب درآمد بپردازد!<br />
برای محافظت از سورس برنامه اندرویدی و جلوگیری از دیکد شدن کدهای جاوا و به عبارتی جلوگیری از دیکامپایل شدن اپ، روش‌ها و ابزار متعددی وجود دارد. برخی از ابزار رایگان و برخی دیگر نیاز به پرداخت هزینه لایسنس دارند. در این مبحث تنها به بررسی روش‌های رایج و ابزار رایگان می‌پردازیم و سایر موارد را صرفا به اشاره‌ای اکتفا خواهیم کرد.<br />
Obfuscation به معنی مبهم سازی، اصلی ترین کاری است که به جهت محافظت از سورس برنامه اندرویدی و جلوگیری از دیکد شدن کدهای جاوا انجام می‌شود. در ادامه مبحث به طور مفصل به جزئیات می‌پردازیم.</p>
<h2 id="what-is-proguard">پروگارد (ProGuard) چیست؟</h2>
<p>اگر به محتویات فایل build.gradle (Module:App) پروژه‌های اندرویدی در اندروید استودیو دقت کرده باشید یک بلاک به صورت زیر به صورت پیش فرض وجود دارد:</p>
<pre class="brush: java; title: ; notranslate">
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
</pre>
<div class="alert alert-warning">
<span class="notice">نکته:</span> کدهایی که درون بلاک buildTypes قرار می‌گیرند تنها هنگام بیلد شدن پروژه اجرا می‌شوند. درون این بلاک یک بلاک دیگر با نام release وجود دارد که کدهای مربوط به پروگارد داخل آن تعریف شده. این کدها هنگامی اجرا خواهند شد که بخواهیم خروجی نسخه release از پروژه خود بگیریم. همچنین اگر بخواهیم کدهای پروگارد یا هر کد موردنظر دیگری را هنگام debug پروژه اجرا کنیم باید بجای بلاکی با نام release در بلاکی با نام debug درون بلاک buildTypes تعریف شود.
</div>
<p>فعال کردن ProGuard در اندروید استودیو بسیار ساده است. پروگارد در بیلد سیستم Gradle قرار داده شده و نیاز به نصب جداگانه ندارد. در حالت پیش فرض برای minifyEnabled مقدار false تعریف شده. این آیتم برای تعیین فعال یا غیر فعال بودن Proguard هنگام کامپایل شدن پروژه استفاده می‌شود. ترجمه تحت الفظی آن می‌شود &#8220;کوچک کردن فعال است&#8221;. چنانچه مقدار را به true تغییر دهیم، پروگارد فعال شده و هنگام گرفتن خروجی پروژه (release یا debug) وظایف خودش را انجام خواهد داد.<br />
در خط دوم، فایل تنظیمات و قوانین پیش فرض پروگارد توسط proguardFiles تعیین شده است.</p>
<h3 id="proguard-tasks">ProGuard چه وظایفی را بر عهده دارد؟</h3>
<p>در صورت فعال کردن پروگارد در پروژه اندرویدی در اندروید استودیو، سه عمل در هنگام کامپایل شدن انجام می‌شود که به شرح زیر می‌باشد:<br />
<b>مبهم سازی نام‌ها یا Name obfuscation:</b> تغییر نام کلاس‌ها، فیلدها و متدها با نام‌های کوتاه و عموما یک کاراکتری. برای مثال ممکن است نام کلاس Users به a تغییر پیدا کند که علاوه بر کاهش تعداد کاراکترهای نام کلاس هنگام فراخوانی و به دنبال آن کاهش حجم نهایی کدها، خوانایی، درک و تشخیص کدها را تا حدود زیادی مشکل می‌کند.<br />
<b>Shrinking یا Tree shaking:</b> این قابلیت باعث می‌شود تا کلاس‌ها، متدها، فیلدها، ویژگی‌ها (attributes) و کتابخانه‌های بلا استفاده درون پروژه هنگام کامپایل حذف شوند که باز هم کاهش حجم نهایی اپ را بدنبال خواهد داشت. بنابراین این قابلیت به بهینه شدن و افزایش سرعت اجرای برنامه روی دستگاه کاربر کمک می‌کند.<br />
<b>بهینه سازی کد یا Code optimization:</b> در هر دو مورد قبل عمل بهینه کردن کدها انجام می‌شد اما هنوز هم برای کمتر شدن حجم کدها جای کار هست. وظیفه دیگر پروگارد بهینه کردن کدهای جاوا است. به عبارت دیگر، کدها بازنویسی می‌شوند تا در حد امکان تعداد کاراکترها و دستورات کاهش یافته و حجم فایل DEX اپ به کمترین میزان ممکن برسد.<br />
به عبارتی کدها Minify می‌شوند. درست مانند آنچه در Minify کردن کدهای CSS صفحات وب اتفاق می‌افتد. هنگام Minify کردن یک کد CSS عملیاتی مانند حذف فاصله‌های اضافی و کامنت‌ها انجام می‌شود. همچنین برای مثال اگر برای یک قسمت از صفحه وب، رنگ <span dir="ltr">#FFFFFF</span> تعریف شده باشد، آنرا به <span dir="ltr">#FFF</span> تغییر می‌دهد. همین تغییرات کوچک و به ظاهر غیر ضرور، در نهایت حجم قابل توجهی از فایل نهایی CSS را کاهش می‌دهد.<br />
برای مثال اگر در بخشی از پروژه اندرویدی یک if/else تعریف کرده باشیم و پروگارد تشخیص دهد بلاک <span dir="ltr">else{}</span> هیچگاه اجرا نخواهد شد، آنرا از خروجی برنامه حذف می‌کند. یا مثلا اگر متدی تعریف کرده باشیم که فقط از یک جا فراخوانی می‌شود، پروگارد آنرا حذف کرده و به صورت inline در محل کد اصلی جایگزین می‌کند.<br />
به این ترتیب درصدی از کدهای اضافی پروژه اندرویدی ما حذف شده که علاوه بر کاهش حجم نهایی فایل APK یا AAB برنامه، با بهینه شدن دستورات و کلاس‌های جاوا، سرعت اجرای برنامه می‌تواند افزایش یابد. پروگارد می‌تواند تا ۵۰% از حجم بایت کدهای اپلیکیشن اندرویدی را کاهش دهد.<br />
خب! تا حد زیادی با ProGuard و کاربردهای آن آشنا شدیم. اما لازم است بگویم که پس از انتشار Android Gradle plugin 3.4.0 و همزمان با انتشار Android studio 3.3 beta (در ماه April سال ۲۰۱۹) جایگزینی جدید با نام R8 توسط توسعه دهندگان اندروید استودیو برای ProGuard معرفی شد. این ابزار جدید نسبت به نسخه قبلی برتریی‌هایی را داشته و خروجی بهینه تری را برای ما رقم می‌زند. البته جای هیچ نگرانی نیست زیرا طریق فعالسازی و استفاده آن تفاوتی با پروگارد ندارد.<br />
در واقع R8 از ترکیب و ادغام ProGuard با ابزار دیگر ساخته شده و از قوانین آن پیروی می‌کند. در ادامه بیشتر با R8 آشنا می‌شویم.</p>
<h2 id="R8-replaced-with-proguard">ابزار R8 جایگزینی شایسته برای ProGuard</h2>
<p>همانطور که قبلا اشاره شد، با انتشار پلاگین گریدل نسخه ۳٫۴٫۰ برای اندروید استودیو، ابزار معروف ProGuard بازنشسته شد و جای خود را به ابزاری تازه نفس و قدرتمندتر به نام R8 داد. ابزار R8 نسبت به پروگارد، عملیات بهینه سازی را با سرعت بالاتری انجام داده و علاوه بر آن، درصد بهینه سازی و کاهش حجم فایل نصبی برنامه نیز افزایش می‌دهد.<br />
شاید بهتر باشد مزایای R8 را نسبت به ProGuard به صورت دقیق‌تر و با جزئیات بیشتری بررسی کنیم.</p>
<h3 id="proguard-vs-r8">مقایسه R8 با ProGuard</h3>
<p>لازم می‌دانم از کلی گویی اجتناب کرده و تفاوت‌های این دو را به لحاظ فنی و دقیق برای شما تشریح کنم تا درک بهتری از تصمیم گوگل برای جایگزینی R8 داشته باشید. البته عنوان مقایسه خیلی برای این قسمت کامل نیست. می‌خواهم تاریخچه آنچه در طی این چند سال توسعه دهندگان اندروید را از ProGuard به R8 رسانده بیان کنم.<br />
در گذشته روش کار به اینصورت بود که کامپایلر جاوا، سورس کدهای جاوای پروژه اندرویدی را به بایت کد (Bytecode) های جاوا تبدیل می‌کرد. سپس این بایت کدها توسط ProGuard بهینه شده و بایت کدهای بهینه‌ای ایجاد می‌شد که سریعتر و کم حجم تر بودند. در نهایت، کامپایلر DX این بایت کدها را به بایت کدهای ویژه ماشین مجازی دالویک (Dalvik) تبدیل می‌کرد. قبلا در مبحث آشنایی با سیستم عامل اندروید با کاربرد ماشین‌های مجازی Dalvik و ART آشنا شدیم.</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/android_dex_compiler.png" alt="کامپایلر DEX اندروید" /><figcaption>کامپایلر DEX اندروید استودیو</figcaption></figure>
<p>این بایت کد که با فرمت <span dir="ltr">.dex</span> در پکیج فایل نصبی APK نگهداری می‌شود بسته به نسخه سیستم عامل اندروید دستگاه کاربر، توسط ماشین مجازی Dalvik یا ART و یا ترکیبی از هردو (مانند Android P) به زبان قابل فهم برای ماشین ترجمه می‌شد.<br />
این فرآیند قدری زمان بر بود و در سال ۲۰۱۵ تیم توسعه اندروید تصمیم گرفت کامپایلر جدیدی را جایگزین کند تا مراحل کامپایل کاهش یابد. بنابراین کامپایلری با نام Jack &#038; Jill معرفی شد.</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/android_jack_and_jill_compiler.png" alt="کامپایلر Jack &#038; Jill" /><figcaption>کامپایلر Jack &#038; Jill اندروید استودیو</figcaption></figure>
<p>در واقع این کامپایلر ترکیبی از کامپایلر جاوا، پروگارد و کامپایلر دالویک بود و دستورات و توابع هرسه در یک مرحله انجام می‌شد که نتیجه آن افزایش سرعت کامپایل پروژه اندرویدی بود. اما Jack &#038; Jill هم خالی از ایراد نبود و در تعامل با برخی بایت کدهای Java مشکلاتی داشت. به همین دلیل تیم توسعه اندروید این کامپایلر را در سال ۲۰۱۷ کنار گذاشت.<br />
سپس کامپایلر دیگری با نام D8 جایگزین شد که از حیث تعداد مراحل تفاوتی با حالت اول نداشت و صرفا در مرحله آخر، D8 جایگزین DX شده بود.</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/android_d8_compiler.png" alt="کامپایلر D8" /><figcaption>کامپایلر D8 اندروید استودیو</figcaption></figure>
<p>کامپایلر D8 سازگاری بیشتر بخصوص با کاتلین داشت. علاوه بر آن بایت کدهای کمتر و بهینه تری را تولید می‌کرد.<br />
در نهایت R8 معرفی شد که از ادغام ProGuard و D8 بوجود آمده است.</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/android_r8_compiler.png" alt="ابزار R8 اندروید استودیو برای مبهم سازی، بهینه سازی و کوچک کردن کدهای برنامه اندرویدی" /><figcaption>کامپایلر R8 اندروید استودیو</figcaption></figure>
<p>در حال حاضر، بایت کدهای جاوا مستقیما توسط کامپایلر R8 به بایت کدهای بهینه دالویک تبدیل می‌شوند. مراحل کمتر، بهینه بودن و سازگاری بیشتر با نیازهای جدید.</p>
<h3 id="R8-advantages">مزایای کامپایلر R8</h3>
<p>از مهمترین دلایلی که گوگل را مجاب کرد تا ابزار کامپایلر R8 را جایگزین ProGuard در اندروید استودیو کند می‌توان به موارد زیر اشاره کرد:<br />
<b>بهینه سازی بیشتر:</b> R8 در مقایسه با ProGuard بهینه سازی و Minification عمیق‌تری انجام می‌دهد که نتیجه آن فشرده شدنِ بیشترِ نسخه خروجی یعنی فایل APK است. به عبارتی می‌توانیم چند درصد دیگر هم کاهش حجم را هنگام کامپایل کردن پروژه اندرویدی خود شاهد باشیم.<br />
بر اساس تست‌هایی که انجام شده R8 حتی می‌تواند تا ۷۰ درصد از حجم برنامه را نسبت به حالت عادی فشرده‌تر کند که یک خروجی فوق العاده به حساب می‌آید. حتی ۱ درصد کاهش حجم بخصوص در برنامه‌های با حجم بالا می‌تواند در تسریع دسترسی کاربران به اپلیکیشن هنگام دریافت آن از مارکت‌ها و همچنین اجرای برنامه روی دستگاه نقش موثری داشته باشد.<br />
<b>سازگاری بیشتر با Kotlin:</b> با پشتیبانی رسمی محیط توسعه اندروید استودیو از زبان محبوب کاتلین در نسخه‌های جدید، سازگاری هرچه بیشتر کامپایلر با کدهای Kotlin ضروری بود. R8 این ویژگی را دارد و در بهینه کردن کدهایی که به زبان کاتلین نوشته شده‌اند بازدهی بالاتری نسبت به پروگارد دارد.<br />
<b>خروجی بهتر:</b> R8 خروجی بهتری نسبت به پروگارد به ما تحویل می‌دهد. علاوه بر آن، سرعت بیلد شدن پروژه نیز به نسبت قبل مقداری کاهش می‌یابد.</p>
<h2 id="enable-R8-proguard-in-android-studio">فعال کردن ProGuard / R8 در اندروید استودیو</h2>
<p>به اندازه کافی در خصوص پروگارد و جایگزین جدید آن یعنی R8 به تئوریات پرداختیم. بهتر است روش فعالسازی و همچنین جزئیات آن را بررسی کنیم. البته در اینجا می‌خواهیم خروجی پروژه را قبل و بعد از فعال کردن R8 بررسی کنیم تا نتیجه کار را در عمل ببینیم.<br />
مطابق مبحث <a href="http://android-studio.ir/create-android-project-and-its-structure/">آموزش ساخت پروژه در اندروید استودیو</a> یک پروژه اندرویدی با نام My Application می‌سازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم.<br />
یک متغیر از نوع String درون اکتیویتی پیش فرض پروژه تعریف می‌کنم:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    String myString;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myString = &quot;android-studio.ir&quot;;

    }
}
</pre>
<p>از مسیر Build > Generate Signed Bundle/APK یک خروجی APK از پروژه می‌گیرم.<br />
بعد از ساخته شدن فایل، آنرا تغییر نام می‌دهم تا از خروجی دوم قابل تشخیص باشد. حالا فایل build.gradle (app) پروژه را باز کرده و در بلاک release برای minifiEnabled مقدار true را جایگزین false می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
</pre>
<div class="alert alert-warning">
<span class="notice">نکته:</span> در ابتدای مبحث هم اشاره شد که R8 از قوانین ProGuard استفاده می‌کند بنابراین آیتم proguardFiles نباید ابهامی در خصوص اینکه آیا R8 فعال است یا ProGuard برایتان ایجاد کند. البته به شرط آنکه از اندروید استودیو ۳٫۴ به بالا استفاده می‌کنید.
</div>
<div class="alert alert-warning">
<span class="notice">نکته:</span> با انتشار نسخه ۳٫۴ اندروید استودیو و معرفی R8 بجای پروگارد، ابتدا برای فعالسازی آن لازم بود کد زیر در gradle.properties تعریف شود:</p>
<pre class="brush: java; title: ; notranslate">
android.enableR8=true
</pre>
<p>اما در نسخه‌های جدید اندروید استودیو این کد کارایی نداشته و صرفا باید مقدار minifyEnabled به true تغییر داده شود.
</p></div>
<p>مجدد یک خروجی APK از پروژه می‌سازم:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/apk_size_before_vs_after_r8_proguard_enabled.png" alt="مقایسه حجم اپلیکیشن اندرویدی قبل و بعد از فعالسازی ProGuard یا R8" /><figcaption>حجم فایل APK قبل و بعد از فعالسازی ProGuard/R8</figcaption></figure>
<p>در تصویر بالا مشاهده می‌کنید حجم فایل APK که در مرحله اول ساخته شده ۲۲۲۱ کیلوبایت است در صورتی که فایل دوم ۱۴۷۰ کیلوبایت حجم دارد. یعنی با فعال شدن R8 در حین کامپایل برنامه چیزی حدود ۳۰ درصد از حجم فایل نصبی برنامه کاهش یافت.<br />
اما همانطور که مفصل بحث کردیم، در کنار بهینه سازی کدها و کاهش حجم نهایی، یک وظیفه دیگر هم بر عهده R8 هست؛ یعنی همان مبهم سازی کدهای جاوا به جهت محافظت از سورس برنامه اندرویدی که در نهایت باعث جلوگیری از دیکد شدن کلاس‌ها، متدها و فیلدها می‌شود.<br />
البته لازم به تکرار است وقتی صحبت از جلوگیری از دیکامپایل شدن اپ می‌کنیم منظور سخت کردن فرایند است وگرنه بطور کامل نمی‌توان از دیکد کردن جاوا و دیکامپایل شدن اپلیکیشن اندرویدی جلوگیری کرد.<br />
برای تست و بررسی فعال شدن R8 در پروژه، هردو فایل APK را توسط <a href="http://www.javadecompilers.com/apk" rel="nofollow noopener" target="_blank">APK Decompiler</a> بصورت آنلاین دیکامپایل می‌کنم. این سرویس رایگان و آنلاین از <a href="https://github.com/skylot/jadx" target="_blank" rel="noopener">JADX</a> برای دیکامپایل کردن فایل dex موجود در پکیج برنامه اندرویدی و تبدیل آن به کلاس‌های Java استفاده می‌کند.</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/decompile_apk_using_jadx_dex_decompiler.png" alt="جلوگیری از دیکامپایل اپ اندرویدی توسط ابزاری مانند JADX یا ApkTool" /></p>
<p>کافیست فایل apk را انتخاب کرده و روی گزینه Upload and Decompile کلیک کنم تا در ظرف مدت چند ثانیه فایل zip سورس برنامه را تحویل دهد!</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/decompile_apk_using_jadx_dex_decompiler_2.png" alt="دیکامپایل فایل APK توسط ابزار آنلاین" /><figcaption>دیکامپایل فایل APK توسط ابزار آنلاین</figcaption></figure>
<p>این کار را برای هردو فایل انجام می‌دهم. فایل‌های zip را باز کرده و بررسی می‌کنم. برای مثال تفاوت کلاس MainActivity به ترتیب، قبل و بعد از فعال شدن R8 به اینصورت است:</p>
<pre class="brush: java; title: ; notranslate">
package com.example.myapplication;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    String myString;

    /* access modifiers changed from: protected */
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView((int) C0400R.layout.activity_main);
        this.myString = &quot;android-studio.ir&quot;;
    }
}
</pre>
<pre class="brush: java; title: ; notranslate">
package com.example.myapplication;

import android.os.Bundle;
import p002b.p004b.p005c.C0149h;

public class MainActivity extends C0149h {
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView((int) R.layout.activity_main);
    }
}
</pre>
<p>ملاحظه می‌کنید علاوه بر حذف خط کامنت از کلاس، نام کلاس کتابخانه</p>
<pre class="brush: java; title: ; notranslate">
androidx.appcompat.app.AppCompatActivity
</pre>
<p>به</p>
<pre class="brush: java; title: ; notranslate">
p002b.p004b.p005c.C0149h
</pre>
<p>تغییر یافته. همچنین String ای که داخل اکتیویتی تعریف شده بود نیز حذف شده زیرا R8 تشخیص داده که این متغیر بلا استفاده بوده و در هیچ قسمتی از برنامه فراخوانی نشده است.<br />
نتیجه‌ی این تغییر، کاهش حجم کد از ۴۲۵ کاراکتر به ۲۷۵ کاراکتر و همچنین سخت شدن درک کدها شده است.</p>
<h3 id="R8-fullMode">فشرده سازی بیشتر با فعال کردن fullMode در R8</h3>
<p>اما R8 هنوز هم می‌تواند اپلیکیشن ما را فشرده تر کند. کافیست در فایل gradle.properties خط زیر را اضافه کنیم:</p>
<pre class="brush: java; title: ; notranslate">
android.enableR8.fullMode=true
</pre>
<p>توجه داشته باشید این فایل مختص پروژه فعلی نیست و تنظیمات آن در همه‌ی پروژه‌های شما اعمال می‌شود. بنابراین اگر در پروژه دیگری نیاز به فعالسازی fullMode نباشد لازم است این خط را حذف یا کامنت کنیم.</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/enable_R8_fullMode_in_android_studio.png" alt="فعال کردن قابلیت fullMode در R8 برای بهینه سازی بیشتر در اندروید استودیو" /><figcaption>فعال کردن قابلیت fullMode در R8</figcaption></figure>
<p>مجددا یک خروجی APK از پروژه می‌گیرم تا تفاوت حجم برنامه قبل و بعد از فعال کردن حالت fullMode را ارزیابی کنیم:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/R8_fullMode_result.png" alt="کاهش حجم بیشتر در R8 توسط قابلیت fullMode" /><figcaption>کاهش بیشتر حجم پس از فعالسازی fullMode</figcaption></figure>
<p>مشاهده می‌کنید بازهم حدود ۱۰۰ کیلوبایت از حجم نهایی اپلیکیشن کاسته شد. به عبارت دیگر با فعال کردن R8 و همچنین حالت fullMode حدود ۴۰ درصد کاهش حجم را در خروجی پروژه شاهد بودیم. البته این اعداد ثابت نیست و بسته به مقدار کدها، تعداد کتابخانه‌های بکار رفته در پروژه، منابع موجود در پروژه مانند تصاویر، فایل‌های صوتی و&#8230; متغیر خواهد بود.</p>
<h3 id="R8-rules">تعریف قوانین سفارشی در R8</h3>
<p>با فعال کردن ProGuard/R8 در پروژه اندرویدی، هرسه عملکردی که قبلا به آنها اشاره کردیم روی سورس پروژه شامل کلاس‌ها، متدها و فیلدها اعمال می‌شوند. اما گاهی اوقات لازم است یک آیتم را به عنوان استثناء در R8 تعریف کنیم تا هیچگونه تغییری روی آن انجام نگردد.<br />
برای مثال در <a href="http://android-studio.ir/retrofit-with-php-mysql-web-service/" target="_blank" rel="noopener">آموزش کار با کتابخانه Retrofit</a> که شامل یک فرم عضویت بود و اطلاعات شخص شامل نام، نام کاربری و رمز عبور به سرور ارسال می‌شد، نام متغیرهای ارسالی نباید دچار هیچگونه تغییراتی شود در غیر اینصورت اطلاعات عضویت شخص در دیتابیس ثبت نخواهد شد.<br />
اما راهکار چیست؟ آیا به دلیل جلوگیری از بروز اشکال در یک کتابخانه یا کلاس یا تابع باید از بهینه کردن و محافظت از سورس برنامه اندرویدی و جلوگیری از دیکامپایل شدن اپ چشم پوشی کنیم؟ خیـــــر!<br />
در ProGuard و جایگزین آن یعنی R8 قابلیتی در اختیار توسعه دهنده و برنامه نویس اندرویدی قرار گرفته که می‌تواند برای آیتم‌های خاص استثناء قائل شد تا بدون هیچگونه مبهم سازی و یا بهینه سازی در نسخه کامپایل شده برنامه قرار گیرند.<br />
دوباره به بلاک release دقت کنید:</p>
<pre class="brush: java; title: ; notranslate">
buildTypes {
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}
</pre>
<p>قوانین و عملکردهای پایه و پیش فرض پروگارد (R8) توسط getDefaultProguardFile از فایلی با نام proguard-android-optimize.txt خوانده می‌شود. اما چنانچه بخواهیم استثنائی در قوانین تعریف کنیم نیازی به ویرایش فایل پیش فرض نیست و لازم است قوانین اختصاصی پروژه را در فایل proguard-rules.pro اضافه کنیم.<br />
این فایل در کنار سایر فایل‌های زیر مجموعه Gradle قرار دارد:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/add_ProGuard_R8_custom_rules_to_proguard-rules.pro.png" alt="تعریف قوانین سفارشی برای ProGuard/R8 در فایل proguard-rules.pro" /><figcaption>محل تعریف قوانین سفارشی برای ProGuard/R8</figcaption></figure>
<p>این فایل به صورت پیش فرض دارای هیچ قانون فعالی نیست و صرفا چند خط توضیحات به صورت کامنت قید شده است. البته در بین توضیحات، چند مورد از قوانین پر کاربرد هم ذکر شده که در صورت نیاز کافیست از حالت کامنت خارج شود:</p>
<p><span class="filename">proguard-rules.pro</span></p>
<pre class="brush: java; title: ; notranslate">
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

برای مثال چنانچه بخواهیم از WebView در برنامه خود استفاده کنیم و صفحه وب موردنظر حاوی کدهای JS (جاوا اسکریپت) باشد، قانونی که بعد از توضیحات مربوط به وب ویو قرار گرفته را از حالت کامنت خارج می‌کنیم:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}
</pre>
<p>معمولا کتابخانه‌هایی که نیاز به تعریف قوانین اختصاصی دارند توضیحات لازم را در صفحه معرفی خود قید می‌کنند. از جمله Retrofit:</p>
<figure>
<img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/r8/proguard_R8_rules_for_retrofit_library.png" alt="قوانین سفارشی کتابخانه Retrofit برای ProGuard/R8" /><figcaption>قوانین سفارشی کتابخانه Retrofit برای ProGuard/R8</figcaption></figure>
<p>ملاحظه می‌کنید در قسمت توضیحات مربوط به قوانین R8 / ProGuard سه لینک ذکر شده که مربوط به قوانین Retrofit و OkHttp و Okio هستند. از آنجایی که کتابخانه‌های OkHttp و Okio در داخل کتابخانه Retrofit قرار دارند، لازم است قوانین هرسه مورد به پروژه اضافه شود.<br />
من برای بررسی بیشتر قوانین، موارد مربوط به خود رتروفیت را اضافه می‌کنم:</p>
<p><span class="filename">proguard-rules.pro</span></p>
<pre class="brush: java; title: ; notranslate">
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
   public *;
}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* &lt;methods&gt;;
}

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*

# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* &lt;methods&gt;; }
-keep,allowobfuscation interface &lt;1&gt;
</pre>
<p>اگر این قوانین برایتان مبهم بنظر می‌رسند جای نگرانی نیست؛ در اینجا به موارد پر کاربرد اشاره می‌کنیم.</p>
<h3 id="R8-important-rules">قوانین R8 / ProGuard</h3>
<p>مهمترین قوانین R8/ProGuard را به طور مختصر توضیح می‌دهم:<br />
<b>-keepattributes:</b> ویژگی‌هایی که توسط این قانون تعریف شوند بدون تغییر باقی می‌مانند.<br />
<b>-keep:</b> توسط این دستور می‌توان یک کلاس و متدها و فیلدهای داخل آن را به عنوان استثناء تعریف کرد تا عملکردهای R8 روی آن پیاده نشود.<br />
<b>-keepclassmembers:</b> متدها و فیلدهای موردنظر داخل یک کلاس را می‌توان به عنوان استثناء تعریف نمود.<br />
<b>-keepnames:</b> از تغییر نام کلاس یا متدها و فیلدهای مدنظر جلوگیری می‌کند.<br />
<b>-dontwarn:</b> تعیین می‌کند که هیچگونه هشداری در مورد ارجاعاتی (References) که در کلاس مدنظر ذکر شده و ProGuard/R8 آن را پیدا نمی‌کند و یا سایر خطاها، داده نشده و از آنها عبور کند.<br />
<b>-dontshrink:</b> این قانون، قابلیت Shrink یعنی حذف آیتم‌های بلا استفاده را غیر فعال می‌کند و صرفا دو عمل دیگر یعنی بهینه سازی (Optimization) و مبهم سازی (Obfuscation) برای آن انجام می‌شود.<br />
<b>-dontoptimize:</b> مانند مورد قبل با این تفاوت که فقط عمل بهینه سازی انجام نمی‌شود.</p>
<p>قوانین پروگارد گسترده تر از چند موردی است که در اینجا اشاره شد. برای مطالعه لیست کامل قوانین و همچنین توضیحات بیشتر به وب سایت آن در صفحه <a href="https://www.guardsquare.com/en/products/proguard/manual/refcard" target="_blank" rel="noopener">ProGuard Reference card</a> مراجعه کنید.</p>
<div class="alert alert-warning">
<span class="notice">نکته:</span> با توجه به تغییرات زیادی که توسط R8 در سورس پروژه انجام می‌شود لازم است قبل از انتشار نسخه نهایی اپ جهت ارائه به کاربران، تمامی قسمت‌های آن به دقت تست و بررسی شده و در صورت نیاز، قوانین لازم برای کتابخانه‌ها و کلاس‌ها تعریف شود.
</div>
<h2 id="other-tools">ابزار و روش‌های دیگر جهت محافظت از سورس برنامه اندرویدی</h2>
<p>تا اینجا توانستیم با استفاده از ابزار داخلی و رایگان اندروید استودیو تا حد زیادی عمل مبهم سازی و بهینه سازی فایل APK را انجام دهیم. اما جلوگیری از دیکد شدن سورس کد برنامه اندرویدی محدود به همین R8 نیست.<br />
R8 یا ProGuard می‌تواند عملیات مبهم سازی را بر روی کلاس‌ها، فیلدها و متدها پیاده سازی کرده تا درک کدها و سوء استفاده از آنها به راحتی امکان پذیر نباشد. اما این ابزار String ها را بدون تغییر باقی می‌گذارد که در مواردی می‌تواند امنیت برنامه ما را بخطر بیندازد.<br />
برای مثال چنانچه اپلیکیشن شما به یک سرور یا API وابسته است و اطلاعاتی بین آنها ردوبدل می‌شود، در صورتی که آدرس (URL) وب سرویس یا API رمزگذاری (Encrypt) نشده باشد هکر می‌تواند با دیکامپایل کردن برنامه به URL یا کلیدهای خصوصی مربوط به API دسترسی پیدا کرده و از آنها برای مقاصد خود استفاده کند.<br />
چنانچه به قابلیت رمزگذاری رشته‌های متنی برای اپ خود نیاز داشته باشیم لازم است از جایگزین‌های R8 استفاده کرده یا ابزار دیگری را در کنار آن اضافه کنیم.<br />
برای مثال شرکت سازنده ProGuard یعنی GuardSquare یک ابزار دیگر با نام <a href="https://www.guardsquare.com/en/products/dexguard" target="_blank" rel="noopener">DexGuard</a> را معرفی کرده که امکانات بیشتری نسبت به پروگارد را در اختیار برنامه نویسان اندرویدی قرار می‌دهد. یکی از امکانات دکس گارد رمزگذاری String هاست. البته این ابزار بر خلاف پروگارد رایگان نبوده و باید لایسنس آن خریداری شود.<br />
اما بر خلاف DexGuard که باید جایگزین ProGuard/R8 شود و رایگان هم نیست، پلاگین‌هایی هستند که رایگان بوده و در کنار R8 و به عنوان مکمل استفاده می‌شوند. از جمله <a href="https://github.com/christopherney/Enigma" target="_blank" rel="noopener">پلاگین Enigma</a> یا <a href="https://github.com/StringCare/AndroidLibrary" target="_blank" rel="noopener">StringCare</a> که هردو رایگان هستند.<br />
البته نوشتن کدهای حساس و مهم برنامه به صورت native به زبان C/C++ هم می‌تواند فرایند مهندسی معکوس را به مراتب دشوار تر از قبل کند. با استفاده از ابزار NDK می‌توانید کدهای نیتیو خود را در قالب فایل‌های  <span dir="ltr">.so</span> به پروژه اندرویدی اضافه کنید.<br />
پرداختن به این ابزار از حوصله این مبحث خارج بوده و صرفا به ذکر نام آنها اکتفا می‌کنم. به امید خدا در آموزش‌های آتی به معرفی کامل و نحوه فعالسازی یکی از آنها خواهیم پرداخت.<br />
موفق و پیروز باشید.</p>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://www.guardsquare.com/en/blog/proguard-and-r8" target="_blank" rel="noopener">https://www.guardsquare.com/en/blog/proguard-and-r8</a><br />
<a href="https://www.guardsquare.com/en/blog/comparison-proguard-vs-r8-october-2019-edition" target="_blank" rel="noopener">https://www.guardsquare.com/en/blog/comparison-proguard-vs-r8-october-2019-edition</a><br />
<a href="https://developer.android.com/studio/build/shrink-code" target="_blank" rel="noopener">https://developer.android.com/studio/build/shrink-code</a><br />
<a href="https://www.guardsquare.com/en/blog/dexguard-vs-proguard" target="_blank" rel="noopener">https://www.guardsquare.com/en/blog/dexguard-vs-proguard</a><br />
<a href="https://blog.mindorks.com/applying-proguard-in-an-android-application" target="_blank" rel="noopener">https://blog.mindorks.com/applying-proguard-in-an-android-application</a><br />
<a href="https://android-developers.googleblog.com/2018/11/r8-new-code-shrinker-from-google-is.html" target="_blank" rel="noopener">https://android-developers.googleblog.com/2018/11/r8-new-code-shrinker-from-google-is.html</a>
</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۲۰<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۲ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/Proguard_R8.zip" class="button green edd-submit">دانلود رایگان با حجم ۲ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/Proguard_R8.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/android-app-source-protect-and-optimize-using-proguard-r8/">بهینه کردن و محافظت از سورس برنامه با ProGuard/R8</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/android-app-source-protect-and-optimize-using-proguard-r8/feed/</wfw:commentRss>
			<slash:comments>11</slash:comments>
		
		
			</item>
		<item>
		<title>بررسی نصب بودن برنامه در اندروید</title>
		<link>https://android-studio.ir/check-if-an-android-application-is-installed-or-not/</link>
					<comments>https://android-studio.ir/check-if-an-android-application-is-installed-or-not/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Tue, 06 Oct 2020 16:05:04 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=191320</guid>

					<description><![CDATA[<p>در این جلسه از سری مباحث آموزش برنامه نویسی اندروید قصد دارم نحوه کنترل و بررسی نصب یا عدم نصب بودن برنامه در اندروید را با دو روش متفاوت بررسی کنم. به اینصورت که چک می‌کنیم آیا اپلیکیشنی با Package name مدنظر ما روی دستگاه اندرویدی قبلا نصب شده یا خیر. همچنین در ادامه تعریف [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/check-if-an-android-application-is-installed-or-not/">بررسی نصب بودن برنامه در اندروید</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/appinstalled/check_if_an_application_is_installed_or_not_on_android_device.png" alt="بررسی نصب بودن برنامه در اندروید" /><br />
در این جلسه از سری مباحث <a href="http://android-studio.ir" target="_blank" rel="noopener noreferrer">آموزش برنامه نویسی اندروید</a> قصد دارم نحوه کنترل و بررسی نصب یا عدم نصب بودن برنامه در اندروید را با دو روش متفاوت بررسی کنم. به اینصورت که چک می‌کنیم آیا اپلیکیشنی با Package name مدنظر ما روی دستگاه اندرویدی قبلا نصب شده یا خیر.<br />
همچنین در ادامه تعریف می‌کنیم چنانچه برنامه مدنظر روی سیستم عامل موجود نبود، صفحه مربوط به نصب نرم افزار در مارکت اندرویدی مانند پلی استور، کافه بازار و&#8230; و یا لینک آنرا در صفحه مرورگر پیش فرض باز کند.</p>
<div class="title-box">
<b>آنچه در این آموزش می‌خوانید:</b></p>
<ul>
<li><a href="#check-if-an-application-is-installed-or-not">در چه مواردی به بررسی نصب بودن برنامه در اندروید نیاز داریم؟</a></li>
<li><a href="#create-project-on-android-studio">ساخت پروژه بررسی نصب بودن برنامه اندرویدی</a>
<ul>
<li><a href="#using-getPackageInfo-method">روش اول: استفاده از متد getPackageInfo</a></li>
<li><a href="#using-intent">روش دوم: استفاده از intent</a></li>
<li><a href="#open-app-store-or-a-web-page">هدایت کاربر به صفحه نصب برنامه در مارکت یا یک صفحه وب</a></li>
</ul>
</li>
</ul>
</div>
<h2 id="check-if-an-application-is-installed-or-not">در چه مواردی به بررسی نصب بودن برنامه در اندروید نیاز داریم؟</h2>
<p>به نام خدا. گاهی اوقات بخشی از فرایندهای موجود در اپلیکیشن ما به یک یا چند برنامه دیگر وابسته است که در صورت نصب نبودن برنامه موردنظر روی دستگاه اندرویدی کاربر، می‌تواند سبب بروز اختلال در عملکرد برنامه ما گردد.<br />
فرض کنید قصد توسعه و برنامه نویسی اپلیکیشنی را دارید که بخشی از امکانات آن با پرداخت درون برنامه‌ای از طریق مارکت‌های اندرویدی فعال می‌شود. در اینجا چنانچه مارکت موردنظر شما (مانند بازار، مایکت و&#8230;) روی دستگاه کاربر از قبل نصب و فعال نشده باشد، عملیات پرداخت ناتمام مانده و برنامه کرش خواهد کرد.<br />
برای حل این مشکل لازم است شرطی را در برنامه تعریف کنیم که قبل از شروع فرایند پرداخت درون برنامه‌ای، بررسی کند صرفا در صورتی این عملیات آغاز شود که مارکت با Package name مدنظر ما روی دستگاه موجود باشد و چنانچه موجود نبود، پیغامی با این مضمون را به کاربر نمایش داده و یا به صفحه دانلود مارکت هدایت شود.<br />
به عنوان یک مثال دیگر، شرکت ارائه دهنده تاکسی آنلاین را درنظر بگیرید که دارای دو اپلیکیشن اندرویدی است. یک برنامه مخصوص مسافران و دیگری ویژه‌ی رانندگان.<br />
قصد داریم در هردو برنامه گزینه‌ای برای انتقال به برنامه دیگر اضافه کنیم به طوری که اگر در اپ ویژه‌ی مسافر، روی گزینه موردنظر کلیک شد، اپ ویژه راننده اجرا شده و بلعکس. و البته اینکه چنانچه برنامه دوم روی دستگاه نصب نبود، پیغام متناسب با آن نمایش داده شده و یا کاربر به صفحه نصب آن در مارکت یا مرورگر هدایت شود.<br />
کاربردهای متعدد دیگری هم برای این قابلیت وجود دارد که در اینجا تنها به دو مورد اکتفا می‌کنم.<br />
در ادامه مبحث در قالب یک پروژه ساده، این قابلیت را بررسی و تمرین می‌کنیم.</p>
<h2 id="create-project-on-android-studio">ساخت پروژه بررسی نصب بودن برنامه اندرویدی</h2>
<p>طبق مبحث <a href="http://android-studio.ir/create-android-project-and-its-structure/" target="_blank" rel="noopener noreferrer">آموزش ساخت پروژه در اندروید استودیو</a> یک پروژه اندرویدی با نام App installed می‌سازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم.<br />
ابتدا نحوه بررسی نصب بودن برنامه در اندروید را به دو روش متفاوت انجام و در مرحله آخر نیز فراخوانی مارکت و یا باز کردن لینک صفحه دانلود برنامه را بررسی می‌کنیم.</p>
<h3 id="using-getPackageInfo-method">روش اول: استفاده از متد getPackageInfo</h3>
<p>در روش نخست، با استفاده از متد getPackageInfo و دستور try catch در لیست برنامه‌های نصب شده روی دستگاه اندرویدی نام پکیج برنامه مدنظر را جستجو می‌کنیم. چنانچه نام پکیج یا همان Package name موردنظر موجود بود کدهای درون بلاک try و در غیر اینصورت قسمت catch اجرا خواهد شد:</p>
<pre class="brush: java; title: ; notranslate">
private boolean installedOrNot(Context cnt, String packageName) {

    PackageManager pm = cnt.getPackageManager();
    boolean appInstalled;

    try {
        pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
        appInstalled = true;
        Toast.makeText(cnt, &quot;برنامه قبلا نصب شده است&quot;, Toast.LENGTH_LONG).show();
    } catch (PackageManager.NameNotFoundException e) {
        appInstalled = false;
        Toast.makeText(cnt, &quot; برنامه قبلا نصب نشده است &quot;, Toast.LENGTH_LONG).show();
    }
    return appInstalled;

}
</pre>
<p>مطابق با کد فوق ابتدا یک متد از جنس boolean و با نام دلخواه installedOrNot درون اکتیویتی و بعد از متد onCreate تعریف کرده‌ام. این متد دو پارامتر دارد که اولی کانتکست و دومی از جنس رشته تعریف شده.<br />
درون این متد ابتدا یک شیء از کلاس PackageManager با نام دلخواه pm ساخته شده است. سپس یک متغیر از جنس boolean با نام appInstalled تعریف شده.<br />
داخل بلاک try و با استفاده از متد getPackageInfo نام پکیج بررسی می‌شود. چنانچه پکیج و به عبارتی اپلیکیشن مدنظر قبلا نصب شده باشد، متغیر appInstalled مقدار true گرفته و کدهای بعد از آن اجرا می‌شود که من در اینجا یک پیغام Toast تعریف کردم.<br />
برای بلاک catch یک Exception (استثناء) از نوع PackageManager.NameNotFoundException تعریف شده که از نامش پیداست مربوط به پیدا نشدن نام است (NameNotFound). یعنی هنگامی که نام پکیج یافت نشد این استثناء اجرا می‌شود که در اینصورت مقدار false برای متغیر appInstalled در نظر گرفته شده و مانند قسمت قبل یک پیغام برای کاربر ظاهر می‌شود که اعلام می‌کند برنامه مدنظر نصب نشده است.<br />
در نهایت از آنجایی که تابع از جنس boolean بود لازم است appInstalled را return کنیم.<br />
حالا متد را در onCreate تعریف می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
installedOrNot(getApplicationContext(), &quot;com.farsitel.bazaar&quot;);
</pre>
<p>پارامتر نخست مربوط به کانتکست است که قبلا آشنا شدیم و نیاز به توضیح مجدد نیست. برای پارامتر دوم نام پکیج موردنظرم را در قالب یک String وارد می‌کنم. من Package name مارکت ایرانی بازار را تعریف کرده‌ام. تا اینجای کار اکتیویتی ما به صورت زیر تکمیل شده است:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.appinstalled;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        installedOrNot(getApplicationContext(), &quot;com.farsitel.bazaar&quot;);

    }

    private boolean installedOrNot(Context cnt, String packageName) {

        PackageManager pm = cnt.getPackageManager();
        boolean appInstalled;

        try {
            pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
            appInstalled = true;
            Toast.makeText(cnt, &quot;برنامه قبلا نصب شده است&quot;, Toast.LENGTH_LONG).show();
        } catch (PackageManager.NameNotFoundException e) {
            appInstalled = false;
            Toast.makeText(cnt, &quot;برنامه قبلا نصب نشده است&quot;, Toast.LENGTH_LONG).show();
        }
        return appInstalled;

    }

}
</pre>
<p>خب! پروژه را اجرا و تست می‌کنیم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/appinstalled/app_not_installed_already_using_getPackageInfo_method.png" alt="بررسی نصب بودن یا نبودن یک اپلیکیشن در اندروید توسط getPackageInfo" /></p>
<p>به محض اجرای برنامه روی شبیه ساز، پیغام &#8220;برنامه قبلا نصب نشده است&#8221; مشاهده شد. یعنی اپلیکیشن بازار روی این دیوایس نصب نیست.<br />
حالا برنامه بازار را روی دستگاه نصب کرده و مجدد پروژه را اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/appinstalled/app_installed_already.png" alt="برنامه قبلا روی دستگاه اندرویدی نصب شده است" /></p>
<p>برخلاف قسمت قبل اینبار پیغام &#8220;برنامه قبلا نصب شده است&#8221; ظاهر شد. بنابراین کد ما به درستی عمل می‌کند.<br />
برای مثال پرداخت درون برنامه‌ای که در ابتدای مبحث عنوان شد، بجای Toast اول می‌توانیم عملیات مربوط به انتقال به مارکت و ثبت تراکنش مالی توسط کاربر را انجام دهیم. همچنین بجای Toast دوم هم می‌توان کاربر را به صفحه دانلود مارکت هدایت کرد یا اینکه در قالب همین پیغام اعلام شود لازم است مارکت x روی دستگاه نصب شود. این بخش را نیز در انتهای مبحث بررسی می‌کنیم.</p>
<h3 id="using-intent">روش دوم: استفاده از intent</h3>
<p>قبلا در جلسه <a href="http://android-studio.ir/intent/">آموزش intent در اندروید</a> با اینتنت‌ها آشنا شدیم. در این روش با استفاده از intent، نام پکیج کلیه برنامه‌های نصب شده روی دستگاه اندرویدی را در یک لیست ذخیره کرده و سپس پکیج موردنظر را درون آن لیست جستجو می‌کنیم.<br />
ابتدا کد مربوط به قسمت قبل را کامنت می‌کنم تا غیر فعال شود. سپس دو متد به صورت زیر به اکتیویتی اضافه می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
protected List&lt;String&gt; getInstalledAppsPackage() {

    Intent mIntent = new Intent(Intent.ACTION_MAIN, null);
    mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    List&lt;ResolveInfo&gt; resolveInfoList = getPackageManager().queryIntentActivities(mIntent, 0);
    List&lt;String&gt; packageNameList = new ArrayList&lt;&gt;();

    for (ResolveInfo resolveInfo: resolveInfoList) {
        ActivityInfo actInfo = resolveInfo.activityInfo;
        packageNameList.add(actInfo.applicationInfo.packageName);
    }

    return packageNameList;

}

private void appStatus() {

    final List&lt;String&gt; installedPackages = getInstalledAppsPackage();

    if (installedPackages.contains(mPackageName)) {
        Toast.makeText(this, &quot;برنامه قبلا نصب شده است&quot;, Toast.LENGTH_LONG).show();
    } else {
        Toast.makeText(this, &quot;برنامه قبلا نصب نشده است&quot;, Toast.LENGTH_LONG).show();
    }

}
</pre>
<p>ملاحظه می‌کنید ابتدا یک تابع از جنس List<String> و نام دلخواه getInstalledAppsPackage تعریف شده. درون این تابع، اکتیویتی لانچر هر یک از برنامه‌های نصب شده روی دستگاه توسط Intent.CATEGORY_LAUNCHER مشخص می‌شود. سپس Package name آنها توسط actInfo.applicationInfo.packageName موجود در حلقه for، دریافت و به لیست packageNameList اضافه (add) می‌گردد.<br />
در ادامه کار یک متد دیگر با نام دلخواه appStatus تعریف شده که درون آن بررسی می‌کنیم آیا این لیست شامل mPackageName ای که تعریف کرده‌ایم هست (contains) یا نه. چنانچه این شرط برقرار بود، پیغام نصب و در غیر اینصورت پیغام عدم نصب نمایش داده خواهد شد.<br />
در نهایت، متد appStatus را در onCreate فراخوانی می‌کنم.<br />
برای تست این کد، نام پکیج مربوط به پلیر VLC را در بدنه اکتیویتی تعریف می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
private String mPackageName = &quot;org.videolan.vlc&quot;;
</pre>
<p>تا اینجای کار اکتیویتی به صورت زیر تکمیل شده است:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.appinstalled;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private String mPackageName = &quot;org.videolan.vlc&quot;;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // installedOrNot(getApplicationContext(), &quot;com.farsitel.bazaar&quot;);
        appStatus();

    }

    /* private boolean installedOrNot(Context cnt, String packageName) {

        PackageManager pm = cnt.getPackageManager();
        boolean appInstalled;

        try {
            pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
            appInstalled = true;
            Toast.makeText(cnt, &quot;برنامه قبلا نصب شده است&quot;, Toast.LENGTH_LONG).show();
        } catch (PackageManager.NameNotFoundException e) {
            appInstalled = false;
            Toast.makeText(cnt, &quot; برنامه قبلا نصب نشده است&quot;, Toast.LENGTH_LONG).show();
        }
        return appInstalled;

    } */


    protected List&lt;String&gt; getInstalledAppsPackage() {

        Intent mIntent = new Intent(Intent.ACTION_MAIN, null);
        mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
        mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        List&lt;ResolveInfo&gt; resolveInfoList = getPackageManager().queryIntentActivities(mIntent, 0);
        List&lt;String&gt; packageNameList = new ArrayList&lt;&gt;();

        for (ResolveInfo resolveInfo: resolveInfoList) {
            ActivityInfo actInfo = resolveInfo.activityInfo;
            packageNameList.add(actInfo.applicationInfo.packageName);
        }

        return packageNameList;

    }

    private void appStatus() {

        final List&lt;String&gt; installedPackages = getInstalledAppsPackage();

        if (installedPackages.contains(mPackageName)) {
            Toast.makeText(this, &quot;برنامه قبلا نصب شده است&quot;, Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this, &quot;برنامه قبلا نصب نشده است&quot;, Toast.LENGTH_LONG).show();
        }

    }

}
</pre>
<p>با اجرای مجدد پروژه، پیغام عدم نصب نمایش داده خواهد شد زیرا این پلیر روی دستگاه نصب نیست.<br />
در قسمت بعد نحوه هدایت کاربر به صفحه نصب یا دریافت برنامه را بررسی می‌کنیم.</p>
<h3 id="open-app-store-or-a-web-page">هدایت کاربر به صفحه نصب برنامه در مارکت یا یک صفحه وب</h3>
<p>چنانچه قصد داشته باشیم برای راحتی کاربر، در صورت نصب نبودن برنامه مورد نیاز به سرعت او را به صفحه دریافت و نصب برنامه هدایت کنیم، با استفاده از یک دستور try catch این کار را انجام می‌دهیم. قصد دارم برای روش دوم که مربوط به برنامه VLC بود، کدی را بنویسم که در صورت نصب نبودن آن، ابتدا صفحه مربوط به آن در مارکت اندرویدی پیش فرض روی دستگاه باز شده و چنانچه مارکتی وجود نداشت، کاربر به یک آدرس اینترنتی هدایت شود.<br />
یک متد دیگر با نام installApp به اکتیویتی اضافه کرده و به صورت زیر تکمیل می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
private void installApp() {

    try {
        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(&quot;market://details?id=&quot; + mPackageName)));
    } catch (android.content.ActivityNotFoundException ane) {
        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(&quot;https://play.google.com/store/apps/details?id=&quot; + mPackageName)));
    }

}
</pre>
<p>برای انتقال کاربر به صفحه معرفی یک برنامه در مارکت‌های اندرویدی، uri به صورت </p>
<p style="text-align: left; direction: ltr;">
market://details?id=PACKAGE_NAME
</p>
<p>تعریف می‌شود. برای قسمت دوم شرط هم من صفحه معرفی برنامه در پلی استور را قرار دادم. نحوه تعیین آدرس صفحه برنامه‌ها در پلی استور به اینصورت است:</p>
<p style="text-align: left; direction: ltr;">
https://play.google.com/store/apps/details?id=PACKAGE_NAME
</p>
<p>حالا متد را در قسمت دوم شرط مربوط به appStatus فراخوانی می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
if (installedPackages.contains(mPackageName)) {
    Toast.makeText(this, &quot;برنامه قبلا نصب شده است&quot;, Toast.LENGTH_LONG).show();
} else {
    Toast.makeText(this, &quot;برنامه قبلا نصب نشده است&quot;, Toast.LENGTH_LONG).show();
    installApp();
}
</pre>
<p>دوباره پروژه را اجرا می‌کنم. با توجه به نصب نبودن پلیر VLC روی این دیوایس، صفحه نصب آن در مارکت بازار باز شد:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/appinstalled/open_play_store_to_install_app.png" alt="هدایت کاربر به اپ استور برای نصب برنامه اندرویدی" /></p>
<p>فروشگاه پلی استور به صورت پیش فرض روی دیوایس‌های <a href="http://android-studio.ir/install-genymotion/" target="_blank" rel="noopener noreferrer">شبیه ساز اندرویدی Genymotion</a> نصب نیست و با توجه به اینکه در قسمت نخست آموزش، برنامه کافه بازار را نصب کرده بودیم، در اینجا صفحه نصب پلیر VLC در این مارکت باز شد.<br />
در قدم نهایی، مارکت کافه بازار را حذف و پروژه را مجدد اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/appinstalled/open_app_page_in_google_play_web.png" alt="باز کردن صفحه نصب برنامه در گوگل پلی" /></p>
<p>مشاهده می‌کنید صفحه معرفی و نصب برنامه در play.google.com در مرورگر دستگاه باز شد.<br />
کد نهایی اکتیویتی:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.appinstalled;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private String mPackageName = &quot;org.videolan.vlc&quot;;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // installedOrNot(getApplicationContext(), &quot;com.farsitel.bazaar&quot;);
        appStatus();

    }

    /* private boolean installedOrNot(Context cnt, String packageName) {

        PackageManager pm = cnt.getPackageManager();
        boolean appInstalled;

        try {
            pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
            appInstalled = true;
            Toast.makeText(cnt, &quot; برنامه قبلا نصب نشده است&quot;, Toast.LENGTH_LONG).show();
        } catch (PackageManager.NameNotFoundException e) {
            appInstalled = false;
            Toast.makeText(cnt, &quot;برنامه قبلا نصب نشده است&quot;, Toast.LENGTH_LONG).show();
        }
        return appInstalled;

    } */

    protected List&lt;String&gt; getInstalledAppsPackage() {

        Intent mIntent = new Intent(Intent.ACTION_MAIN, null);
        mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
        mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
        List&lt;ResolveInfo&gt; resolveInfoList = getPackageManager().queryIntentActivities(mIntent, 0);
        List&lt;String&gt; packageNameList = new ArrayList&lt;&gt;();

        for (ResolveInfo resolveInfo: resolveInfoList) {
            ActivityInfo actInfo = resolveInfo.activityInfo;
            packageNameList.add(actInfo.applicationInfo.packageName);
        }

        return packageNameList;

    }

    private void appStatus() {

        final List&lt;String&gt; installedPackages = getInstalledAppsPackage();

        if (installedPackages.contains(mPackageName)) {
            Toast.makeText(this, &quot;برنامه قبلا نصب شده است&quot;, Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(this, &quot;برنامه قبلا نصب نشده است&quot;, Toast.LENGTH_LONG).show();
            installApp();
        }

    }

    private void installApp() {

        try {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(&quot;market://details?id=&quot; + mPackageName)));
        } catch (android.content.ActivityNotFoundException ane) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(&quot;https://play.google.com/store/apps/details?id=&quot; + mPackageName)));
        }

    }

}
</pre>
<p>موفق و پیروز باشید.</p>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://developer.android.com/reference/android/content/pm/ResolveInfo" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/content/pm/ResolveInfo</a><br />
<a href="https://developer.android.com/reference/android/content/pm/PackageManager" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/content/pm/PackageManager</a><br />
<a href="https://developer.android.com/reference/android/content/Intent" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/content/Intent</a>
</p>
<p class="source">توجه : سورس پروژه درون پوشه Exercises قرار دارد</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش به همراه سورس پروژه</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۱۴<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۱ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/InstalledApps.zip" class="button green edd-submit">دانلود رایگان با حجم ۱ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/InstalledApps.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/check-if-an-android-application-is-installed-or-not/">بررسی نصب بودن برنامه در اندروید</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/check-if-an-android-application-is-installed-or-not/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>مدیریت و غیر فعال کردن چرخش صفحه نمایش</title>
		<link>https://android-studio.ir/disable-android-screen-activity-orientation-change/</link>
					<comments>https://android-studio.ir/disable-android-screen-activity-orientation-change/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Sat, 23 May 2020 12:25:20 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=182138</guid>

					<description><![CDATA[<p>در این جلسه به بررسی نحوه مدیریت چرخش صفحه نمایش یا به عبارتی چرخش اکتیویتی برنامه اندرویدی هنگام تغییر جهت قرارگیری دیوایس می‌پردازیم. یا به زبان ساده تر، غیر فعال کردن چرخش صفحه نمایش در برنامه اندرویدی از حالت عمودی (Portrait) به افقی (Landscape) و بلعکس. آنچه در این آموزش می‌خوانید: چرخش صفحه نمایش (اکتیویتی) [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/disable-android-screen-activity-orientation-change/">مدیریت و غیر فعال کردن چرخش صفحه نمایش</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p>در این جلسه به بررسی نحوه مدیریت چرخش صفحه نمایش یا به عبارتی چرخش اکتیویتی برنامه اندرویدی هنگام تغییر جهت قرارگیری دیوایس می‌پردازیم. یا به زبان ساده تر، غیر فعال کردن چرخش صفحه نمایش در برنامه اندرویدی از حالت عمودی (Portrait) به افقی (Landscape) و بلعکس.</p>
<div class="title-box">
<b>آنچه در این آموزش می‌خوانید:</b></p>
<ul>
<li><a href="#android-screen-and-activity-orientation-change">چرخش صفحه نمایش (اکتیویتی) برنامه اندرویدی</a></li>
<li><a href="#disable-android-screen-and-activity-orientation-change">غیر فعال کردن چرخش صفحه نمایش</a></li>
<li><a href="#disable-android-screen-and-activity-orientation-change-project">پروژه مدیریت و غیر فعال کردن چرخش صفحه نمایش (اکتیویتی) در اندروید</a>
<ul>
<li><a href="#disable-android-screen-orientation-change-using-screenOrientation-attribute">روش اول: غیر فعال کردن چرخش اکتیویتی در مانیفست پروژه</a></li>
<li><a href="#disable-android-screen-orientation-change-using-setRequestedOrientation-method">روش دوم: غیر فعال کردن چرخش اکتیویتی توسط کد جاوا</a></li>
</ul>
</li>
</ul>
</div>
<h2 id="android-screen-and-activity-orientation-change">چرخش صفحه نمایش (اکتیویتی) برنامه اندرویدی</h2>
<p>به نام خدا. حتما در کار با برنامه‌های موبایلی و بخصوص اندرویدی، با قابلیت چرخش صفحه نمایش هنگام چرخش و تغییر وضعیت دستگاه مواجه شده‌اید. هنگام چرخش موبایل یا تبلت توسط کاربر، وضعیت جدید دستگاه توسط سنسور تعبیه شده در آن تشخیص داده شده و سیستم عامل نیز وضعیت جهت صفحه نمایش و به عبارت دیگر، اکتیویتی در حال اجرای برنامه را بر اساس جهت قرار گیری دستگاه تغییر می‌دهد.</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/manageorientation/android_screen_activity_orientation.png" alt="چرخش صفحه نمایش یا اکتیویتی در اندروید" /></p>
<p>به طور خلاصه، هنگامی که دستگاه در حالت افقی یا Landscape قرار گرفته باشد، اکتیویتی نیز به حالت افقی نمایش داده می‌شود. و همچنین هنگامی که دستگاه در حالت عمودی یا Portrait قرار گرفته باشد، اکتیویتی نیز به حالت عمودی نمایش داده می‌شود.</p>
<h2 id="disable-android-screen-and-activity-orientation-change">غیر فعال کردن چرخش صفحه نمایش</h2>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/manageorientation/disable_android_screen_activity_orientation.png" alt="غیر فعال کردن چرخش صفحه نمایش (اکتیویتی) در برنامه نویسی اندروید" /></p>
<p>اما گاهی اوقات لازم است این قابلیت در برنامه و یا برخی از اکتیویتی‌های آن غیر فعال شده و اکتیویتی صرفا در یک حالت ثابت نمایش داده شود. از نمونه‌های بارز آن می‌توان به بازی‌های موبایلی اشاره کرد که غالباً در یک جهت ثابت تعریف شده‌اند و با چرخش دستگاه، جهت صفحه تغییر نمی‌کند.<br />
بجز بازی‌ها در برخی دیگر از برنامه‌ها نیز بنا به ماهیت برنامه و محتویات آن ممکن است بخواهیم وضعیت اکتیویتی را در یک حالت مشخص قفل کنیم. برای مثال به اپلیکیشن‌های بانکی اشاره می‌کنم که عمدتا در حالت عمودی طراحی شده و با چرخش موبایل یا تبلت همچنان در حالت عمودی قرار دارند.<br />
در مبحث <a href="http://android-studio.ir/get-android-screen-orientation/">آموزش تشخیص جهت صفحه نمایش دستگاه اندرویدی</a> نحوه بررسی وضعیت فعلی صفحه نمایش پرداختیم. در این جلسه به نحوه غیر فعال کردن چرخش صفحه نمایش در اندروید و همچنین انواع روش‌ها و جزئیات آنها می‌پردازم.</p>
<h2 id="disable-android-screen-and-activity-orientation-change-project">پروژه مدیریت و غیر فعال کردن چرخش صفحه نمایش (اکتیویتی) در اندروید</h2>
<p>می‌خواهیم مدیریت و همچنین غیر فعال کردن چرخش صفحه نمایش در اندروید را در قالب یک پروژه ساده بررسی کنیم.<br />
طبق مبحث <a href="http://android-studio.ir/create-android-project-and-its-structure/">آموزش ساخت پروژه در اندروید استودیو</a> یک پروژه اندرویدی با نام Manage Orientation می‌سازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم.<br />
برای غیر فعال کردن چرخش صفحه نمایش دو راه وجود دارد. در روش نخست در مانیفست پروژه یک ویژگی (attribute) به تگ اکتیویتی مربوطه اضافه می‌شود اما در روش دوم اینکار مستقیما درون اکتیویتی و در متد onCreate آن انجام می‌شود.<br />
قبل از هر چیز پروژه را اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/manageorientation/android_portrait_screen.png" alt="صفحه دستگاه اندرویدی در حالت عمودی یا Portrait" /></p>
<p>به طور پیش فرض، دیوایس در حالت عمودی قرار گرفته که صفحه نمایش و اکتیویتی نیز در همین وضعیت قرار دارد. حالا دستگاه را در وضعیت افقی قرار می‌دهم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/manageorientation/change_android_device_and_screen_to_landscape.png" alt="قرار دادن صفحه (اکتیویتی) اندرویدی در حالت افقی یا Landscape" /></p>
<p>ملاحظه می‌کنید اکتیویتی نیز مطابق با وضعیت دستگاه در حالت افقی قرار گرفته و یک چرخش ۹۰ درجه‌ای انجام شده است.<br />
در ادامه مبحث با استفاده از دو روش، این چرخش را مدیریت و غیر فعال می‌کنیم.</p>
<h3 id="disable-android-screen-orientation-change-using-screenOrientation-attribute">روش اول: غیر فعال کردن چرخش اکتیویتی در مانیفست پروژه</h3>
<p>در این روش صرفا با اضافه کردن ویژگی android:screenOrientation به تگ اکتیویتی مدنظر در فایل AndroidManifest.xml تعیین می‌کنیم وضعیت نمایش اکتیویتی روی چه حالتی باید ثابت شود؛ افقی یا عمودی. به کد زیر دقت کنید:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;activity android:name=&quot;.MainActivity&quot;
    android:screenOrientation=&quot;portrait&quot;&gt;
    &lt;intent-filter&gt;
        &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;

        &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
    &lt;/intent-filter&gt;
&lt;/activity&gt;
</pre>
<p>برای اکتیویتی MainActivity ویژگی android:screenOrientation با مقدار portrait تعریف کردم. انتظار دارم با اجرای مجدد پروژه، اکتیویتی در حالت افقی دستگاه نیز به صورت portrait نمایش داده شود که با اجرای پروژه، نتیجه مطلوب گرفته می‌شود:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/manageorientation/set_portrait_or_landscape_orientation_using_screenOrientation_attribute.png" alt="غیر فعال کردن چرخش صفحه نمایش دستگاه اندرویدی توسط ویژگی screenOrientation در مانیفست پروژه" /></p>
<p>طبیعتا انتظار داریم با تعریف مقدار landscape بجای portrait، اکتیویتی در هر دو حالت قرار گیری دستگاه به صورت افقی نمایش داده شود:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/manageorientation/set_landscape_for_screenOrientation_attribute.png" alt="قرار دادن اکتیویتی در حالت افقی (Landscape) توسط screenOrientation" /></p>
<p>اما هنوز محدودیتی داریم به اینصورت که ثابت شدن وضعیت قرارگیری اکتیویتی فقط در جهت پیش فرض دیوایس انجام می‌شود. یعنی چنانچه مقدار landscape تعریف شود، اکتیویتی فقط در یک جهت افقی دستگاه ثابت می‌ماند و چنانچه کاربر دستگاه را ۱۸۰ درجه بچرخاند به طوری که دستگاه مجدد در حالت افقی قرار گیرد، اکتیویتی به صورت وارونه نمایش داده خواهد شد.<br />
برای حل این مشکل لازم است بجای مقادیر portrait و landscape به ترتیب از sensorPortrait و sensorLandscape استفاده شود. به عبارتی پیشوند sensor به ابتدای مقادیر اضافه شده است. تفاوت این دو گزینه در تصویر زیر مشهود است:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/manageorientation/landscape_vs_sensorLandscape.png" alt="تفاوت تعیین مقدار  landscape و sensorLandscape در screenOrientation" /></p>
<p>آیتم دیگری نیز وجود دارد که حالت افقی یا عمودی صفحه را می‌توان در جهت عکس حالت پیش فرض افقی یا عمودی دستگاه قرار داد. به عبارتی، صفحه را در وضعیت ۱۸۰ درجه‌ای نسبت به حالت افقی یا عمودی پیش فرض دستگاه قرار می‌دهد. برای اینکار از پیشوند reverse (به معنی معکوس) استفاده می‌کنیم؛ reversePortrait و reverseLandscape. البته بعید می‌دانم این گزینه کاربرد زیادی داشته باشد!<br />
گزینه‌ دیگری که سیستم عامل اندروید در اختیار توسعه دهندگان قرار داده، آیتم‌های userPortrait و userLandscape هستند که از نام آن مشخص است نحوه قرار گیری حالت افقی یا عمودی را بر اساس تنظیماتی که کاربر روی دستگاه خود انجام داده تنظیم می‌شود. یعنی اینکه حالت افقی صفحه در جهت حالت افقی دستگاه باشد یا جهت عکس آن (چرخش ۱۸۰ درجه‌ای) به سلیقه کاربر بستگی خواهد داشت.</p>
<div class="alert alert-warning">
<span class="notice">نکته:</span> معمولا به طور پیش فرض در اکثر دستگاه‌های اندرویدی، userLandscape و userPortrait همان نقش sensorLandscape و sensorPortrait را ایفا می‌کنند.
</div>
<h3 id="disable-android-screen-orientation-change-using-setRequestedOrientation-method">روش دوم: غیر فعال کردن چرخش اکتیویتی توسط کد جاوا</h3>
<p>در روش دوم، تعیین نحوه چرخش اکتیویتی به طور مستقیم درون خود اکتیویتی و متد onCreate آن انجام می‌شود. برای اینکار از متد setRequestedOrientation استفاده می‌کنیم:</p>
<pre class="brush: java; title: ; notranslate">
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
</pre>
<p>در کد فوق حالت SCREEN_ORIENTATION_SENSOR_LANDSCAPE تعیین شده که با حالت sensorLandscape تعریف شده در تگ اکتیویتی در مانیفست برابر است. سایر آیتم‌ها نیز مشابه آنچه قبلا توضیح دادیم در دسترس هستند:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/manageorientation/setRequestedOrientation_fields.png" alt="غیر فعال کردن چرخش صفحه نمایش (اکتیویتی) در اندروید توسط متد setRequestedOrientation" /></p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.manageorientation;

import androidx.appcompat.app.AppCompatActivity;

import android.content.pm.ActivityInfo;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);

    }
}
</pre>
<p>موفق و پیروز باشید.</p>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://developer.android.com/guide/topics/manifest/activity-element" target="_blank" rel="noopener noreferrer">https://developer.android.com/guide/topics/manifest/activity-element</a>
</p>
<p class="source">توجه : سورس پروژه درون پوشه Exercises قرار دارد</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش به همراه سورس پروژه</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۹<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۱ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/Disable_orientation_change.zip" class="button green edd-submit">دانلود رایگان با حجم ۱ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/Disable_orientation_change.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/disable-android-screen-activity-orientation-change/">مدیریت و غیر فعال کردن چرخش صفحه نمایش</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/disable-android-screen-activity-orientation-change/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title>حذف، نمایش و مدیریت کیبورد اندروید</title>
		<link>https://android-studio.ir/hide-and-show-and-manage-android-soft-keyboard/</link>
					<comments>https://android-studio.ir/hide-and-show-and-manage-android-soft-keyboard/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Sat, 09 May 2020 07:15:54 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<category><![CDATA[معرفی کتابخانه]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=181288</guid>

					<description><![CDATA[<p>در این جلسه از سری مباحث آموزش برنامه نویسی اندروید قصد دارم نحوه مخفی کردن و نمایش کیبورد (Keyboard) سیستم عامل اندروید را بررسی کنم. همچنین در ادامه آموزش از کتابخانه‌ای استفاده خواهیم کرد که علاوه بر مخفی کردن و نمایش کیبورد، رویداد مربوطه را به ما اعلام می‌کند. آنچه در این آموزش می‌خوانید: مدیریت [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/hide-and-show-and-manage-android-soft-keyboard/">حذف، نمایش و مدیریت کیبورد اندروید</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p>در این جلسه از سری مباحث <a href="http://android-studio.ir" target="_blank" rel="noopener noreferrer">آموزش برنامه نویسی اندروید</a> قصد دارم نحوه مخفی کردن و نمایش کیبورد (Keyboard) سیستم عامل اندروید را بررسی کنم. همچنین در ادامه آموزش از کتابخانه‌ای استفاده خواهیم کرد که علاوه بر مخفی کردن و نمایش کیبورد، رویداد مربوطه را به ما اعلام می‌کند.</p>
<div class="title-box">
<b>آنچه در این آموزش می‌خوانید:</b></p>
<ul>
<li><a href="#android-soft-keyboard-management">مدیریت کیبورد اندروید</a></li>
<li><a href="#hide-android-keyboard-using-InputMethodManager-class">حذف و نمایش کیبورد اندروید توسط کلاس InputMethodManager</a></li>
<li><a href="#manage-android-keyboard-using-KeyboardVisibilityEvent-library">مدیریت کیبورد اندروید توسط کتابخانه KeyboardVisibilityEvent</a>
<ul>
<li><a href="#hide-and-show-android-keyboard">مخفی کردن و نمایش کیبورد</a></li>
<li><a href="#keyboard-hide-and-show-event-listener">گوش دادن به رویداد حذف یا ظاهر شدن کیبورد</a></li>
</ul>
</li>
</ul>
</div>
<h2 id="android-soft-keyboard-management">مدیریت کیبورد اندروید</h2>
<p>به نام خدا. اگر برنامه نویس و توسعه دهنده موبایل و بخصوص اندروید هستید به احتمال زیاد با چالش نمایش کیبورد هنگام عدم نیاز به آن، روبرو شده‌اید. یا اینکه بخشی از برنامه شما باید به صورتی پیاده سازی شود که یک یا چند عملیات بر اساس وضعیت کیبورد، یعنی مخفی یا نمایان بودن آن انجام شود.</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/keyboardmanagement/android_soft_keyboard_management.png" alt="مدیریت، حذف و نمایش کیبورد در برنامه نویسی اندروید" /></p>
<p>در این آموزش ابتدا نحوه حذف کیبورد توسط کلاس InputMethodManager را بررسی کرده و در قسمت بعد، با استفاده از کتابخانه KeyboardVisibilityEvent علاوه بر امکان حذف و نمایش کیبورد، یک شنونده (Listener) نیز برای شنود رویداد حذف و ظاهر شدن کیبورد تعریف می‌کنیم.<br />
طبق مبحث <a href="http://android-studio.ir/create-android-project-and-its-structure/">آموزش ساخت پروژه در اندروید استودیو</a> یک پروژه اندرویدی با نام KeyboardManagement می‌سازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم.</p>
<h2 id="hide-android-keyboard-using-InputMethodManager-class">حذف و نمایش کیبورد اندروید توسط کلاس InputMethodManager</h2>
<p>برای مثال یک فرم عضویت را درنظر بگیرید. طبیعتا بعد از اینکه کاربر اطلاعات لازم را وارد کرده و روی دکمه ثبت نام ضربه بزند، نیازی به باز ماندن کیبورد نیست و عدم مخفی شدن آن، برای کاربر ایجاد مزاحمت کرده که از لحاظ تجربه کاربری (UX) یک آیتم منفی محسوب می‌گردد. بنابراین باید کاری کنیم تا به محض کلیک کردن کاربر روی Button ثبت نام، کیبورد مخفی شود.<br />
در layout اکتیویتی سه ویجت از نوع TextView، EditText و Button تعریف می‌کنم:</p>
<p><span class="filename">activity_main.xml</span></p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;vertical&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;TextView
        android:id=&quot;@+id/txt_result&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_gravity=&quot;center_horizontal&quot;
        android:text=&quot;متن پیش فرض&quot;
        android:textSize=&quot;22sp&quot; /&gt;

    &lt;EditText
        android:id=&quot;@+id/txt_input&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot; /&gt;

    &lt;Button
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_gravity=&quot;center_horizontal&quot;
        android:id=&quot;@+id/submit_btn&quot;
        android:text=&quot;ثبت&quot; /&gt;

&lt;/LinearLayout&gt;
</pre>
<p>در مرحله بعد یک Listener برای دکمه تعریف می‌کنم به صورتی که متن وارد شده در EditText را روی TextView ست می‌کند:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.keyboardmanagement;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView txtResult;
    EditText txtInput;
    Button btnSubmit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtResult = findViewById(R.id.txt_result);
        txtInput = findViewById(R.id.txt_input);
        btnSubmit = findViewById(R.id.submit_btn);

        btnSubmit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                String userText = txtInput.getText().toString();
                txtResult.setText(userText);

            }
        });

    }
}
</pre>
<p>پروژه را اجرا کرده و یک متن آزمایشی ثبت می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/keyboardmanagement/run_android_keyboard_hide_&#038;_show_project.png" alt="اجرای پروژه اندرویدی مخفی کردن و نمایش کیبورد نرم افزاری سیستم عامل اندروید" /></p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/keyboardmanagement/android_keyboard_hide_&#038;_show_project.png" alt="پروژه اندرویدی مخفی کردن و نمایش کیبورد نرم افزاری سیستم عامل اندروید" /></p>
<p>ملاحظه می‌کنید بعد از پایان کار همچنان کیبورد نمایش داده می‌شود.<br />
برای مخفی کردن کیبورد یک متد با نام دلخواه closeKeyboard به اکتیویتی و بعد از متد onCreate اضافه می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
private void closeKeyboard() {

    View focusView = this.getCurrentFocus();
    if (focusView != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
    }

}
</pre>
<p>در خط نخست، view ای که روی آن focus (تمرکز) شده را در یک متغیر از جنس View ذخیره می‌کنم. من نام این متغیر را focusView انتخاب کردم. بنابراین در پروژه فعلی ما، هنگامی که کاربر روی EditText کلیک می‌کند، این view ذخیره می‌شود. در ادامه یک شرط تعریف شده که ابتدا بررسی می‌کند focusView برابر با null نباشد. یعنی چنانچه هیچ view ای در حالت focus قرار نگرفته باشد، کدهای درون این شرط اجرا نخواهد شد.<br />
درون این شرط یک نمونه از کلاس InputMethodManager با نام imm ساخته‌ام. در خط آخر، توسط متد hideSoftInputFromWindow این کلاس، SoftInput یا به عبارتی همان کیبورد (ورودی نرم افزاری) مخفی (hide) می‌شود.<br />
در نهایت، متد را در Listener دکمه فراخوانی می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
btnSubmit.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {

        String userText = txtInput.getText().toString();
        txtResult.setText(userText);
        closeKeyboard();

    }
});
</pre>
<p>کد کامل اکتیویتی:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.keyboardmanagement;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView txtResult;
    EditText txtInput;
    Button btnSubmit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtResult = findViewById(R.id.txt_result);
        txtInput = findViewById(R.id.txt_input);
        btnSubmit = findViewById(R.id.submit_btn);

        btnSubmit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                String userText = txtInput.getText().toString();
                txtResult.setText(userText);
                closeKeyboard();

            }
        });

    }

    private void closeKeyboard() {

        View focusView = this.getCurrentFocus();
        if (focusView != null) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
        }

    }

}
</pre>
<p>پروژه را دوباره اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/keyboardmanagement/hide_soft_keyboard_using_InputMethodManager_class.png" alt="مخفی یا حذف کردن کیبورد در اندروید توسط کلاس InputMethodManager" /></p>
<p>حالا بلافاصله بعد از کلیک روی دکمه ثبت، کیبوردی که به واسطه focus شدن روی EditText باز شده بود، مخفی شد.</p>
<h2 id="manage-android-keyboard-using-KeyboardVisibilityEvent-library">مدیریت کیبورد اندروید توسط کتابخانه KeyboardVisibilityEvent</h2>
<p>در قسمت قبل بدون استفاده از کتابخانه و صرفا توسط کلاس داخلی اندروید، کیبورد را مخفی کردیم. اما در این قسمت برای مدیریت کیبورد نرم افزاری اندروید از کتابخانه KeyboardVisibilityEvent استفاده می‌کنیم که علاوه بر امکان نمایش دادن یا مخفی کردن کیبورد، به رویداد باز یا بسته شدن کیبورد هم می‌توانیم گوش دهیم. یعنی این کتابخانه مخفی و ظاهر شدن کیبورد را گزارش می‌دهد که در برخی موارد ممکن است توسعه دهنده به آن نیاز داشته باشد.<br />
آدرس معرفی کتابخانه در گیت هاب:</p>
<p style="text-align: left; direction: ltr;">
<a href="https://github.com/yshrsmz/KeyboardVisibilityEvent" target="_blank" rel="noopener noreferrer">https://github.com/yshrsmz/KeyboardVisibilityEvent</a>
</p>
<p>طبق توضیحات صفحه گیت هاب کتابخانه برای افزودن آن به پروژه باید خط زیر را در build.gradle اضافه کنم:</p>
<pre class="brush: java; title: ; notranslate">
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:LATEST_VERSION'
</pre>
<div class="alert alert-warning">
<span class="notice">نکته:</span> بجای LATEST_VERSION باید عدد آخرین نسخه کتابخانه را جایگزین کنیم. نسخه فعلی کتابخانه (در زمان تهیه این آموزش) در قسمت The latest version نمایش داده شده است. یعنی ۳٫۰٫۰-RC2:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/keyboardmanagement/add_KeyboardVisibilityEvent_library_to_androidstudio_project.png" alt="افزودن کتابخانه KeyboardVisibilityEvent به پروژه اندروید در اندروید استودیو" /></p>
<p>البته نیاز به نوشتن دستی ورژن کتابخانه نیست و با کلیک روی آن، به <a href="https://bintray.com/yshrsmz/maven/keyboardvisibilityevent/_latestVersion">این لینک</a> هدایت می‌شویم که در قسمت Maven build settings (گزینه Gradle) خط زیر نمایش داده می‌شود:</p>
<pre class="brush: java; title: ; notranslate">
implementation 'net.yslibrary.keyboardvisibilityevent:keyboardvisibilityevent:3.0.0-RC2'
</pre>
</div>
<p>پروژه را سینک می‌کنم تا کتابخانه اضافه شود.</p>
<h3 id="hide-and-show-android-keyboard">مخفی کردن و نمایش کیبورد</h3>
<p>برای مخفی کردن کیبورد کافیست خط زیر را در متد Listener دکمه بجای متد closeKeyboard اضافه کنم:</p>
<pre class="brush: java; title: ; notranslate">
UIUtil.hideKeyboard(MainActivity.this);
</pre>
<p>UIUtil یکی از کلاس‌های کتابخانه است. متد hideKeyboard این کتابخانه، کیبورد را مخفی می‌کند. علاوه بر این، متد showKeyboard عمل خلاف متد قبل، یعنی نمایش کیبورد را انجام می‌دهد. البته در حالت عادی، با focus شدن کاربر روی یک EditText کیبورد به طور خودکار نمایش داده می‌شود اما ممکن است استثنائاتی وجود داشته باشد که لازم باشد اندروید را وادار به نمایش کیبورد کنیم. این متد بر خلاف متد hideKeyboard که فقط یک پارامتر ورودی دارد، پارامتر دوم هم باید وارد شود که نام EditText مدنظر ماست. این متد را به onCreate اکتیویتی اضافه کردم:</p>
<pre class="brush: java; title: ; notranslate">
UIUtil.showKeyboard(MainActivity.this, txtInput);
</pre>
<h3 id="keyboard-hide-and-show-event-listener">گوش دادن به رویداد حذف یا ظاهر شدن کیبورد</h3>
<p>با استفاده از متد setEventListener کلاس KeyboardVisibilityEvent این کتابخانه، به صورت زیر یک متد با نام onVisibilityChanged ساخته می‌شود:</p>
<pre class="brush: java; title: ; notranslate">
KeyboardVisibilityEvent.setEventListener(this, new KeyboardVisibilityEventListener() {
    @Override
    public void onVisibilityChanged(boolean b) {

    }
});

تغییر وضعیت کیبورد از طریق پارامتر b دریافت می‌شود. برای نمایش خروجی این متد، یک شرط تعریف می‌کنم که خروجی هر حالت در قالب یک Toast ظاهر می‌شود:
KeyboardVisibilityEvent.setEventListener(this, new KeyboardVisibilityEventListener() {
    @Override
    public void onVisibilityChanged(boolean b) {

        if (b) {
            Toast.makeText(MainActivity.this, &quot;کیبورد ظاهر شد&quot;, Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(MainActivity.this, &quot;کیبورد حذف شد&quot;, Toast.LENGTH_SHORT).show();
        }

    }
});
</pre>
<p>کد کامل اکتیویتی:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.keyboardmanagement;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import net.yslibrary.android.keyboardvisibilityevent.KeyboardVisibilityEvent;
import net.yslibrary.android.keyboardvisibilityevent.KeyboardVisibilityEventListener;
import net.yslibrary.android.keyboardvisibilityevent.util.UIUtil;

public class MainActivity extends AppCompatActivity {

    TextView txtResult;
    EditText txtInput;
    Button btnSubmit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtResult = findViewById(R.id.txt_result);
        txtInput = findViewById(R.id.txt_input);
        btnSubmit = findViewById(R.id.submit_btn);

        UIUtil.showKeyboard(MainActivity.this, txtInput);

        btnSubmit.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                String userText = txtInput.getText().toString();
                txtResult.setText(userText);
                //closeKeyboard();
                UIUtil.hideKeyboard(MainActivity.this);

            }
        });

        KeyboardVisibilityEvent.setEventListener(this, new KeyboardVisibilityEventListener() {
            @Override
            public void onVisibilityChanged(boolean b) {

                if (b) {
                    Toast.makeText(MainActivity.this, &quot;کیبورد ظاهر شد&quot;, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, &quot;کیبورد حذف شد&quot;, Toast.LENGTH_SHORT).show();
                }

            }
        });

    }

    private void closeKeyboard() {

        View focusView = this.getCurrentFocus();
        if (focusView != null) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
            imm.showSoftInput(focusView, 0);
        }

    }

}
</pre>
<p>پروژه را اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/keyboardmanagement/KeyboardVisibilityEvent_visible_keyboard_event.png" alt="رویداد ظاهر شدن کیبورد اندروید در کتابخانه KeyboardVisibilityEvent" /></p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/keyboardmanagement/KeyboardVisibilityEvent_hide_keyboard_event.png" alt="رویداد مخفی شدن کیبورد اندروید در کتابخانه KeyboardVisibilityEvent" /></p>
<p>مشاهده می‌کنید پیغام‌های &#8220;کیبورد ظاهر شد&#8221; و &#8220;کیبورد حذف شد&#8221; در زمان مناسب نمایش داده شده است.<br />
موفق و پیروز باشید.</p>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://developer.android.com/reference/android/content/Context#INPUT_METHOD_SERVICE" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/content/Context#INPUT_METHOD_SERVICE</a><br />
<a href="https://developer.android.com/reference/android/app/Activity#getCurrentFocus()" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/app/Activity#getCurrentFocus()</a>
</p>
<p class="source">توجه : سورس پروژه درون پوشه Exercises قرار دارد</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش به همراه سورس پروژه</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۱۲<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۱ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/Keyboard_management.zip" class="button green edd-submit">دانلود رایگان با حجم ۱ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/Keyboard_management.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/hide-and-show-and-manage-android-soft-keyboard/">حذف، نمایش و مدیریت کیبورد اندروید</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/hide-and-show-and-manage-android-soft-keyboard/feed/</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>بدست آوردن اطلاعات دستگاه اندرویدی</title>
		<link>https://android-studio.ir/get-android-device-software-and-hardware-info/</link>
					<comments>https://android-studio.ir/get-android-device-software-and-hardware-info/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Wed, 29 Apr 2020 09:24:03 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=180342</guid>

					<description><![CDATA[<p>در این جلسه از سری مباحث آموزش برنامه نویسی اندروید به نحوه بدست آوردن اطلاعات دستگاه اندرویدی شامل مشخصات سخت افزاری و نرم افزاری به وسیله کلاس Build می‌پردازیم. بدست آوردن اطلاعات دستگاه‌های اندرویدی به نام خدا. با توجه به متن باز بودن سیستم عامل اندروید، تولید کنندگان مطرح و گمنام بی شماری انواع دستگاه‌های [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/get-android-device-software-and-hardware-info/">بدست آوردن اطلاعات دستگاه اندرویدی</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/deviceinfo/get_android_device_software_&#038;_hardware_info.png" alt="بدست آوردن اطلاعات سخت افزاری و نرم افزاری دستگاه‌های اندرویدی" /><br />
در این جلسه از سری مباحث <a href="http://android-studio.ir" target="_blank" rel="noopener noreferrer">آموزش برنامه نویسی اندروید</a> به نحوه بدست آوردن اطلاعات دستگاه اندرویدی شامل مشخصات سخت افزاری و نرم افزاری به وسیله کلاس Build می‌پردازیم.</p>
<h2>بدست آوردن اطلاعات دستگاه‌های اندرویدی</h2>
<p>به نام خدا. با توجه به متن باز بودن سیستم عامل اندروید، تولید کنندگان مطرح و گمنام بی شماری انواع دستگاه‌های اندرویدی را تولید و روانه بازار می‌کنند. هر مدل از این دیوایس‌ها دارای سخت افزاری متفاوت از سایر مدل‌هاست. همچنین با توجه به انتشار نسخه‌های جدید اندروید توسط گوگل، نسخه‌های مختلفی از این سیستم عامل توسط کاربران در حال استفاده است.<br />
گاهی اوقات ممکن است بخشی از عملکرد برنامه ما وابسته به نسخه خاصی از اندروید و یا سخت افزار مانند CPU باشد. یا اینکه بخواهیم بخشی از برنامه بر اساس تفاوت سخت افزار و نرم افزار هر دستگاه، به شیوه متفاوتی اجرا شود. در هر صورت لازم است تا توسعه دهنده اندروید به ویژگی‌های نرم افزاری و سخت افزاری دستگاه دسترسی داشته باشد.<br />
در اندروید کلاسی با نام Build این وظیفه را بعهده دارد. در آموزش‌های گذشته مانند <a href="http://android-studio.ir/android-notification/" target="_blank" rel="noopener noreferrer">آموزش کار با نوتیفیکیشن در اندروید</a> و <a href="http://android-studio.ir/android-service/" target="_blank" rel="noopener noreferrer">آموزش Service در اندروید</a> نیز از این کلاس برای تشخیص نسخه اندروید و انجام عمل متناسب با آن استفاده کرده بودیم. در این جلسه قصد دارم سایر قابلیت‌های این کلاس را بررسی کنم.</p>
<h2>ویژگی‌های کلاس Build</h2>
<p>در جدول زیر به تعدادی از ویژگی‌های کلاس Build پرداخته و کاربرد هرکدام را به مختصر توضیح می‌دهم:</p>
<table class="table table-striped" style="text-align: center;">
<thead>
<tr>
<th>نام ویژگی</th>
<th>شرح</th>
</tr>
</thead>
<tbody>
<tr>
<td>BRAND</td>
<td>برند سازنده دستگاه</td>
</tr>
<tr>
<td>MANUFACTURER</td>
<td>نام سازنده دستگاه</td>
</tr>
<tr>
<td>DEVICE</td>
<td>نام طرح صنعتی دستگاه</td>
</tr>
<tr>
<td>MODEL</td>
<td>نام رسمی دستگاه</td>
</tr>
<tr>
<td>DISPLAY</td>
<td>شناسه صفحه نمایش دستگاه</td>
</tr>
<tr>
<td>TYPE</td>
<td>نوع دستگاه از جمله &#8220;user&#8221; و &#8220;eng&#8221;</td>
</tr>
<tr>
<td>HARDWARE</td>
<td>مشخصات سخت افزار دستگاه</td>
</tr>
<tr>
<td>PRODUCT</td>
<td>نام کلی دستگاه</td>
</tr>
<tr>
<td>VERSION.RELEASE</td>
<td>نسخه اندروید</td>
</tr>
<tr>
<td>VERSION.SDK_INT</td>
<td>نسخه SDK اندروید</td>
</tr>
</tbody>
</table>
<div class="alert alert-warning">
<span class="notice">نکته:</span> خروجی برخی ویژگی‌ها مانند BRAND و MANUFACTURER در اکثر دستگاه‌ها یکسان است.
</div>
<h2>پروژه بدست آوردن اطلاعات دستگاه اندرویدی</h2>
<p>قصد دارم تعدادی از مهمترین ویژگی‌های کلاس Build اندروید را در قالب یک پروژه ساده استفاده کنم بطوری که خروجی روی اکتیویتی نمایش داده شود. مطابق مبحث <a href="http://android-studio.ir/create-android-project-and-its-structure/">آموزش ساخت پروژه در اندروید استودیو</a> یک پروژه اندرویدی با نام DeviceInfo می‌سازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم.<br />
ابتدا ۵ عدد TextView به layout اکتیویتی اضافه می‌کنم. هرکدام از این View ها یکی از مشخصات دستگاه را نمایش خواهد داد. </p>
<p><span class="filename">activity_main.xml</span></p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:gravity=&quot;center&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/txt_manufacturer&quot;
        android:textSize=&quot;20dp&quot;
        android:text=&quot;&quot; /&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/txt_model&quot;
        android:textSize=&quot;20dp&quot;
        android:text=&quot;&quot;
        android:layout_below=&quot;@id/txt_manufacturer&quot; /&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/txt_version&quot;
        android:textSize=&quot;20dp&quot;
        android:text=&quot;&quot;
        android:layout_below=&quot;@id/txt_model&quot;/&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/txt_sdk&quot;
        android:textSize=&quot;20dp&quot;
        android:text=&quot;&quot;
        android:layout_below=&quot;@id/txt_version&quot; /&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/txt_hardware&quot;
        android:textSize=&quot;20dp&quot;
        android:text=&quot;&quot;
        android:layout_below=&quot;@id/txt_sdk&quot; /&gt;

&lt;/RelativeLayout&gt;
</pre>
<p>سپس اکتیویتی را به صورت زیر تکمیل می‌کنم:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.deviceinfo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Build;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    TextView manufacturerTxt, modelTxt, versionTxt, sdkTxt, hardwareTxt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        manufacturerTxt = findViewById(R.id.txt_manufacturer);
        modelTxt = findViewById(R.id.txt_model);
        versionTxt = findViewById(R.id.txt_version);
        sdkTxt = findViewById(R.id.txt_sdk);
        hardwareTxt = findViewById(R.id.txt_hardware);

        String manufacturer = Build.MANUFACTURER;
        String model = Build.MODEL;
        String version = Build.VERSION.RELEASE;
        int sdk = Build.VERSION.SDK_INT;
        String hardware = Build.HARDWARE;

        manufacturerTxt.setText(&quot;Manufacturer: &quot; + manufacturer);
        modelTxt.setText(&quot;Model: &quot; + model);
        versionTxt.setText(&quot;Version: &quot; + version);
        sdkTxt.setText(&quot;SDK: &quot; + Integer.toString(sdk));
        hardwareTxt.setText(&quot;Hardware: &quot; + hardware);

    }
}
</pre>
<p>چهار متغیر از جنس String و یک متغیر از جنس int تعریف کرده‌ام که به ترتیب، مشخصات مربوط به MANUFACTURER و MODEL و RELEASE و SDK_INT و HARDWARE را از کلاس Build گرفته و ذخیره می‌کنند. در نهایت هرکدام را روی یک TextView نمایش می‌دهم.<br />
پروژه را اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/deviceinfo/show_android_device_properties_using_build_class.png" alt="نمایش ویژگی‌های دستگاه اندرویدی توسط کلاس Build" /></p>
<p>ملاحظه می‌کنید مشخصات دستگاه شامل نام سازنده، مدل دستگاه، نسخه اندروید، نسخه SDK و مدل CPU در اکتیویتی نمایش داده شده است.<br />
از جمله کارایی‌های این کلاس می‌توان به اجرا یا عدم اجرای کد ساخت کانال نوتیفیکیشن بر اساس نسخه سیستم عامل اشاره کرد که در مبحث <a href="http://android-studio.ir/android-notification/" target="_blank" rel="noopener noreferrer">آموزش کار با نوتیفیکیشن در اندروید</a> به آن پرداخته شده است:</p>
<pre class="brush: java; title: ; notranslate">
if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.O) {

    // Create Notification channel

}
</pre>
<p>شرط فوق بررسی می‌کند در صورتی کد اجرا شود که نسخه اندروید دستگاه حداقل Oreo و یا بالاتر باشد.<br />
موفق و پیروز باشید.</p>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://developer.android.com/reference/android/os/Build" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/os/Build</a><br />
<a href="https://developer.android.com/reference/android/os/Build.VERSION" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/os/Build.VERSION</a>
</p>
<p class="source">توجه : سورس پروژه درون پوشه Exercises قرار دارد</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش به همراه سورس پروژه</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۶<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۱ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/Device_info.zip" class="button green edd-submit">دانلود رایگان با حجم ۱ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/Device_info.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/get-android-device-software-and-hardware-info/">بدست آوردن اطلاعات دستگاه اندرویدی</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/get-android-device-software-and-hardware-info/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>تشخیص جهت (orientation) صفحه نمایش دستگاه اندرویدی</title>
		<link>https://android-studio.ir/get-android-screen-orientation/</link>
					<comments>https://android-studio.ir/get-android-screen-orientation/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Mon, 20 Apr 2020 16:22:32 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=178870</guid>

					<description><![CDATA[<p>در این جلسه از سری مباحث آموزش برنامه نویسی اندروید قصد داریم دو روش بدست آوردن جهت یا Orientation صفحه نمایش دستگاه‌های اندرویدی را بررسی کنیم. به این ترتیب می‌توانیم تشخیص دهیم صفحه نمایش در حالت عمودی (Portrait) قرار گرفته یا افقی (Landscape). تشخیص جهت صفحه نمایش در اندروید به نام خدا. در روند توسعه [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/get-android-screen-orientation/">تشخیص جهت (orientation) صفحه نمایش دستگاه اندرویدی</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/orientation/get_screen_orientation_in_android_programming.png" alt="تشخیص جهت (orientation) صفحه نمایش دستگاه اندرویدی" /><br />
در این جلسه از سری مباحث <a href="http://android-studio.ir" target="_blank" rel="noopener noreferrer">آموزش برنامه نویسی اندروید</a> قصد داریم دو روش بدست آوردن جهت یا Orientation صفحه نمایش دستگاه‌های اندرویدی را بررسی کنیم. به این ترتیب می‌توانیم تشخیص دهیم صفحه نمایش در حالت عمودی (Portrait) قرار گرفته یا افقی (Landscape).</p>
<h2>تشخیص جهت صفحه نمایش در اندروید</h2>
<p>به نام خدا. در روند توسعه یک اپلیکیشن اندرویدی ممکن است در برخی مواقع لازم باشد عمل یا اعمالی بر اساس نحوه قرارگیری دستگاه انجام شوند. این عمل و تغییرات می‌تواند در رابط کاربری بوده و یا اینکه مربوط به عملکرد اصلی برنامه ما باشد.<br />
برای تشخیص جهت قرارگیری صفحه نمایش به دو روش می‌توان عمل کرد که در ادامه و در قالب یک پروژه، هر دو روش را بررسی می‌کنیم. مطابق مبحث <a href="http://android-studio.ir/create-android-project-and-its-structure/">آموزش ساخت پروژه در اندروید استودیو</a> یک پروژه اندرویدی با نام Orientation می‌سازم. <a href="http://android-studio.ir/activity/">اکتیویتی</a> را از نوع Empty Activity و زبان را Java انتخاب کردم.<br />
ابتدا دو TextView در layout اکتیویتی تعریف می‌کنم تا خروجی هر دو روش را نمایش دهد:</p>
<p><span class="filename">activity_main.xml</span></p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:gravity=&quot;center&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/txt_one&quot;
        android:textSize=&quot;30dp&quot;
        android:text=&quot;&quot; /&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/txt_two&quot;
        android:textSize=&quot;30dp&quot;
        android:text=&quot;&quot;
        android:layout_below=&quot;@id/txt_one&quot; /&gt;

&lt;/RelativeLayout&gt;
</pre>
<h3>روش اول تشخیص جهت صفحه نمایش</h3>
<p>در این روش با استفاده از کلاس Configuration وضعیت فعلی صفحه نمایش را بررسی کرده و با تعریف یک شرط، خروجی مناسب را دریافت می‌کنیم. </p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.orientation;

import androidx.appcompat.app.AppCompatActivity;

import android.content.res.Configuration;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView txtOne, txtTwo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtOne = findViewById(R.id.txt_one);
        txtTwo = findViewById(R.id.txt_two);

        int screenOrientation = this.getResources().getConfiguration().orientation;

        if (screenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
            txtOne.setText(&quot;Landscape&quot;);
        } else {
            txtOne.setText(&quot;Portrait&quot;);
        }

    }
}
</pre>
<p>یک متغیر با نام دلخواه screenOrientation و از جنس int تعریف کردم که orientation فعلی صفحه را در خود ذخیره می‌کند:</p>
<pre class="brush: java; title: ; notranslate">
int screenOrientation = this.getResources().getConfiguration().orientation;
</pre>
<p>عدد ذخیره شده در متغیر فوق، عدد ۱ یا ۲ خواهد بود که عدد ۱ نشانه وضعیت Portrait و عدد ۲ نشانه وضعیت Landscape است. سپس یک شرط تعریف شده که بررسی می‌کند چنانچه مقدار ذخیره شده در این متغیر با Configuration.ORIENTATION_LANDSCAPE برابر باشد (به عبارتی عدد ۲ ذخیره شده باشد) قسمت اول شرط و در غیر اینصورت قسمت دوم شرط اجرا شود. بدیهی است چنانچه از Configuration.ORIENTATION_PORTRAIT استفاده می‌کردم خروجی شرط بلعکس می‌شد. کانتکست را this قرار داده‌ام.<br />
پروژه را اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/orientation/get_android_screen_orientation_using_Configuration_class.png" alt="تشخیص جهت صفحه نمایش دستگاه اندرویدی توسط کلاس Configuration" /></p>
<p>در ابتدای اجرای <a href="http://android-studio.ir/install-genymotion/">شبیه ساز Genymotion</a> صفحه نمایش در حالت عمودی قرار دارد و خروجی شرط که در TextView نمایش داده شده با وضعیت دستگاه همخوانی داشته و عبارت Portrait را نمایش می‌دهد. حالا دستگاه را در حالت افقی قرار می‌دهم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/orientation/change_orientation_to_landscape.png" alt="تشخیص جهت صفحه نمایش دستگاه اندرویدی توسط کلاس Configuration در حالت افقی یا Landscape" /></p>
<p>مشاهده می‌کنید با تغییر وضعیت صفحه نمایش و قرار گرفتن در حالت افقی یا Landscape و restart شدن اکتیویتی، قسمت دوم شرط اجرا شده است.</p>
<h3>روش دوم تشخیص جهت صفحه نمایش</h3>
<p>در این قسمت از resource پروژه برای تشخیص وضعیت صفحه نمایش استفاده می‌کنیم. روی فولدر res پروژه راست کلیک کرده و Android Resource File را انتخاب می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/orientation/add_layout.xml_to_layout-land-res-directory.png" alt="تشخیص جهت صفحه نمایش دستگاه اندرویدی توسط resource پروژه (xml)" /></p>
<p>نام فایل را layouts وارد کرده و در قسمت Directory name مطابق تصویر فوق، values-land می‌نویسم. از آنجایی که نام این دایرکتوری به land ختم شده، محتویات داخل آن در صورتی در دسترس خواهد بود که وضعیت صفحه نمایش در حالت Landscape قرار گرفته باشد. پس از ایجاد فایل layouts.xml آنرا به صورت زیر تکمیل می‌کنم:</p>
<p><span class="filename">values-land/layouts.xml</span></p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
    &lt;bool name=&quot;is_landscape&quot;&gt;true&lt;/bool&gt;
&lt;/resources&gt;
</pre>
<p>در این فایل یک ویژگی از جنس bool (مخفف boolean) تعریف کردم با نام دلخواه is_landscape و مقدار true. مجدد یک فایل دیگر به همین شیوه و با همان نام layouts.xml اما اینبار در دایرکتوری values-port ایجاد می‌کنم:</p>
<p><span class="filename">values-port/layouts.xml</span></p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
    &lt;bool name=&quot;is_landscape&quot;&gt;false&lt;/bool&gt;
&lt;/resources&gt;
</pre>
<p>در این فایل هم یک ویژگی با نام is_landscape تعریف کردم با این تفاوت که مقدار آن false است. این دو فایل در محیط پروژه به اینصورت نمایش داده می‌شود:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/orientation/layouts.xml_in_layout-land_&#038;_layout-port_android_project_directory.png" alt="فایل layouts.xml در دایرکتوری‌های layout-land و layout-port " /></p>
<p>حالا در اکتیویتی یک متغیر از جنس boolean تعریف می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
boolean isLandscape = this.getResources().getBoolean(R.bool.is_landscape);
</pre>
<p>توسط کد فوق، مقدار ویژگی bool با نام is_landscape از layouts.xml ای که با توجه به وضعیت صفحه نمایش در دسترس است، در متغیر isLandscape ذخیره می‌شود. یعنی چنانچه صفحه نمایش در حالت افقی قرار داشته باشد، layouts.xml دایرکتوری values-land در دسترس خواهد بود در نتیجه مقدار true را برمی‌گرداند و همینطور برای حالت عمودی مقدار false. در نهایت شرطی را برای بررسی خروجی این متغیر مانند قسمت قبل تعریف می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
if (isLandscape) {
    txtTwo.setText(&quot;Landscape&quot;);
} else {
    txtTwo.setText(&quot;Portrait&quot;);
}
</pre>
<p>مجدد پروژه را اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/orientation/get_android_screen_orientation_using_resources_bool_attribute.png" alt="تشخیص جهت (orientation) صفحه نمایش دستگاه اندرویدی توسط layouts.xml" /></p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/orientation/get_android_screen_orientation_using_resources_bool_attribute_2.png" alt="تشخیص جهت (orientation) صفحه نمایش دستگاه اندرویدی توسط layouts.xml" /></p>
<p>ملاحظه می‌کنید خروجی هردو روش بر اساس افقی یا عمودی بودن دستگاه بروز می‌شود.<br />
کد کامل اکتیویتی:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.orientation;

import androidx.appcompat.app.AppCompatActivity;

import android.content.res.Configuration;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView txtOne, txtTwo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtOne = findViewById(R.id.txt_one);
        txtTwo = findViewById(R.id.txt_two);

        int screenOrientation = this.getResources().getConfiguration().orientation;

        if (screenOrientation == Configuration.ORIENTATION_LANDSCAPE) {
            txtOne.setText(&quot;Landscape&quot;);
        } else {
            txtOne.setText(&quot;Portrait&quot;);
        }

        boolean isLandscape = this.getResources().getBoolean(R.bool.is_landscape);

        if (isLandscape) {
            txtTwo.setText(&quot;Landscape&quot;);
        } else {
            txtTwo.setText(&quot;Portrait&quot;);
        }

    }
}
</pre>
<p>موفق و پیروز باشید.</p>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://developer.android.com/reference/android/content/res/Configuration" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/content/res/Configuration</a>
</p>
<p class="source">توجه : سورس پروژه درون پوشه Exercises قرار دارد</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش به همراه سورس پروژه</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۹<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۱ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/Orientation.zip" class="button green edd-submit">دانلود رایگان با حجم ۱ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/Orientation.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/get-android-screen-orientation/">تشخیص جهت (orientation) صفحه نمایش دستگاه اندرویدی</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/get-android-screen-orientation/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>بدست آوردن اندازه صفحه نمایش دستگاه اندرویدی</title>
		<link>https://android-studio.ir/android-screen-size-and-dpi/</link>
					<comments>https://android-studio.ir/android-screen-size-and-dpi/#comments</comments>
		
		<dc:creator><![CDATA[سیدمهدی مطهری]]></dc:creator>
		<pubDate>Sat, 18 Apr 2020 06:42:12 +0000</pubDate>
				<category><![CDATA[آموزش‌های تکمیلی]]></category>
		<category><![CDATA[آموزش‌های رایگان]]></category>
		<guid isPermaLink="false">https://android-studio.ir/?p=178784</guid>

					<description><![CDATA[<p>در این جلسه از سری مباحث آموزش برنامه نویسی اندروید قصد داریم نحوه بدست آوردن اندازه صفحه نمایش یک دستگاه اندرویدی را بررسی کنیم. برای این کار از دوس کلاس Display و DisplayMetrics استفاده خواهیم کرد. بدست آوردن اندازه صفحه نمایش در اندروید به نام خدا. در برخی موارد ممکن است توسعه دهنده و برنامه [&#8230;]</p>
<p>نوشته <a href="https://android-studio.ir/android-screen-size-and-dpi/">بدست آوردن اندازه صفحه نمایش دستگاه اندرویدی</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/screensize/android_device_screen_size_and_dpi.png" alt="بدست آوردن اندازه صفحه نمایش در برنامه نویسی اندروید" /><br />
در این جلسه از سری مباحث <a href="http://android-studio.ir/">آموزش برنامه نویسی اندروید</a> قصد داریم نحوه بدست آوردن اندازه صفحه نمایش یک دستگاه اندرویدی را بررسی کنیم. برای این کار از دوس کلاس Display و DisplayMetrics استفاده خواهیم کرد.</p>
<h2>بدست آوردن اندازه صفحه نمایش در اندروید</h2>
<p>به نام خدا. در برخی موارد ممکن است توسعه دهنده و برنامه نویس اندروید، نیازمند دانستن اندازه (طول و عرض) صفحه نمایش دستگاه‌های اندرویدی بر حسب پیکسل (pixel) باشد تا بر اساس آن بتواند عمل مناسبی انجام دهد. همچنین می‌توان تراکم پیکسلی صفحه نمایش یا همان DPI (مخفف Dots per inch) به معنی تعداد نقطه در اینچ را نیز بدست آورد.<br />
برای اینکار مطابق مبحث <a href="http://android-studio.ir/create-android-project-and-its-structure/">آموزش ساخت پروژه در اندروید استودیو</a> یک پروژه اندرویدی با نام ScreenSize می‌سازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم. در این تمرین از دو کلاس Display و DisplayMetrics برای بدست آوردن اندازه صفحه نمایش استفاده می‌کنم که از کلاس دوم علاوه بر سایر صفحه نمایش، DPI صفحه هم بدست می‌آید.<br />
قبل از کار با کلاس‌ها، دو TextView در layout اکتیویتی تعریف می‌کنم:</p>
<p><span class="filename">activity_main.xml</span></p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:gravity=&quot;center&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/display&quot;
        android:text=&quot;&quot; /&gt;

    &lt;TextView
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:id=&quot;@+id/display_metrics&quot;
        android:text=&quot;&quot;
        android:layout_below=&quot;@id/display&quot; /&gt;

&lt;/RelativeLayout&gt;
</pre>
<h3>بدست آوردن اندازه صفحه نمایش توسط کلاس Display </h3>
<p>ابتدا یک نمونه از کلاس Display به صورت زیر می‌سازم:</p>
<pre class="brush: java; title: ; notranslate">
Display displaySize = getWindowManager().getDefaultDisplay();
</pre>
<p>با تعریف کد فوق، اطلاعات مربوط به صفحه نمایش دستگاه اندرویدی از جمله نام صفحه و اندازه آن در displaySize ذخیره می‌شود. سپس یک نمونه از کلاس Point می‌سازم تا مختصات طول و عرض صفحه نمایش را از displaySize دریافت و ذخیره کند:</p>
<pre class="brush: java; title: ; notranslate">
Point size = new Point();
displaySize.getSize(size);
</pre>
<p>توسط <span dir="ltr">getSize()</span> مختصات در size ذخیره شده است. در نهایت، طول و عرض دریافتی را به دو مقدار از جنس int تبدیل می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
int displayWidth = size.x;
int displayHeight = size.y;
</pre>
<p>در دو خط بالا، size.x اندازه عرض و size.y اندازه ارتفاع یا طول را برمی‌گرداند که در دو int ذخیره شده است.<br />
حالا مختصات را در TextView اولی نمایش می‌دهم. کد کامل اکتیویتی:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.screensize;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView txtDisplay, txtDisplayMetrics;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtDisplay = findViewById(R.id.display);
        txtDisplayMetrics = findViewById(R.id.display_metrics);

        Display displaySize = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        displaySize.getSize(size);
        int displayWidth = size.x;
        int displayHeight = size.y;

        txtDisplay.setText(&quot;Width: &quot; + displayWidth + &quot; / &quot; + &quot;Height: &quot; + displayHeight);

    }
}
</pre>
<p>پروژه را اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/screensize/android_screen_size_in_pixel_using_Display_class.png" alt="بدست آوردن اندازه صفحه نمایش دستگاه اندرویدی توسط کلاس Display" /></p>
<p>مشاهده می‌کنید اعداد ۱۰۸۰ و ۲۰۳۴ به ترتیب عرض و ارتفاع صفحه نمایش این دستگاه، یعنی دیوایس مجازی <a href="http://android-studio.ir/install-genymotion/">شبیه ساز اندرویدی Genymotion</a> را نشان می‌دهند.</p>
<h3>بدست آوردن اندازه صفحه نمایش توسط کلاس DisplayMetrics </h3>
<p>کدهای زیر را به اکتیویتی اضافه می‌کنم:</p>
<pre class="brush: java; title: ; notranslate">
DisplayMetrics metricSize = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metricSize);
int metricDisplayWidth = metricSize.widthPixels;
int metricDisplayHeight = metricSize.heightPixels;
int metricDPI = metricSize.densityDpi;
float xdp = metricSize.xdpi;
float ydp = metricSize.ydpi;
txtDisplayMetrics.setText(&quot;Width: &quot; + metricDisplayWidth + &quot; / &quot; + &quot;Height: &quot; + metricDisplayHeight + &quot; DPI: &quot; + metricDPI + &quot; X:&quot; + xdp + &quot; Y:&quot; + ydp);
</pre>
<p>در این قسمت و با استفاده از کلاس DisplayMetrics مانند قسمت قبل ابتدا اندازه صفحه نمایش بر حسب پیکسل گرفته شده است. metricSize.widthPixels مقدار عرض و metricSize.heightPixels مقدار ارتفاع را برمی‌گرداند. همچنین metricSize.densityDpi مقدار DPI صفحه را برمی‌گرداند. علاوه بر آن، مختصات طول و عرض DPI هم توسط metricSize.xdpi و metricSize.ydpi گرفته شده است.<br />
مجدد پروژه را اجرا می‌کنم:</p>
<p><img decoding="async" class="aligncenter img-responsive" src="http://android-studio.ir/wp-content/uploads/screensize/android_screen_size_and_dpi_using_DisplayMetrics_class.png" alt="بدست آوردن اندازه صفحه نمایش و DPI دستگاه اندرویدی توسط کلاس DisplayMetrics" /></p>
<p>در تصویر فوق ملاحظه می‌کنید طول و عرض صفحه نمایش مانند خروجی کلاس قبل بوده و علاوه بر آن، DPI صفحه و مختصات X و Y آن نیز عدد ۴۲۰ را نشان می‌دهد.<br />
کد کامل اکتیویتی:</p>
<p><span class="filename">MainActivity.java</span></p>
<pre class="brush: java; title: ; notranslate">
package ir.android_studio.screensize;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Point;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Display;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    TextView txtDisplay, txtDisplayMetrics;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        txtDisplay = findViewById(R.id.display);
        txtDisplayMetrics = findViewById(R.id.display_metrics);

        Display displaySize = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        displaySize.getSize(size);
        int displayWidth = size.x;
        int displayHeight = size.y;

        txtDisplay.setText(&quot;Width: &quot; + displayWidth + &quot; / &quot; + &quot;Height: &quot; + displayHeight);

        DisplayMetrics metricSize = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metricSize);
        int metricDisplayWidth = metricSize.widthPixels;
        int metricDisplayHeight = metricSize.heightPixels;
        int metricDPI = metricSize.densityDpi;
        float xdp = metricSize.xdpi;
        float ydp = metricSize.ydpi;

        txtDisplayMetrics.setText(&quot;Width: &quot; + metricDisplayWidth + &quot; / &quot; + &quot;Height: &quot; + metricDisplayHeight + &quot; DPI: &quot; + metricDPI + &quot; X:&quot; + xdp + &quot; Y:&quot; + ydp);

    }
}
</pre>
<h3>مطالعه‌ی بیشتر:</h3>
<p style="text-align: left; direction: ltr;">
<a href="https://developer.android.com/reference/android/view/Display" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/view/Display</a><br />
<a href="https://developer.android.com/reference/android/graphics/Point" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/graphics/Point</a><br />
<a href="https://developer.android.com/reference/android/util/DisplayMetrics" target="_blank" rel="noopener noreferrer">https://developer.android.com/reference/android/util/DisplayMetrics</a>
</p>
<p class="source">توجه : سورس پروژه درون پوشه Exercises قرار دارد</p>
<div class="alert dlbox">
<span class="title"><i class="titleicon fa fa-download fa-lg"></i>دانلود نسخه PDF این آموزش به همراه سورس پروژه</span><br />
<i class="dlicon fa fa-square fa-lg"></i> تعداد صفحات : ۷<br />
<i class="dlicon fa fa-square fa-lg"></i> حجم : ۱ مگابایت<br />
<i class="dlicon fa fa-square fa-lg"></i> قیمت : رایگان<br />
<a href="http://dl.android-studio.ir/courses/ScreenSize.zip" class="button green edd-submit">دانلود رایگان با حجم ۱ مگابایت</a> <a href="http://dl2.android-studio.ir/courses/ScreenSize.zip" class="button red edd-submit">لینک کمکی</a>
</div>
<p>نوشته <a href="https://android-studio.ir/android-screen-size-and-dpi/">بدست آوردن اندازه صفحه نمایش دستگاه اندرویدی</a> اولین بار در <a href="https://android-studio.ir">اندروید استودیو</a> پدیدار شد.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://android-studio.ir/android-screen-size-and-dpi/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/

Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: android-studio.ir @ Y-m-d H:i:s by W3 Total Cache
-->