معرفی library های مفید اندروید

پیش نوشت:از اونجایی که مکانیزم خاصی برای به اشتراک گذاری لینک های مفید ندارم , تصمیم گرفتم اگر منبع و یا سایت خوبی دیدم به صورت یک پست مستقل معرفی کنم و به سایت اصلی لینک بدم.

اگر بخواهید به صورت حرفه ای با اندروید کار کنید libraryهای بسیار مفیدی توسط سایر برنامه نویسان ایجاد شده است که می توانید از آن ها استفاده کنید.برای دیدن لیست کامل این کتابخانه ها روی این لینک کلیک کنید.

Name Description
Retrofit A type-safe REST client for Android which intelligently maps an API into a client interface using annotations.
Glide A powerful image downloading and caching library for Android.
ButterKnife Using Java annotations, makes Android development better by simplifying common tasks.
Parceler Android Parcelable made easy through code generation
IcePick Android Instance State made easy
LeakCanary Catch memory leaks in your apps
Espresso Powerful DSL for Android integration testing
Robolectric Efficient unit testing for Android

 

lifecycle یک Activity

همانطور که در پستهای قبلی به lifecycle یک activity اشاره کردم,طول عمر یک activity در واقع از زمان ایجاد آن activity تا زمان حذف آن activity توسط سیستم و یا کاربر ادامه دارد.activity به ازای کارهایی که کاربر با صفحه انجام می دهد ممکن است وضعیتش به وضعیت های مختلف تغییر کند.برای هر وضعیت یک callback داریم که می توانیم از callbackها برای انجام یک سری کارها استفاده کنیم.کد زیر پیاده سازی کننده تمام callback های مهم در lifecycle یک activity است.

public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}

همچنین شکل زیر نشان دهنده lifecycle یک activity  است.توجه کنید که مستطیل ها درواقع همان callbackهایی هستند که در کد بالا پیاده سازی کرده ایم.
activity-lifecycle

کل طول عمر یک activity بین متدهای onCreate() و onDestroy() خلاصه شده است.در متدها onCreate() ما باید کارهای اولیه مربوط به راه اندازی یک activity را انجام دهیم و همچنین در onDestroy() کارهای مربوط به آزاد سازی منابع را انجام دهیم.برای مثال اگر می خواهیم یک thread برای انجام عملیات دانلود یک فایل از اینترنت را اجرا کنیم باید آن را onCreate() اجرا کنیم و در onDestroy() آن thread را منوقف کنیم.
کل زمانی که کاربر می تواند activity را ببیند و با آن کار کند بین متدهای onStart() و onStop() خلاصه شده است.زمانی که onStop() صدا زده می شود activity دیگر برای کاربر قابل مشاهده نیست.
طول عمر زمانی کهactivity بالاتر از باقی activityها قرار دارد و فوکس بر روی آن است بین onResume() و onPause() است.onPause زمانی صدا زده می شود که مثلا دستگاه به خواب رفته یا یک دیالوگ باز شده است.برای این که ما به صورت مکرر ممکن است بین این دو وضعیت سوئیج کنیم بنابراین نیاز است کارهای سنگینی در این دو متد انجام ندهیم که موجب کندی نشود.

Activity #2

اجرا کردن یک Activity

برای اجرای Activity به راحتی می توانید یک Intent ایجاد کنید و مشخص کنید که چه activity را می خواهید اجرا کنید.همچنین می توانید به intent مقداری داده اضافی برای اجرا شدن صحیح activity مورد نظر ارسال کنید.سپس شما باید متد startActivity() را صدا بزنید.
به کد زیر توجه کنید:

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

شرایطی وجود دارد که نیاز دارید کاری را انجام دهید که برنامه شما قادر به انجام آن کار نیست در این حالت در صورتیکه این امکان توسط برنامه دیگر فراهم شده است می توانید از آن برنامه ها استفاده کنید.برای مثال زمانیکه شما می خواهید یک ایمیل ارسال کنید نیاز نیست خودتان کدهای مربوط به آن را بنویسید و می توانید از intent خاصی بدین منظور استفاده کنید.به کد زیر دقت کنید:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

Intent.EXTRA_EMAIL مشخص کننده آدرسی است که می خواهید به آن ایمیل ارسال کنید.

اجرا کردن یک activity برای دریافت نتیجه

ممکن است شما نیاز داشته باشید تا یک activity را صدا بزنید و نتیجه را از آن activity دریافت کنید.در این صورت شما activity خود را به وسیله startActivityForResult() باید اجرا کنید.برای گرفتن نتیجه نیاز است callbackی به اسم onActivityResult() را پیاده سازی کنید:

