آموزش: #2 نکات و اصول مهم در برنامه نویسی JavaAndroid
در ادامه مقاله قبلیم که شماره یک "نکات و اصول مهم در برنامه نویسی Java/Android" بود، در این مقاله شماره دو همین موضوع رو ارائه میدم. منتها کمی پیشرفته تر...
1- طبق گفته Sun، از دستورات System.runFinalizersOnExit() و Runtime.runFinalizersOnExit() استفاده نکنید، اینها منسوخ و Unsafe اعلام شدن:
JAVA-DOC: Because it is inherently unsafe. It may result in finalizers being called on live objects while other threads are concurrently manipulating those objects, resulting in erratic behavior or deadlock. While this problem could be prevented if the class whose objects are being finalized were coded to "defend against" this call, most programmers do not defend against it. They assume that an object is dead at the time that its finalizer is called.
Further, the call is not "thread-safe" in the sense that it sets a VM-global flag. This forces every class with a finalizer to defend against the finalization of live objects!
Joshua Bloch: Never call System.runFinalizersOnExit or Runtime.runFinalizersOnExit for any reason: they are among the most dangerous methods in the Java libraries.
2- Handler یا Timer؟ (در شرایط کاربری یکسان)
Handler: براحتی reschedule میشه، هر اینترفیس Runnable ای رو میشه داخلش attach کرد، حافظه کمتری رو مصرف می کنه، بهترین گزینه برای update های UI هستش، از Multi-threading پشتیبانی می کنه، استثناهای پرتاب شده از task ها و thread هارو هندل میکنه، در ساخت Thread میشه Handler اون thread رو هم باهاش طراحی کرد، زمانی که دستگاه به حالت sleep بره کماکان به کارش ادامه میده!
Timer: قابلیت reschedule شدن نداره، فقط یک TimerTask رو میشه داخلش attach کرد، حافظه بیشتری رو اشغال میکنه، بهترین گزینه برای background-task ها هستش، نمی تونه UI رو update کنه، فقط با یک Thread کار میکنه(بقیه task ها باید queue بمونن تا کار اولی تموم بشه)، استثناهای task و thread رو هندل نمی کنه و فقط نابودشون میکنه(دیگر task ها رو هم اجرا نخواهد کرد)، زمانی که دستگاه به حالت sleep بره متوقف میشه.
3- آیا میدونید با فعال کردن خاصیت android:debuggable=true در فایل AndroidManifest.xml پروسه Proguard غیرفعال میشه؟
ProGuard runs only when you build your application in release mode, so you do not have to deal with obfuscated code when you build your application in debug mode.
4- اگر به استثنا نوع UnknownHostException برخورد کردید و نتونستید این نوع استثنا رو catch کنید، تعجب نکنید. چراکه استثنای UnknownHostException از دسته java.net هستش درحالی که Exception از دسته java.lang!
راه ساده:
try
{
// Codes.
}
catch (java.net.UnknownHostException exception) // java.net
{
// Handling codes.
}
catch (java.lang.Exception exception) // java.lang
{
// Handling codes.
}
5- با تغییر color scheme تصویر به RGB_565 می تونید میزان حافظه اشغالی توسط تصویر رو کاهش بدید:
البته فقط کمی کیفیت تصویر پایین میاد...
BitmapFactory.Options _options = new BitmapFactory.Options();
_options.inPreferredConfig = Config.RGB_565;
6- طبیعتاً باید از انجام عملیات Networking در Thread UI/main خودداری کنید، مخصوصاً در حین کار با دیتای remote. چراکه UI برنامه قفل و یک استثنای NetworkOnMainThreadException (در اندروید 3 به بعد) هم پرتاپ خواهد شد.
درصورت برخورد با این مشکل از AsyncTask یا IntentService استفاده کنید، یا کلاً StrictMode رو خاموش کنید:
if (android.os.Build.VERSION.SDK_INT > 9)
{
// Not recommended.
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
}
توجه: حتماً از وجود دسترسی android.permission.INTERNET هم در فایل AndroidManifest.xml اطمینان حاصل کنید.
7- هنگام تنظیم parameter های کتابخانه AndroidHTTPClient، یادتون باشه که حتماً httpGet.setParams رو هم تنظیم کنید، در غیر اینصورت تنظیماتتون کار نخواهند کرد.
8- همیشه درنظر داشته باشید که دستور invalidate() باید در UI Thread صدا زده بشه و دستور postInvalidate() در Non-UI Thread. درغیر اینصورت دستورها کار نخواهند کرد و به مشکل ANR یا استثنای CalledFromWrongThreadException برخواهید خورد.
9- بنظر من زمانی که قراره فقط نوع خاصی از exception رو هندل کنید، try/catch بهترین گزینست. ولی زمانی که قرار نیست نوع خاصی رو هندل کنید یا تعداد زیادی exception سر راهتون قرار داره، throw کردن مناسب تره.
10- آیا میدونید دستور isEmpty() کاراکتر Space رو متن بحساب میاره و false برمیگردونه؟
11- همیشه همه dialog هارو در متد OnPause() یا OnDestroy() توسط dismiss() ببندید و درنهایت توسط دستور removeDialog() نابودشون کنید(API < 13). همچنین بهتره از دستور isFinishing() قبل از نمایش dialog استفاده کنید تا با خطای Activity has leaked window that was originally added روبرو نشید.
نکته: البته دستور دیگه ای هم هست بنام dismissDialog() که فقط dialog باز شده رو مخفی میکنه، تا بتونید در قسمت های دیگه برنامه مجدداً نمایشش بدید. (API < 13)
12- آیا می دونید دستور finish() قادر به بستن پنجره AlertDialog و لغو کردن ProgressDialog نیست؟
پس OnPause, OnDestroy یا OnStop برنامه تون رو برای هندل کردن این پنجره ها safe کنید.
13- می تونید با نصب پلاگین FindBug در برنامه Eclipse، باگ، خطا و اشتباهات رایج برنامه نویسی در کدهاتون رو پیدا و اونها رو رفع کنید.
14- نکته جزیی: پیشنهاد می کنم از تگ @todo در javadoc استفاده نکنید... از اونجایی که مستندات کدها عمومی هستند و برای کسب اطلاعات کلی طراحی شدند، محل مناسبی برای قرار دادن TODO های پروژه نیستند.
15- نکته جزیی: از قرار دادن کامنت بعد از annotation خودداری کنید. چون annotation ها مطعلق به خط بعدیشون و بخشی از کد هستند، و نه کامنت یا مستندات.
16- آیا میدونید استفاده از مقدار standard در خاصیت launchMode فایل AndroidManifies.xml باعث destroy و recreate شدن Activity میشه و استفاده از FLAG_ACTIVITY_CLEAR_TOP رو هم مسدود میکنه؟
17- در بعضی موارد با قرار دادن دستور زیر در حین بازکردن Activity جدید، خطای Window already focused, ignoring focus gain of برطرف خواهد شد:
YourIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
18- آیا میدونید اگر بیش از یک Activity در stack برنامه وجود داشته باشه، دستور System.exit قادر نیست process برنامه رو نابود کنه و فقط برنامه رو restart می کنه؟
(البته بعضی ها میگن از دستور System.runFinalizersOnExit قبل از دستور System.exit استفاده بشه، درحالی که deprecate شده و توسط گوگل هم Unsafe تلقی شده)
19- نکته جزیی: همیشه در آخر هر فایل یک خط خالی(blank line) قرار بدید. چراکه بعضی از ویرایشگر ها بجای بررسی EOF، خط آخر رو ignore می کنند! (در همه زبانها)
20- آیا میدونید رفتار اندروید در هنگام فشردن دکمه Home با فشردن کلید Back متفاوته؟
Back: اکتیویتی جاری رو finish() میکنه و بترتیب متدهای OnPause() و OnStop() و OnDestroy() رو فرا می خونه و درنهایت Activity جاری رو از stack خارج میکنه و به اکتیویتی قبلی(که در stack هست) برمیگرده، یا حتی به صفحه home! درصورتی که کاربر همین برنامه رو مجدداً باز کنه، بترتیب متدهای onCreate() و OnResume() صدا زده میشن و برنامه مجدداً از نو راه اندازی میشه.
Home: بی درنگ متد OnPause() و بعد از لحظاتی متد OnStop() رو فرا می خونه و سپس برنامه رو به background هدایت میکنه، و در نهایت متد onSaveInstanceState رو صدا میزنه. درصورتی که کاربر همین برنامه رو مجدداً باز کنه، بترتیب متدهای OnRestart() و OnStart() و OnResume() صدا زده میشه و نهایتاً Activity به foreground هدایت میشه و در راس stack قرار میگیره.
نکته: در صورت لزوم می تونید این رفتارهارو توسط overriding تغییر بدید.
21- در صورتی که متد ()onBackPressed در Activity تون موجود هست، باید صراحتاً دستور finish() رو در اونجا صدا بزنید تا Activity مورد نظر وارد OnDestroy() و بعد نابود بشه، در غیر اینصورت نیازی به صدا زدن finish() ندارید.
22- آیا میدونید دستور finish() قادر به آزاد سازی ارتباطات presistent سوکتی نیست و باید از killProcess استفاده بشه؟
نکته: برای نابود سازی process به دسترسی android.permission.KILL_BACKGROUND_PROCESSES نیاز هست.
نکته 2: یادتون باشه که finish() رو قبل از killProcess صدا بزنید تا ابتدا داده های کاربر ذخیره بشن و بعد برنامه تون کشته بشه.
23- از اونجایی که Anonymous Runnable ها ارجاع ضمنی به Class و View ای دارند که بهش پیوستند (مثل Activity)، بنابراین باعث جلوگیری از اعمال GC میشن(بر روی همون کلاس)، حداقل تا زمانی که عملیات اون thread به پایان برسه... همچنین از نظر اولویت اجرایی با thread main/UI برابری میکنن! پس گزینه 100% مناسبی برای multi-threading های سنگین نیستند:
Thread _thread = new Thread(new Runnable() // Bad.
{
@Override
public void run()
{
try
{
// Codes.
}
catch (Exception exception)
{
exception.printStackTrace();
}
}
});
_thread.start();
24- فراموش نکنید کتابخانه OkHTTP (که در اندورید Lollipop هم استفاده شده) گزینه مناسبی برای انجام عملیات HTTP هستش و کتابخانه Retrofit گزینه مناسب برای کار با WebService ها.
25- در صورتی که LogCat تون در برنامه Eclipse از کار افتاده و هیچ گزارشی رو نمایش نمیده یکی از راه های زیر رو دنبال کنید:
- پنل LogCat رو ببندید و مجدداً بازش کنید.
- یا prespective مخصوص DDMS رو باز و بر روی emulator تون در پنل Devices کلیک کنید. و بعد به prespective خودتون برگردید.
- یا ADB رو reset کنید. (از طریق کنسول سیستم یا گزینه Reset ADB در پنل Devices برنامه Eclipse)
- یا در Eclipse به مسیر Menu -> Windows -> Preferences -> Android -> LogCat برید و گزینه Monitor LogCat for message... رو تیک بزنید.
- یا logging رو از طریق شل ADB فعال کنید:
adb shell
echo 1 > /sys/kernel/logger/log_main/enable
- یا اگر Mylyn رو نصب دارید، اون رو uninstall کنید.
- یا اتصال دستگاه رو از سیستم قطع کنید و مجدداً وصلش کنید. (یا Emulator رو خاموش و روشن کنید)
- یا برنامه Eclipse رو ببندید و مجدداً باز کنید.
- یا به مسیر /system/etc/init.d/ دستگاه یا emulator برید و همه فایلها رو بررسی کنید تا خط rm /dev/log/main رو پیدا کنید، بعد این خط رو کامنت کنید:
# rm /dev/log/main
- یا پلاگین ADT رو حذف و مجدداً نصب کنید.
- یا نهایتاً مجبورید از LogCat خود ADB استفاده کنید. (adb logcat)
26- از اونجایی که ایران در لیست تحریم گوگل قرار داره و SDK Manager هم فقط از HTTP Pr@xi پشتیبانی می کنه، برای تنظیم Socks Pr@xi می تونید دستور زیر رو در Console/Command prompt تون بزنید:
لینوکس
export _JAVA_OPTIONS="-DsocksProxyHost=<YourProxyHost> -DsocksProxyPort=<YourProxyPort>"
ویندوز
set _JAVA_OPTIONS="-DsocksProxyHost=<YourProxyHost> -DsocksProxyPort=<YourProxyPort>"
حالا SDK Manager رو باز کنید و گزینه Menu -> Packages -> Reload رو بزنید تا لیست پکیج ها دانلود بشه.
27- بد نیست بدونید زمانی که برنامه تون توسط Eclipse یا Market ها اجرا میشه، flag intent ای که بهش اختصاص داده میشه FLAG_ACTIVITY_NEW_TASK هستش. که این flag باعث میشه تا کاربر قادر بشه دو نسخه از برنامه شمارو همزمان باز کنه.
پس با درک درستی از این قائده، OnCreate() برنامه تون رو دربرابر اینجور باگها ایمن کنید.
29- آیا میدونید Constructor ها متد نیستند؟
class Vehicle
{
public Vehicle() // Constructor
{
return;
}
public void Vehicle() // Method!
{
// print 123
return;
}
}
Vehicle Saloons = new Vehicle();// Construct
Saloons.Vehicle(); // Method call!
حتی درصورت عدم وجود Constructor، کامپایلر یک Constructor بدون پارامتر، همنام کلاس شما تولید میکنه...
30- طبق گفته گوگل، Log های سطح Verbose نباید با نسخه نهایی برنامه کامپایل بشن، بجز در حین توسعه و برنامه نویسی. Log های سطح Debug می تونن با برنامه کامپایل بشن منتها در حین Runtime نباید فعال باشن. (مثلاً با IF محدود بشن) و نهایتاً Log های سطح Error, Warning, Info باید همیشه داخل برنامه باشن، کامپایل بشن و در زمان اجرا هم فعال باشند.
The order in terms of verbosity, from least to most is ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled into an application except during development. Debug logs are compiled in but stripped at runtime. Error, warning and info logs are always kept.
نکته: برای اینکار میتونید از Proguard یا Gradle هم استفاده کنید...
نکته 2: طبیعتاً Log ها بخاطر عملیات read/write/print ای که انجام میدن تاثیرات جزئی هم روی performance برنامه میذارن.
31- آیا میدونید هنگام اتصال دستگاه اندروید به کامپیوتر، External storage دستگاه از دسترس خارج میشه و بر روی کامپیوتر mount میشه؟
(و این رفتار عادی باعث crash برنامه های درحال اجرایی میشه که درست طراحی نشدند)
32- طبق گفته گوگل، میتونید با recycle() کردن Bitmap ها و null قراردادنشون کمک شایانی به تخلیه حافظه بکنید و احتمال وقوع OOM رو هم کاهش بدید:
Bitmap myBitmap = (Bitmap)BitmapFactory.decodeResource(getResources(), R.drawable.my_image);
myBitmap.recycle();
myBitmap = null;
نکته: در اندروید 3 به بعد، استفاده از BitmapFactory.Options.inBitmap می تونه جایگزین خوبی برای عملیات بالا باشه، چون این دستور استفاده مجدد(reuse) از Bitmap کنونی و ساخته شده رو بهتون میده.
34- آیا میدونید در دستگاه های قدیمی اندروید، internal storage فیزیکی به 2 تیکه فضای System storage و Internal SD card پارتیشن بندی شده، در حالیکه در دستگاه های جدیدتر، فضای internal فقط به یک تکه پارتیشن بندی شده. اون هم با نام Internal storage؟
35- حتاالمکان برای load کردن تصاویر remote در ImageView، بجای BitmapFactory.decodeStream از Drawable.createFromStream استفاده کنید، چون کتابخانه BitmapFactory.decodeStream حافظه بیشتری نسبت به Drawable.createFromStream مصرف می کنه.
36- نکته جزیی: یادتون باشه برای بدست اوردن نسخه DVM نصب شده روی دستگاه اندرویدتون باید مقدار java.vm.version رو بازیابی کنید، و نه چیز دیگه:
System.getProperty("java.vm.version");
به واژه vm دقت کنید، شما درحال کار با Dalvik VM هستید و نه Java VM...! همین نکته ریز ساعت ها وقت من رو گرفت.
37- آیا میدونید درصورت عدم تعریف Annotation بر روی کلاس یا متد، ابزار Lint گوگل از مقدار android:minSdkVersion در فایل AndroidManifest.xml بصورت پیشفرض استفاده می کنه؟
38- طبق گفته های گوگل، استفاده از متد ()onOptionsItemSelected در اندروید 2.3 به قبل بهترین کارایی رو داره، درصورتی که ()onMenuItemSelected در اندروید 3 به بعد بهترین کارایی رو داره.
همچنین در اندروید 2.3 به قبل متد onCreateOptionsMenu() زمانی صدا زده میشه که کاربر برای اولین بار منوی برنامه رو باز میکنه، درصورتی که در اندروید 3 به بعد این متد در حین شروع Activity صدا زده میشه.
بعلاوه در اندروید 2.3 به قبل متد onPrepareOptionsMenu() در هربار که کاربر منوی برنامه رو باز میکنه صدا زده میشه، درصورتی که در اندروید 3 به بعد یکبار صدا زده میشه و تا زمانی که گزینه منو در نوار برنامه وجود داره فعال می مونه.
39- در ادامه، آیا میدونید متد onCreateDialog() فقط یکبار صدا زده میشه، و برای تغییر محتوای dialog باید از متد onPrepareDialog() استفاده کنید؟ (بهمراه Override)
40- و در آخر، بصرفه نیست با Implements کردن DialogInterface.OnClickListener با AlertDialog ها کار کنید. این اینترفیس برای Dialog طراحی شده و قابلیت های اضافی برای AlertDialog فراهم میکنه که کاربردی هم نیستند، و در بعضی از قسمت ها مجبورید type-casting هم انجام بدید. درعوض باید AlertDialog.OnClickListener رو implement کنید.
منبع: وبلاگ خودم
سلام دوست عزیز فرق بین کتابخانه retrofit با okhttp چیه؟ آیا بحث soap با res هست؟
پاسخگویی و مشاهده پاسخ های این سوال تنها برای اعضای ویژه سایت امکان پذیر است .
چنانچه تمایل دارید به همه بخش ها دسترسی داشته باشید میتوانید از این بخش لایسنس این آموزش را خریداری نمایید .