در اینجا، یک سری نکات را در طول برنامه نویسی، متذکر میشوم تا مدیریت کدهای شما را در اندروید راحتتر کند.
یک نکتهی دیگر را که باید متذکر شوم این است که همه اصطلاحاتی که در این مقاله استفاده میشوند بر اساس اندروید استادیو و مستندات رسمی گوگل است است؛ به عنوان نمونه عبارتهای ماژول و پروژه آن چیزی هستند که ما در اندروید استادیو به آنها اشاره میکنیم، نه آنچه که کاربران Eclipse به آن اشاره میکنند.
یک. برای هر تکه کد و یا متدی که مینویسید مستندات کافی قرار دهید و اگر این متد نیاز به مجوز خاصی دارد مانند نمونه زیر، آن را حتما ذکر کنید:
/** * * <p> * check network is available or not <br/> * internet connection is not matter,for check internet connection refer to IsInternetConnected() Method in this class * </p> * <p> * Required Permission : <b>android.permission.ACCESS_NETWORK_STATE</b> * </p> * @param context * @return returns true if a network is available */ public boolean isNetworkAvailable(Context context) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); return activeNetworkInfo != null && activeNetworkInfo.isConnected(); }
public class ProjectSettings { public static NotificationsId=new NotificationsId(); public static UrlAddresss=new UrlAddresss(); public static SdPath=new SdPath(); ...... }
ProjectSettings.NotificationsId.UpdateNotificationId
سه. حداکثر استفاده از اینترفیس را به خصوص برای UI انجام بدهید:
به عنوان نمونه، بسیاری نمایش یک toast را به شکل زیر انجام میدهند:
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
public interface IMessageUI { void ShowToast(Context context,String message); } public class MessageUI impelement IMessageUI { public void ShowToast(Context context,string message) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } }
چهار. اگر برای اولین بار است وارد اندروید میشوید، خوب چرخههای یک شیء، چون اکتیویتی یا فراگمنت را یاد بگیرید تا در آینده با مشکلات خاصی روبرو نشوید.
چون اکتیویتی در حالت stop بوده و بعد از آن به حالت Resume رفته و تا موقعی که این اکتیویتی از حافظه خارج نشود یا گوشی چرخش نداشته باشد، واکشی دیتاها صورت نخواهد گرفت. پس بهترین مکان در این حالت، رویداد OnStart است که در هر دو وضعیت صدا زده میشود؛ یا اینکه در OnRestatr روی آداپتور تغییرات جدید را اعمال کنید تا نیازی به واکشی مجدد دادهها نباشد.
تا بدینجا اکتیویتی مشکلی ندارد و میتواند به عملیات پاسخ دهد ولی اگر قسمتی از اکتیویتی در زیر لایهای از UI پنهان شود، به عنوان مثال دیالوگی باز شود که قسمتی از اکتیویتی را بپوشاند و یا منویی همانند تلگرام قسمتی از صفحه را بپوشاند، اکتیویتی اصطلاحا در حالت Pause قرار گرفته و بدین ترتیب رویداد OnPause اجرا میگردد. اگر همین دیالوگ بسته شود و مجددا اکتیویتی به طور کامل نمایان گردد مجددا رویداد OnResume اجرا میگردد.
از رویداد Onresume میتوانید برای کارهایی که بین زمان آغاز اکتیویتی و برگشت اکتیویتی مشترکند استفاده کرد. اگر به هر نحوی اکتیویتی به طور کامل پنهان شود٬، به این معناست که شما به اکتیویتی دیگری رفتهاید رویداد OnStop اجرا شدهاست و در صورت بازگشت، رویداد OnRestart اجرا خواهد شد. ولی اگر مدت طولانی از رویداد OnStop بگذرد احتمال اینکه سیستم مدیریت منابع اندروید، اکتیویتی شما را از حافظه خارج کند زیاد است و رویداد OnDestroy صورت خواهد گرفت. در این حالت دفعه بعد، مجددا همه عملیات از ابتدا آغاز میگردند.
شش. اگر برنامه شما قرار است در چندین حالت مختلفی که اتفاق میافتد، یک کار خاصی را انجام دهد، برای برنامهتان یک Receiver بنویسید و در آن کدهای تکراری را نوشته و در محلهای مختلف وقوع آن رویدادها، رسیور را صدا بزنید. برای نمونه برنامه تلگرام یک سرویس پیام رسان پشت صحنه دارد که در دو رویداد قرار است اجرا شوند. یکی موقعی که گوشی بوت خود را تکمیل کرده است و در حال آغاز فرایندهای سیستم عامل است و دیگر زمانی است که برنامه اجرا میشود. در اینجا تلگرام از یک رسیور سیستمی برای آگاهی از بوت شدن و یک رسیور داخل برنامه جهت آگاهی از اجرای برنامه استفاده میکند و هر دو به یک کلاس از جنس BroadcastReceiver متصلند:
<receiver android:name=".AppStartReceiver" android:enabled="true"> <intent-filter> <action android:name="org.telegram.start" /> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
public class AppStartReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { AndroidUtilities.runOnUIThread(new Runnable() { @Override public void run() { ApplicationLoader.startPushService(); } }); } }
برنامه تلگرام حتی برای حالتهای پخش هم رسیورها استفاده کرده است که در همین رسیور وضعیت تغییر پلیر مشخص میشود:
<receiver android:name=".MusicPlayerReceiver" > <intent-filter> <action android:name="org.telegram.android.musicplayer.close" /> <action android:name="org.telegram.android.musicplayer.pause" /> <action android:name="org.telegram.android.musicplayer.next" /> <action android:name="org.telegram.android.musicplayer.play" /> <action android:name="org.telegram.android.musicplayer.previous" /> <action android:name="android.intent.action.MEDIA_BUTTON" /> <action android:name="android.media.AUDIO_BECOMING_NOISY" /> </intent-filter> </receiver>
هفت. اگر از یک ORM برای لایه دادهها استفاده میکنید (قبلا در سایت جاری در مورد ORMهای اندروید صحبت کردهایم و ORMهای خوش دستی که خودم از آنها استفاده میکنم ActiveAndroid و CPORM هستند که هم کار کردن با آنها راحت است و هم اینکه امکانات خوبی را عرضه میکنند) در این نوع ORMها شما نباید انتظار چیزی مانند EF را داشته باشید و در بعضی موارد باید کمی خودتان کمک کنید. به عنوان مثال در Active Android برای ایجاد یک inner join باید به شکل زیر بنویسید:
From query= new Select() .from(Poem.class) .innerJoin(BankPoemsGroups.class) .on("poems.id=bank_poems_groups.poem") .where("BankGroup=?", String.valueOf(groupId)); return query.execute();
@Table(name="poems") public class Poem extends Model { public static String tableName="poems"; public static String codeColumn="code"; public static String titleColumn="title"; public static String bookColumn="book"; ...... @Column(name="code",index = true) public int Code; @Column(name="title") public String Title; @Column(name="book") public Book Book; .....}
From query= new Select() .from(Poem.class) .innerJoin(BankPoemsGroups.class) .on(Poem.TableName+"."+ Poem.IdColumn+"="+ BankPoemsGroups.TableName+"."+ BankPoemsGroups.PoemColumn) .where(Poem.BankGroupColumn+"=?", String.valueOf(groupId)); return query.execute();
public class QueryConcater { public String GetInnerJoinQuery(String table1,String field1,String table2,String field2) { String query=table1 +"." +field1+"="+table2+"."+field2; return query; } ...... }
return new Select() .from(Color.class) .innerJoin(ProductItem.class) .on(queryConcater.GetInnerJoinQuery(ProductItem.TableName, ProductItem.ColorColumn, Color.TableName)) .where(queryConcater.WhereConditionQuery (ProductItem.TableName, ProductItem.ProductColumn), productId) .execute();
هشت. سعی کنید همیشه از یک سیستم گزارش خطا در اپلیکیشن خود استفاده کنید. در حال حاضر معروفترین سیستم گزارش خطا Acra است که میتوانید backend آن را هم از اینجا تهیه کنید و اگر هم نخواستید، سایت Tracepot امکانات خوبی را به رایگان برای شما فراهم میکند. از این پس با سیستم آکرا شما به یک سیستم گزارش خطا متصلید که خطاهای برنامه شما در گوشی کاربر به شما گزارش داده خواهد شد. این گزارشها شامل:
- وضعیت گوشی در حین باز شدن برنامه و در حین خطا چگونه بوده است.
- مشخصات گوشی
- این خطا به چه تعداد رخ داده است و برای چه تعداد کاربر
- گزارش گیری بر اساس اولین تاریخ رخداد خطا و آخرین تاریخ، نسخه سیستم عامل اندروید، ورژن برنامه شما و...
نه. آکرا همانند Elmah نمیتواند خطاهای catch شده را دریافت کند. برای حل این مشکل عبارت زیر را در catchها بنویسید:
ACRA.getErrorReporter().handleException(caughtException);
نمونه اشتباه:
public void CopyFile(String source,String destination,CopyFileListener copyFileListener) { try { InputStream in = new FileInputStream(source); OutputStream out = new FileOutputStream(destination); long fileLength=new File(source).length(); // Transfer bytes from in to out byte[] buf = new byte[64*1024]; int len; long total=0; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); total+=len; copyFileListener.PublishProgress(fileLength,total); } in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } }
public void CopyFile(String source,String destination,CopyFileListener copyFileListener) throws IOException { InputStream in = new FileInputStream(source); OutputStream out = new FileOutputStream(destination); long fileLength=new File(source).length(); // Transfer bytes from in to out byte[] buf = new byte[64*1024]; int len; long total=0; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); total+=len; copyFileListener.PublishProgress(fileLength,total); } in.close(); out.close(); }