private void pickContact() {
    // Create an intent to "pick" a contact, as defined by the content provider URI
    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    startActivityForResult(intent, PICK_CONTACT_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
        // Perform a query to the contact's content provider for the contact's name
        Cursor cursor = getContentResolver().query(data.getData(),
        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
        if (cursor.moveToFirst()) { // True if the cursor is not empty
            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
            String name = cursor.getString(columnIndex);
            // Do something with the selected contact's name...
        }
    }
}

درکد بالا نحوه انتخاب یک آیتم از دفترچه تلفن کاربر را مشاهده می کنید.

از بین بردن یک Activity

اگر نیاز داشتید یک activity را به صورت صریح shut down کنید از متد finish() می توانید استفاده کنید.البته در مطالب بعدی متوجه خواهید شد که نیاز نیست خودتان دستی این کار را انجام دهید و سیستم اندروید به صورت اتوماتیک این کار را برای ما انجام خواهد داد.

Activity #1

در پست نگاه اجمالی به یک برنامه اندروید component های اصلی سازنده یک برنامه اندروید را به صورت مختصر معرفی کردم.در این پست قصد دارم به صورت جزئی تر به Activity در اندروید بپردازم.

Activity یک ازcomponent های اصلی یک برنامه اندروید که فراهم کننده رابط کاربری است که کاربر می تواند به وسیله آن با سیستم ارتباط برقرار کند.در صورتیکه برنامه نویس وب بوده باشید یک Activity را می توانید مانند یک صفحه html در نظر بگیرید.

هر برنامه معمولا شامل چندین Activity است که که بدون وابستگی به یکدیگر با هم ارتباط دارند.همچنین اغلب در یک برنامه یک activity به عنوان activity اصلی شناخته می شود و مشخص کننده اولین صفحه ای است که کاربر پس از ورود به برنامه با آن روبرو می شود.

هر activity می تواند یک activity دیگر را اجرا کند در این حالت  activity جدید فعال می شود و activity قبلی به حالت stop شده توسط سیستم در یک stack به اسم back stack نگه داری می شود.به عبارتی زمانی کهactivity جدید اجرا شود سیستم آنactivity را درون stack اضافه می کند. هر زمان کاربر کارش با activity جدید تمام شد و دکمه back را فشار داد سیستم آن activity را از stack بیرون می کشد و  از بین (destroyed)می برد.در این حالت activity قبلی دوباره شروع به کار می کند.

به ازای هر تغییر وضعیت در activity یک callback صدا زده می شود.برای مثال در زمانی که activity به حالت stopped می رود یک callback اجرا می شود.در هر callback ما می توانیم کارهای مربوط به آن حالت را انجام دهیم برای مثال دروضعیت stopped باید از شر اشیا بزرگ (large objects) خلاص شویم و همچنین  کارهای سنگین مثل کارهای شبکه و دیتابسی را متوقف کنیم.همچنین به طور مثال اگر وضعیت activity به وضعیت resume تغییر کرد باید کارهای لازم در آن وضعیت را انجام دهیم.

ساخت یک Activity

برای ساخت یک activity نیاز دارید تا کلاسی بسازید که از کلاس پایه Activity به ارث برده است.همچنین نیاز است که یک سری از callback هایی را که سیستم در هنگام تغییر وضعیت activity أن ها را صدا می زند را پیاده سازی کنید.دو  callback اصلی عبارت انداز :

onCreate():این متد را سیستم زمانی که activity ما ساخته شد صدا می زدند در این متد باید کارهای اساسی مثل تعیین رابط کاربری یک activity به وسیله setContentView() انجام شود.

onPause():این متد زمانی که کاربر از activity جاری خارج می شود صدا زده می شود.البته صدا زده شدن این متد به این معنی نیست که activity از بین می رود.کارهایی مثل نگهداری و ذخیره اطلاعات اساسی در این متد می تواند انجام شود زیرا ممکن است کاربر دیگر به این activity باز نگردد و داده های آن از بین برود.

پیاده سازی رابط کاربری

هرactivity دارای یک رابط کاربری و هر رابط کاربری دارای viewهای متفاوتی است.

هر رابط کاربری از ساختار سلسله مراتبی تعدادی از viewها که از کلاس View به ارث برده اند ایجاد شده است.هر view یک قسمت مشخص مستطیلی شکل را از یک activity اشغال می کند که کاربر می تواند به وسیله آن با برنامه ارتباط برقرار کند.برای مثال دکمه یک view است که کاربر برای انجام دادن یک کار مشخص روی آن کلیک میکند.

اندروید شامل یک سری از viewهای از پیش ساخته شده برای استفاده در برنامه های مختلف است.برای مثال Widgetهایی مانند text field ,checkbox و image.همچنین شامل Layoutهای متفاوتی است که می توانند سامان دهنده viewهای متفاوتی باشند.Layoutها از کلاس ViewGroup مشتق شده اند.

همچنین در صورت نیاز می توانیم از کلاس های View و  ViewGroup به ارث ببریم و viewهای مورد نیاز خودمان را بسازیم.

نحوه تعریفactivity در manifest

برای دسترسی سیستم به activity های برنامه به منظور اجرای آن activityها نیاز است تا آنها را در فایل manifest تعریف کنیم.بدین منظور مانند زیر عمل می کنیم:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

خواص متفاوتی وجود دارند که در تگ activity می توانیم تعریف کنیم.برای مثال می توانیم برای activity خود یک label در نظر بگیریم و یا مثلا یک icon مشخص کنیم ولی هیچ کدام از این ها برای تعریف activity در فایل manifest اجباری نیستند.به غیر از android:name که نام activity است باید تعریف شود.
بعد از اینکه برنامه را منتشر کردیم نباید نام activity را تغییر دهیم زیرا ممکن است باعث از کار افتادن برنامه بعد از آپدیت برنامه توسط کاربرها شود.یک سری تغیرات را نباید بعد از پابلیش برنامه انجام دهیم.لیست آن ها را می توانید در اینجا مشاهده کنید.

استفاده ازintent filterها

یک activity ممکن است شامل چند intent filter مختلف باشد و هر intent filter مشخص کننده نحوه اجرا کردن این activity توسط سایر componentها است.اگر برنامه ای که می نویسید قرار نیست توسط سایر برنامه های دیگر اجرا شود بنابراین نیاز نیست که intent filter خاصی به غیر از android.intent.action.MAIN که برای مشخص کننده نقطه ورود به برنامه هست نوشته شود.

دریافت implicit intent

به کمک implicit intent می توانیم از سایر برنامه ها intent دریافت کنیم.بدین جهت برای هر component در فایل manifest می توانیم تعدادی <intent-filter> تعریف کنیم.هر intent filter مشخص کننده نوع intentهایی است که برنامه ما با توجه به action, data, و  category می تواند قبول کند.سیستم تنها در صورتی یک intent را به برنامه ما ارسال می کند که با یک intent filter همخوانی داشته باشید.

نکته:explicit intent ها بدون توجه به intent filter تحویل داده می شوند.

هر component باید intent filterهای مجزایی به ازای کارهایی که می تواند انجام دهد تعریف کند.به عنوان مثال اگر یک activity در یک برنامه گالری , عکس را نمایش می دهد و هم عکس را ویرایش می کند نیاز دارد که دو intent filter تعریف کند.یکی برای نمایش و یکی برای ویرایش و زمانی که activity باز شد با توجه به intent ارسالی رفتار مناسب را برگزیند.

مثال زیر نمایش دهنده تعریف یک intent filter برای یک activity است:

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

برای اینکه سیستم اندروید یک intent را به برنامه تحویل دهد باید action,categoryو data باintent filter تعریفی ما مطابقت داشته باشد و حتی اگر یکی از آنها با intent filter همخوانی نداشت intent به برنامه ارسال نمی شود.
نکته:برای دریافت implicit intents نیاز است category به اسم CATEGORY_DEFAULT تعریف شود.در غیر این صورت ما implicit intentی دریافت نخواهیم کرد.
حال به مثال زیر دقت کنید:

<activity android:name="MainActivity">
    <!-- This activity is the main entry, should appear in app launcher -->
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name="ShareActivity">
    <!-- This activity handles "SEND" actions with text data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <action android:name="android.intent.action.SEND_MULTIPLE"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="application/vnd.google.panorama360+jpg"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="video/*"/>
    </intent-filter>
</activity>

MainActivity که به عنوان activity اول تعریف شده و به عنوان نقطه ورودی برنامه محسوب می شود. ACTION_MAIN مشخص کننده این موضوع است و همچنین مشخص می کند که این activity انتظار دریافت داده خاصی به وسیله intent را ندارد.CATEGORY_LAUNCHER مشخص کننده این است که از icon این activity به عنوان icon برنامه استفاده شود.البته اگر هیچ iconی مشخص نکرده بود در این صورت icon از تگ application خوانده خواهد شد.

ShareActivity نیز امکان به اشتراک گذاری متن و مدیا را برای ما فراهم می کند.

استفاده از pending intent:

pending intent یک توکن است که برنامه های دیگر داده می شود و به وسیله آن برنامه های دیگر می تواند یک قطعه کد با دسترسی های مثل برنامه ما را اجرا کنند.به عبارت دیگر این pendingintent به ما اجازه می دهد که یک intent ایجاد کنیم و آن را به یک برنامه دیگر پاس دهیم و اجازه بدهیم که آن برنامه آن intent را در آینده با دسترسی های مشابه دسترسی های برنامه ما اجرا کند.

موارد استفاده:
تعریف یک Intent برای اجرا در قسمت Notification.( سیستم اندروید از طریق NotificationManagerدر آینده Intent مورد نظر را اجرا خواهد کرد)
تعریف یک Intent برای اجرا در قسمت App Widget(صفحه اصلی Intent مورد نظر را اجرا خواهد کرد)
تعریف یک Intent برای اجرا در آینده( AlarmManager در سیستم اندروید Intent مورد نظر را اجرا خواهد کرد)

اجباری کردن app chooser

زمانی که ما از implicit intent استفاده می کنیم سیستم به دنبال برنامه هایی می گردد که قابلیت اجرا کردن درخواست ما را داشته باشند.در این صورت اگر بیشتر از یک برنامه این قابلیت را داشته باشند سیستم به کاربر قابلیت انتخاب یکی(app chooser) از آنها را می دهد.همیچنین کاربر نیز می تواند با انتخاب یکی از آن ها به عنوان پیش فرض آن برنامه را برای یک intent خواص انتخاب کند.این موضوع به کاربر کمک می کند که مثلا هر زمان که خواست یک صفحه وب را مشاهده کند,برنامه گوگل کروم باز شود و هر بار از کاربر سوال نشود.

app chooser

اما زمان هایی و جود دارد که ما می خواهیم  کاربر را فورس کنیم تا همیشه از لیست برنامه ها برای پردازش یک intent یکی را انتخاب کند.مثلا فرض کنید میخواهید به وسیله ACTION_SEND یک intent برای به اشتراک گذاری محتوا بسازید.در این صورت صحیح نیست که مثلا اگر کاربر app فیس بوک را به عنوان پیش فرض انتخاب کرده باشد همیشه با آن بخواهد محتوا را با فیس بوک به اشتراک بگذارد.

بنابراین نیاز است app chooser را اجباری کنیم.برای این موضوع یک intent به وسیلهcreateChooser() می سازیم.

Intent sendIntent = new Intent(Intent.ACTION_SEND);
...

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);

// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

Intent و Intent Filter

Intent یک شی حاوی یک سری اطلاعات که برای درخواست یک کار از  برنامه دیگر مورد استفاده قرار می گیرد.همچنین Intent ارتباط بین اجزای اصلی یک برنامه را تسهیل می کند.سه مورد کاربرد اصلی را می توان برای Intent نام برد:

  1. اجرا کردن یک activity
  2. اجرا کردن یک service
  3. تحویل دادن یک broadcast

انواع Intent

Intentها را می توان به دو نوع تقسیم بندی کرد:

  1. Explicit intent:استفاده از intent به وسیله نام کامل component مورد نظر.برای مثال زمانی که شما می خواهید یک activity را اجرا کنید و آن activity در برنامه خودتان وجود دارد.
  2. Implicit intent:به جای استفاده از نام کامل component, یک action کلی را مشخص می کنید ,که کدام یک از سایر برنامه های قابلیت اجرای action مورد نظر ما را دارند.برای مثال اگر می خواهید موقعیت کاربر را در نقشه نشانش دهید از Implicit intent استفاده می کنید و به وسیله آن یک درخواست برای برنامه هایی که قادر به نشان دادن نقشه هستند ارسال می کنید.

intent-filters

شکل بالا نشان دهنده چگونگی باز کردن یک activity بوسیلهImplicit intent است.

زمانی که ما از Implicit intent استفاده می کنیم سیستم اندروید بدنبال برنامه ای می گردد که قادر به پردازش درخواست ما باشد.سیستم از طریق intent filterهایی که در مانیفست باقی برنامه نوشته شده این کار را انجام می دهد.اگر یک برنامه پیدا کند آن برنامه را باز می کند ولی اگر بیشتر از یک برنامه پیدا کند سیستم به ما قابلیت انتخاب بین آنها را می دهد.

intent filter عبارتی است که در  فایل manifest نوشته می شود و به عبارتی انواع intentی است که برنامه تمایل به دریافت آن ها را دارد.همچنین با تعریف intent filter برای یک activity باقی برنامه ها قادر به اجرای activity شما از طریق intentها خواهند بود.

نکته امنیتی:برای اجرا کردن سرویس ها همیشه سعی کنید از explicit intent استفاده کنید.بنابراین سعی کنید که intent filter برای سرویسهای خود تعریف نکنید.از Android 5.0 به بعد اگر بخواهید bindService() را به وسیله implicit intent فراخوانی کنید سیستم خطا می دهد.

استفاده ازintent

شی intent شامل اطلاعاتی که سیستم برای اجرا کردن یک component از آن استفاده می کند و همچنین دارای اطلاعاتی برای انجام دادن عملیات مورد نظر توسط component درخواستی نیز است.

اطلاعات اصلی intent:

  • نام component:این مقدار optional است .ولی وجه تمایز بین یک implicit intent و explicit intent است.به عبارتی با مشخص کردن نام component باعث ساخت یک explicit intent می شویم ولی اگر ذکر نکنیم باعث می شود که سیستم تصمیم بگیرد که کدام component  از کدام app باید اجرا شود.همچنین برای set کردن این مقدار از نام کامل باید استفاده شود.برای مثال com.example.ExampleActivity.
  • Action:این مقدار مشخص کننده یک action عمومی برای انجام است.برای مثال استفاده از ACTION_VIEW به همراه startActivity() زمانی استفاده می شود که نیاز باشد به کاربر چیزی را نشان دهیم.مثل عکسی که در گالری ذخیره شده و یا آدرسی در نقشه.همچنین از ACTION_SEND به همراه startActivity() زمانی استفاده می شود که داده ای داریم که کاربر می تواند آن را به اشتراک بگذارد.
  • Data:یک URI است که به یک داده ی خاص اشاره می کند.همچنین بهتر است هنگام استفاده از اطلاعات MIME type آن را نیز مشخص کنید.برای مثال activity که قابلیت نمایش عکس را دارد با activity که قابلیت پخش فایل موسیقی را دارد با هم متفاوت هستند برای همین موضوع بهتر است MIME type را مشخص کنیم.MIME type کمک می کند که سیستم اندروید بتواند بهترین component را برای رسیدگی به درخواست ما پیدا کند.
  • Category:یک رشته حاوی اطلاعاتی اضافی برای مشخص کردن componentهایی که باید intent مورد نظر ما را هندل کنند.

همچنین intent شامل یک سری اطلاعات دیگر نیز می شود:

  • Extra:یک  Key-value pairs است که به component مورد نظر برای انجام یک action ارسال می شود.
  • Flags:مشخص کننده متادیتا برای یک intent است و همچنین تعیین می کند که سیستم اندروید چگونه یک activity اجرا کند(مثلا  به کدام Task اختصاص دهد)  و همچنین  بعد از اجرا چگونه با activity رفتار کند.

مثالی از explicit intent:

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);

مثالی از implicit intent:

// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");

// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
    startActivity(sendIntent);
}

کد بالا واضح است ولی اگر به if داخل کد دقت کنید متوجه خواهید شد که ابتدا با این کار از وجود  component مورد نظر برای پاسخگویی به intent خودمان مطمئن شده ایم  و سپس startActivity را صدا می زنیم.به این علت که اگر componentی برای هندل کردن intent ما در سیستم وجود نداشته باشد برنامه ما کرش خواهد کرد.

 

معماری اندروید

سیستم عامل اندروید یک سیستم لینوکسی و سورس باز است که در شکل زیر اجزای اصلی تشکیل دهنده سیستم اندروید را ملاحظه می کنید:

android-stack_2x

Linux Kernel

پایه و اساس سیستم عامل اندروید هسته لینوکس است.برای مثال مباحثی مانند مدیریت threadها و همچنین مدیریت memory در Android Runtime (ART) برپایه کرنل لینوکس بنا شده است.همچنین این موضوع باعث شده که از مزایا و امکانات امنیتی هسته لینوکس نیز بهره ببرد.

Hardware Abstraction Layer

یک واسط برای فراهم کردن امکانات سخت افزاری به Java API framework. است.مثلا امکاناتی مثل دوربین را فراهم می کند.

Android Runtime

برای اندروید ۵ به بالا هر برنامه روی یک پروسس و هر پروسس دارای یک Android Runtime مختص به خودش است.ART برای اجرا کردن چندین ماشین مجازی بصورت همزمان و همچنین اجرای فایل های DEX در یک محیط با memory بسیار اندک نوشته شده است.DEX فایل ها در واقع کدهای جاوا هستند که توسط ابزار بیلد مثل Jack به آن تبدیل می شوند که قابلیت اجرا شدن روی ART را دارا هستند.

مزایا ART:

  • Ahead-of-time (AOT) and just-in-time (JIT) compilation
  • Optimized garbage collection
  • امکانات دیباگ و خطایابی راحت تر

در اندروید های ۵ به قبل Dalvik در واقع Android runtime در سیستم اندرویدی بوده.اگر برنامه شما در ART اجرا می شود در Dalvik نیز می تواند اجرا شود ولی برعکس آن صدق نمی کند.

Native C/C++ Libraries

بسیاری از اصلی ترین اجزای سیستم اندروید بوسیله کتابخانه های سی و سی پلاس پلاس نوشته شده اند به عنوان مثال می توان از ART و HAL نام برد.

Java API Framework

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

System Apps

یک سری برنامه به صورت پیش فرض در اندروید وجود دارند که کار کردن با این سیستم را برای ما ممکن می سازند.مانند برنامه sms برنامه calendar , مرورگر وب و …

 

معرفی Butter Knife

butterknife
Butter Knife کار پیدا کردن و cast اتوماتیک view های مورد نظر ما را از طریق Annotation انجام می دهد و استفاده از آن بسیار ساده است.همچنین در آن از reflection استفاده نشده و در واقع کدها generate می شوند.

به کدهای زیر توجه کنید:

mUserNameEditText = (EditText) view.findViewById(R.id.username);
mPasswordEditText = (EditText) view.findViewById(R.id.password);

حال به این مثال دقت کنید:

  @BindView(R.id.username) TextView username;
  @BindView(R.id.password) TextView password;

در مثال اول  مجبوریم هر جای که نیاز بود یک view خاص را پیدا کنیم یک cast اضافی نیز انجام دهیم.اما Butter Knife این کار را برای ما راحت تر و تمیزتر انجام می دهد.
برای استفاده از Butter Knife در Activity مانند زیر عمل می کنیم:

class ExampleActivity extends Activity {
    @BindView(R.id.title) TextView title;
    @BindView(R.id.subtitle) TextView subtitle;
    @BindView(R.id.footer) TextView footer;

    @Override public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.simple_activity);
    ButterKnife.bind(this);
// TODO Use fields...
    }
}

یک امکان دیگری که Butter Knife به ما می دهد امکان bind کردن resourceهای مختلف است:


class ExampleActivity extends Activity {
  @BindString(R.string.title) String title;
  @BindDrawable(R.drawable.graphic) Drawable graphic;
  @BindColor(R.color.red) int red; // int or ColorStateList field
  @BindDimen(R.dimen.spacer) Float spacer; // int (for pixel size) or float (for exact value) field
  // ...
}

امکان جالب دیگر قابلیت جمع کردن چند view در یک لیست است:

@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List&lt;EditText&gt; nameViews;

و سپس انجام یک عملیات روی تمامی view ها:


ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);

برای تعریف DISABLE و ENABLED هم مانند زیر عمل خواهیم کرد:


static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {
  @Override public void apply(View view, int index) {
    view.setEnabled(false);
  }
};
static final ButterKnife.Setter<View, Boolean> ENABLED = new ButterKnife.Setter<View, Boolean>() {
  @Override public void set(View view, Boolean value, int index) {
    view.setEnabled(value);
  }
};

مورد دیگر امکان listen کردن روی رویداد  OnClick  مربوط به view ها است:


@OnClick(R.id.submit)
public void submit() {
  // TODO submit data to server...
}

همچنین به روش زیر نیز می توانید از این امکان استفاده کنید:


@OnClick(R.id.submit)
public void sayHi(Button button) {
  button.setText("Hello!");
}

علاوه بر آن می توانید مثال بالا را برای چند view نیز انجام دهید:


@OnClick({ R.id.door1, R.id.door2, R.id.door3 })
public void pickDoor(DoorView door) {
  if (door.hasPrizeBehind()) {
    Toast.makeText(this, "You win!", LENGTH_SHORT).show();
  } else {
    Toast.makeText(this, "Try again", LENGTH_SHORT).show();
  }
}

به علت آنکه lifecycle یک fragment با یک activity متفاوت است در fragmentها نیاز است به شکل زیر عمل کنیم:

public class FancyFragment extends Fragment {
  @BindView(R.id.button1) Button button1;
  @BindView(R.id.button2) Button button2;
  private Unbinder unbinder;

  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fancy_fragment, container, false);
    unbinder = ButterKnife.bind(this, view);
    // TODO Use fields...
    return view;
  }

  @Override public void onDestroyView() {
    super.onDestroyView();
    unbinder.unbind();
  }
}

برای نصب نسخه جاری می توانید از کدهای زیر در GRADLE استفاده کنید:
compile 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'

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

Permission در اندروید

اندروید یک سیستم عامل privilege-separated است.به عبارتی هر برنامه به وسیله Linux user ID و  group ID منحصر بفرد خود اجرا می شود.اندروید بوسیله سیستم permission به یک برنامه دسترسی می دهد که بتواند یک عملیات خاص را انجام دهد.در این پست سعی می کنم نکاتی درباره مکانیزم permission در اندروید ارائه کنم.

امنیت در اندروید طوری پیاده سازی شده است که به صورت پیش فرض هیچ برنامه ای قادر نخواهد بود عملیاتی را انجام دهد که تاثیر منفی روی باقی برنامه ها,سیستم عامل و یا کاربران بگذارد.برای مثال نمی توان به داده های خصوصی مثل ایمیل ها و یا دفترچه تلفن دسترسی پیاده کرد.نمی توان فایل های برنامه های دیگر را خواند و یا تغییر داد.نمی توان عملیات شبکه ای انجام داد و یا مثلا  از به خواب رفتن دستگاه جلوگیری کرد.

هر برنامه روی یک پروسس منحصر به فرد خودش اجرا می شود بنابراین برای دسترسی به داده ها و یا به اشتراک گذاری آنها باید به صورت صریح به سیستم اندروید دسترسی مورد نظر خود را اعلام کنیم.با اعلام کردن این موضوع اندروید می تواند از کاربر نهایی برنامه تائیده بگیرد که آیا این دسترسی مورد قبول او هست یا نه؟

اینکه برنامه با چه تکنولوژی و زبانی نوشته شده باشد فرقی نمی کند و نحوه اعمال موارد امنیتی برای تمام آن ها به یک شکل خواهد بود.

هر apk نیاز است توسط یک certificate که کلید خصوصی آن توسط برنامه نویس نگهداری می شود sign شود.البته نیازی نیست certificate معتبر باشد و می تواند self-signed certificate باشد.این کار برای شناسایی کردن برنامه نویس پروژه استفاده می شود.و همچنین باعث می شود سیستم به برنامه دسترسی یا عدم دسترسی به signature-level permissions را بدهد.علاوه برآن سیستم قادر خواهد بود به برنامه دسترسی یا عدم دسترسی به داشتن Linux identity مشابه را بدهد.

زمانی که یک برنامه  نصب می شود سیستم به آنLinux user ID  منحصر بفرد اختصاص می دهد. اگر همان برنامه را در دستگاه دیگری نصب کنیم  این شناسه متفاوت خواهد بود.ما می توانیم یک Linux user ID را در دو برنامه یکسان کنیم البته فقط در صورتیکه با یک certificate مشخص sign شده باشد.

جهت گرفتن دسترسی اضافه برای برنامه نیز می توان از تگ uses-permission در فایل مانیفست استفاده کرد.

در صورتیکه دسترسی های مورد نیاز برنامه دسترسی های معمولی باشند و خطری برای حریم خصوصی کاربر نداشتهباشند سیستم اندروید به صورت پیش فرض بدون سوال از کاربر نهایی دسترسی های مورد نیاز را قبول می کند ولی درصورتیکه دسترسی های خواسته شده ریسکی برای حریم خصوصی کاربر داشته باشند در این صورت اندروید از کاربر تائید خواهد گرفت.

البته گرفتن این تائیده در ورژن های مختلف متفاوت است:

  • اگر برنامه روی Android 6.0 (API level 23) و یا بالاتر اجرا شود و یا targetSdkVersion 23یا بالاتر باشد در این صورت برنامه درخواست دسترسی هایش را در زمان اجرا خواهد داد.در این حالت کاربر نیز میتواند هر زمانی که خواست یک دسترسی را از یک برنامه بگیرد.بنابراین به عنوان برنامه نویس باید همیشه حواسمان به چک دسترسی ها باشد و باید مطمئن شویم برنامه دسترسی مورد نظر هر زمان که به آن نیاز بود داشته باشد.
  • اگر برنامه روی Android 5.1 (API level 22) و یاtargetSdkVersion 22 و یا پایین تر اجرا می شود درخواست دسترسی در زمان نصب از کاربر گرفته می شود و اگر برنامه بروزرسانی شود و درخواست جدید را نیاز داشته باشد در این صورت باز هم از کاربر تائید گرفته می شود.در این حالت کاربر فقط در صورتی می تواند دسترسی خاصی را از برنامه بگیرد که تمام برنامه را پاک کند.

اغلب اگر به یک  API  دسترسی نداشته باشیم SecurityException دریافت خواهیم کرد ولی این موضوع برای همه جا صدق نمی کند.

تعیین اتوماتیک دسترسی ها

در طول زمان ممکن است محدودیت های جدید توسط پلتفورم اندروید به APIهای آن اضافه شود که نیاز به تعریف دسترسی در فایل مانیفست را داشته باشند.در این صورت برنامه هایی که از قبل نوشته شده اند نیازی به گرفتن مجوز برای دسترسی به آن APIها ندارند.البته این موضوع با targetSdkVersion کنترل خواهد شد.برای مثال محدودیت دسترسی  WRITE_EXTERNAL_STORAGE در API level 4 اضافه شده است اگر برنامه ما دارای targetSdkVersion ۳ باشد در این صورت این مجوز به صورت اتوماتیک به برنامه  اضافه خواهد شد.

برای مشاهده دسترسی اضافه شده در هر ورژن از این آدرس می توانید استفاده کنید.

Normal and Dangerous Permissions


دسترسی در اندروید در چند سطح طبقه بندی شده است که دو سطح اصلی آن عبارت اند از normal و dangerous .

دسترسی های normal به آن دسته از دسترسی هایی گفته می شود که یک برنامه نیاز دارد به اطلاعات و داده های خارج از sandbox خودش دسترسی پیدا کند و  ریسک کمی نیز برای کاربر و عملکرد سایر برنامه ها دارد.لیستی از آنها را در این آدرس می توانید ببینید.در دسترسی های normal سیستم به صورت اتوماتیک آنها را grant خواهد کرد.

دسترسی های  Dangerous آنهایی هستند که برنامه بخواهد به اطلاعات خصوصی کاربر دسترسی پیدا کند و یا تاثیری روی داده ها و عملکرد برنامه های دیگر داشته باشد.در این صورت سیستم حتما از کاربر تائیده خواهد گرفت.

Permission groups

هر دسترسی Dangerous در اندروید به یک Permission groups اختصاص دارد.اگر برنامه ما روی Android 6.0 (API level 23) و targetSdkVersion 23 نصب شده باشد اتفاقات زیر در زمان گرفتن دسترسی رخ خواهد داد:

  • اگر برنامه یک دسترسی خاصی را می خواهد که از قبل دسترسی دیگری در آن permission group درخواست نداده باشد سیستم اندروید از ما تائیده خواهد گرفت.
  • اگر برنامه یک دسترسی را درخواست کرد که از قبل دسترسی دیگری در آن permission group درخواست داده باشد سیستم اندروید به طور اتوماتیک درخواست ما را تائید خواهد کرد.

به عبارتی دیگر مثلا اگر برنامه دسترسی READ_CONTACTS را از قبل گرفته باشد چون WRITE_CONTACTS نیز در یک گروه با READ_CONTACTS قرار دارد در این صورت اگر برنامه نیازی به WRITE_CONTACTS داشته باشد درخواست ما تائید خواهد شد.

اگر برنامه روی Android 5.1 (API level 22) و یاtargetSdkVersion 22 و یا پایین تر اجرا می شود درخواست دسترسی در زمان نصب از کاربر گرفته می شود.

لیست زیر نمایش دهنده permission groupها در اندروید است:

Permission Group Permissions
CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE

تعریف Permission اختصاصی


ممکن است نیاز داشته باشید که برای برنامه خود یک Permission اختصاصی تعریف کنید و مثلا از کاربر برای انجام یک عمل خاص تائیده بگیرید.در این حالت می توانید از تگ <permission> در فایل AndroidManifest.xml  استفاده کنید.برای اطلاعات بیشتر به این آدرس مراجعه کنید.