کانال بله, جهت پشتیبانی و اطلاع رسانی کانال بله, جهت پشتیبانی و اطلاع رسانی
عضویت

آموزش Broadcast receiver در اندروید

این آموزش به شرح نحوه ی ایجاد و استفاده از کامپوننت نرم افزاری broadcast receiver در اندروید را شرح می دهد.

Broadcast receiver

شرح مفهوم broadcast receiver

Broadcast receiverیک کامپوننت نرم افزاری در اندروید است که به شما این امکان را می دهد تا به event های مختلف گوش فرا دهید. به عبارت دیگر broadcast receiver یک نوع گوش فراخوان به رخدادها است.
زمانی که رخداد یا event مورد نظر رخ می دهد، runtime سیستم اندروید تمامی receiver هایی که برای آن رخداد ثبت شده و به آن گوش می دهند را با خبر می کند.
برای مثال می توان به اپلیکیشن هایی اشاره کرد که به رخداد سیستمی ACTION_BOOT_COMPLETED گوش داده و زمانی که سیستم اندروید پروسه ی boot را به اتمام می رساند، اپلیکیشن را از این اتفاق با خبر می سازد.

پیاده سازی receiver

Receiver را بایستی در لایه ی XML، درون فایل تنظیمات AndroidManifest.xml تعریف نمایید.
یک receiver را می توان به صورت فوق تعریف کرد که روش static نام دارد یا می توان آن را به صورت dynamic در کلاس های جاوا با فراخوانی متد Context.registerReceiver() اعلان نمایید.
برای تعریف receiver لازم است از کلاس BroadcastReceiver ارث بری نمایید.
اگر event ای که broadcast receiver برای آن ثبت شده رخ دهد، سیستم اندروید متد onReceive() را از receiver را صدا می زند.

Lifecycle یا چرخه ی حیات کامپوننت broadcast receiver

پس از اینکه اجرای متد onReceive() از کلاس receiver به پایان رسید، سیستم اندروید این اجازه را دارد که receiver را برای استفاده ی مجدد در آینده (recycle) بازیافت نماید.

پردازش ناهمزمان (async processing)

پیش از API 11 توسعه دهندگان امکان اجرای هیچ گونه عملیات ناهمزمانی را در متد onReceive() نداشتند چرا که به محض به اتمام رسیدن اجرای متد onReceive() سیستم اندروید می توانست آن کامپوننت را بازیافت نماید. چنانچه عملیات طولانی برای اجرا وجود داشته باشد، در این شرایط بهتر است بجای broadcast receiver، یک کامپوننت سرویس را فراخوانی نمایید.
از ورژن 11 کتابخانه های اندروید، شما می توانید متد goAsync() را برای اجرای عملیات ناهمزمان فراخوانی نمایید. این متد در خروجی آبجکتی از جنس PendingResult برمی گرداند. لازم به ذکر است که تا زمانی که متد PendingResult.finish() را بر روی این آّبجکت فراخوانی نکرده اید، سیستم اندروید receiver را همچنان فعال در نظر می گیرد. از این طریق شما می توانید در receiver، عملیات ناهمزمان راه اندازی نمایید. بلافاصله پس از اینکه thread پردازش خود را به پایان می رساند، تسک (میزبان) متد finish() را صدا زده و به سیستم اندروید اعلان می کند که کامپوننت مورد بحث اکنون قابل بازیافت (استفاده ی مجدد و recycle) می باشد.

محدودیت هایی در تعریف و استفاده از broadcast receiver

از ویرایش 3.1 به بعد اندروید، چنانچه اپلیکیشن مورد نظر را کاربر راه اندازی نکرده یا اینکه آن را به صورت صریح از طریق منوی اندروید (Manage ▸ Application) خاتمه داده باشد، آنگاه سیستم به صورت پیش فرض تمامی receiver ها را از دریافت intent ها محروم خواهد نمود.
این یک امکان امنیتی است که به واسطه ی آن کاربر اطمینان دارد تنها اپلیکیشن هایی که خود به صورت صریح اجرا کرده قادر به دریافت broadcast intent ها خواهند بود.

نکته:

این بدین معنی نیست که کاربر بایستی اپلیکیشن را مجددا پس از reboot اجرا کند. بلکه سیستم اندروید به یاد دارد که کاربر قبلا آن را یکبار اجرا کرده است. بنابراین چنانچه کاربر اپلیکیشن را اجرا کرده باشد(آن را به صورت صریح نبسته باشد) سیستم اندروید پس از reboot به خاطر می آورد که برنامه ی مورد بحث یکبار قبلا اجرا شده و خود اقدام به اجرای مجدد آن می نماید.

ارسال broadcast به اپلیکیشن به منظور تست

دستورات زیر را در پنجره ی فرمان adb (adb command line tool) درج نمایید. اسم کلاس و پکیج که در دستور زیر مشاهده می کنید، می بایست قبلا در لایه ی XML، داخل فایل manifest تعریف شده باشد. شما بایستی intent و داده های کپسوله در آن را به کامپوننت مورد نظر ارسال نمایید. حال زمانی که broadcast سراسری به نام ACTION_BOOT_COMPLETED را به کامپوننت نرم افزاری مد نظر ارسال می کنید، چندین اتفاق متعدد در سیستم اندروید رخ می دهد.

# trigger a broadcast and deliver it to a component
adb shell am activity/service/broadcast -a ACTION -c CATEGORY -n NAME
# for example (this goes into one line)
adb shell am broadcast -a
android.intent.action.BOOT_COMPLETED -c android.intent.category.HOME -n
package_name/class_name

شرح مفهوم Pending Intent

Pending intent یک آبجکت متشکل از درخواست عملیات دلخواه (راه اندازی یک activity، اجرای سرویس یا ارسال broadcast) اطلاعات و جزئیات عملیات (کپسوله شده در یک intent) و در نهایت یک context می باشد. نمونه ی مشتق شده از کلاس PendingIntent را می توان به سایر اپلیکیشن های اندرویدی واگذار کرد تا حتی پس از بسته شدن برنامه ی میزبان یا ایجاد کننده ی pending intent، بتواند عملیاتی را به انجام برساند. زمانی که توسعه دهنده pending intent را به اپلیکیشن دیگری می فرستد، در عمل به آن برنامه اجازه می دهد تا مجوزهای اپلیکیشن ایجاد کننده ی pending intent را جهت انجام عملیات خاص داشته باشد. این بر خلاف intent های متعارف است که اپلیکیشن های دریافت کننده ی آن می بایست برای انجام عملیات از مجوزهای خود استفاده کنند.
به عبارت دیگر pending intent یک token است که برنامه نویس به اپلیکیشن دیگری مانند notification manager، alarm manger یا هر اپلیکیشن دیگری می دهد. با ارسال این نوع intent به اپلیکیشن دیگر، در واقع توسعه دهنده به اپلیکیشن مقصد این اجازه را می دهد تا مجوزهای اپلیکیشن فرستنده را جهت اجرای کد و عملیات از پیش تعریف شده داشته باشد.
برای اجرای یک broadcast از طریق pending intent، ابتدا متد getBroadcast() را فراخوانی نموده و آبجکتی از کلاس PendingIntent را دریافت نمایید. حال جهت اجرا activity از طریق pending intent، متد PendingIntent.getActivity() را صدا زده و activity مورد نظر را به عنوان خروجی از این متد دریافت می کنید.

Broadcast ها و رخدادهای سیستمی

کلاس Intent بسیاری از رخدادهای سیستمی را در قالب فیلدهای ایستا و ثابت (static و final) به صورت آماده (از قبل تعریف شده) در خود کپسوله می کند. سایر کلاس های سیستم اندروید نیز رخدادهای سیستمی دیگری را تعریف می کنند. به عنوان مثال می توان به TelephoneManager برای تغییر وضعیت گوشی اشاره کرد.
جدول زیر تعدادی رخداد سیستمی بسیار مهم را همراه با شرح کاربرد ذکر می کند.

جدول 1- رخدادهای سیستمی
شرح کاربرد
Event
Boot سیستم با پایان رسیده است. برای اجرای نیاز به مجوز android.permission.RECEIVE_BOOT_COMPLETED دارد.
Intent.ACTION_BOOT_COMPLETED
دستگاه به منبع تغذیه وصل شده است.
Intent.ACTION_POWER_CONNECTED
دستگاه دیگر به منبع تغذیه وصل نیست.
Intent.ACTION_POWER_DISCONNECTED
زمانی اجرا می شود که باتری دستگاه رو به اتمام باشد. معمولا برای کاستن از تعداد activity های اپلیکیشنی که میزان مصرف باتری آن بالا است، مورد استفاده قرار می گیرد.
Intent.ACTION_BATTERY_LOW
باتری دستگاه بار دیگر در وضعیت مناسبی قرار دارد.
Intent.ACTION_BATTERY_OKAY

اجرا و راه اندازی سرویس به صورت خودکار از Receiver

یکی از رفتارهایی که زیاد مورد نیاز بوده و پیاده سازی می شود، فراخوانی و اجرای یک سرویس به صورت خودکار پس از reboot سیستم است. این کار می تواند برای مثال جهت همگاهنگ سازی داده ها صورت گیرد. برای این منظور ابتدا یک receiver (گوش فراخوان به رخداد) برای android.intent.action.BOOT_COMPLETED تعریف می کنید. لازم است مجوز android.permission.RECEIVE_BOOT_COMPLETED را نیز در فایل تنظیمات برای اجرای عملیات مزبور به اپلیکیشن اعطا نمایید.
مثال زیر نحوه ی تعریف رخداد BOOT_COMPLETED در فایل تنظیمات (Manifest) را به نمایش می گذارد.

<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="de.vogella.android.ownservice.local"
          android:versioncode="1"
          android:versionname="1.0">
                    <uses-sdk android:minsdkversion="10" />
                    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
                    <application android:icon="@drawable/icon"
                                 android:label="@string/app_name">
                    <activity android:name=".ServiceConsumerActivity"
                              android:label="@string/app_name">
                    <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
                    <receiver android:name="MyScheduleReceiver">
                    <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
                    <receiver android:name="MyStartServiceReceiver">
        </receiver>
    </application>
</manifest>

به دنبال تعریف رخداد فوق در لایه ی XML، کامپوننت receiver می بایست سرویس را به وسیله ی کد زیر به صورت خودکار و با بالا آمدن سیستم راه اندازی کند.

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
                // assumes WordService is a registered service
                Intent intent = new Intent(context, WordService.class);
                context.startService(intent);
        }
}
توجه:

چنانچه اپلیکیشن در کارت SD (حافظه ی خارجی) مستقر باشد، در آن صورت پس از رخداد android.intent.action.BOOT_COMPLETED، اپلیکیشن مورد نظر دیگر در دسترس نخواهد بود. در این شرایط می بایست به رخداد android.intent.action.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE گوش داده و Receiver را داخل فایل تنظیمات اپلیکیشن برای آن ثبت نمایید.

نکته:

به خاطر داشته باشید از ورژن 11 API کتابخانه های اندروید، برای اینکه اپلیکیشن بتواند رخداد android.intent.action.BOOT_COMPLETED events. را دریافت کند، کاربر می بایست حداقل یکبار قبلا اپلیکیشن را اجرا کرده باشد.

تمرین: ثبت یک RECEIVER جهت گوش فراخوانی به تماس های دریافتی

هدف از تمرین

در تمرین حاضر یک broadcast receiver تعریف می کنید که به رخدادهای مربوط به وضعیت دستگاه تلفن گوش می دهد. اگر دستگاه یک تماس دریافتی داشته باشد، در آن صورت receiver از رخداد آگاه شده و یک پیغام مربوطه ثبت (log) می نماید.

ساخت پروژه

یک پروژه ی جدید به نام de.vogella.android.receiver.phone ایجاد نموده و سپس یک activity جدید تعریف نمایید.

نکته:

به یاد داشته باشید که receiver تنها به شرطی که کاربر قبلا آن را یکبار اجرا کرده باشد، صدا خورده می شود. برای این منظور لازم است یک activity تعریف نمایید.

پیاده سازی receiver برای گوش فراخوانی به رخدادهای مربوطه به وضعیت تلفن و تماس ها

کلاس MyPhoneReceiver را ایجاد نمایید.

package de.vogella.android.receiver.phone;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;
public class MyPhoneReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
                Bundle extras = intent.getExtras();
                if (extras != null) {
                        String state = extras.getString(TelephonyManager.EXTRA_STATE);
                        Log.w("MY_DEBUG_TAG", state);
                        if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                                String phoneNumber = extras
                                                .getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
                                Log.w("MY_DEBUG_TAG", phoneNumber);
                        }
                }
        }
}

اعطا مجوز گوش فراخوانی به تغییرات مربوط به وضعیت در receiver

مجوز android.permission.READ_PHONE_STATE را به فایل تنظیمات (manifest) اضافه نموده تا اپلیکیشن قابلیت گوش فراخوانی به تغییرات در وضعیت (از طریق receiver) را داشته باشد. سپس receiver مورد نظر را در لایه ی XML (داخل فایل Manifest) تعریف نمایید. فایل manifest پس از وارد نمودن تغییرات فوق بایستی ظاهری مشابه زیر داشته باشد.

تست اپلیکیشن

اپلیکیشن خود را نصب نموده و سپس از طریق ADV (android device monitor) یک تماس تلفنی به دستگاه را شبیه سازی نمایید. مطمئن شوید که receiver فراخوانی شده و یک پیغام در LogCat view ثبت می شود.

<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="de.vogella.android.receiver.phone"
          android:versioncode="1"
          android:versionname="1.0">
                    <uses-sdk android:minsdkversion="15" />
                    <uses-permission android:name="android.permission.READ_PHONE_STATE">
    </uses-permission>
                    <application android:icon="@drawable/icon"
                                 android:label="@string/app_name">
                    <activity android:name=".MainActivity"
                              android:label="@string/title_activity_main">
                    <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
                    <receiver android:name="MyPhoneReceiver">
                    <intent-filter>
                    <action android:name="android.intent.action.PHONE_STATE">
                </action>
            </intent-filter>
        </receiver>
    </application>
</manifest>

تمرین: receiver و سرویس های سیستم

هدف از تمرین

در این تمرین یک receiver را از طریق سرویس درون ساخته ی محیط اندروید Android alert manager زمان بندی خواهید کرد. پس از اینکه receiver فراخوانی می شود، این کامپوننت نرم افزاری با استفاده از Android vibrator manager و یک پیغام Toast (popup message) کاربر را از تغییرات مربوطه آگاه می سازد.

پیاد سازی پروژه

یک پروژه و activity جدید به ترتیب به نام های de.vogella.android.alarm و AlarmActivity ایجاد نمایید.
سپس فایل یک layout با محتوای زیر ایجاد کنید.

<?xml version="1.0" encoding="utf-8" ?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">
                    <edittext android:id="@+id/time"
                              android:layout_width="wrap_content"
                              android:layout_height="wrap_content"
                              android:hint="Number of seconds"
                              android:inputtype="numberDecimal">
    </edittext>
                    <button android:id="@+id/ok"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:onclick="startAlert"
                            android:text="Start Counter">
    </button>
</linearlayout>

کلاس broadcast receiver را به صورت زیر پیاده سازی نمایید. این کلاس سرویس vibrator را صدا می زند.

package de.vogella.android.alarm;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Vibrator;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
                Toast.makeText(context, "Don't panik but your time is up!!!!.",
                                Toast.LENGTH_LONG).show();
                // Vibrate the mobile phone
                Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
                vibrator.vibrate(2000);
        }
}

این کلاس را در لایه ی XML، داخل فایل AndroidManifest.xml اعلان نموده و اجازه ی vibrate برای دستگاه را به اپلیکیشن اعطا نمایید.

<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="de.vogella.android.alarm"
          android:versioncode="1"
          android:versionname="1.0">
                    <uses-sdk android:minsdkversion="15" />
                    <uses-permission android:name="android.permission.VIBRATE">
    </uses-permission>
                    <application android:icon="@drawable/icon"
                                 android:label="@string/app_name">
                    <activity android:name=".AlarmActivity"
                              android:label="@string/app_name">
                    <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
                    <receiver android:name="MyBroadcastReceiver">
        </receiver>
    </application>
</manifest>

بدنه ی کلاس AlarmActivity خود را به صورت زیر ویرایش نمایید. این activity داخل خود یک آبجکت intent تعریف نموده و به وسیله ی آن receiver را فراخوانی می کند، سپس اطلاعات این intent را به سرویس alarm manager معرفی/ارسال می نماید.

package de.vogella.android.alarm;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class AlarmActivity extends Activity {
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.main);
        }
        public void startAlert(View view) {
                EditText text = (EditText) findViewById(R.id.time);
                int i = Integer.parseInt(text.getText().toString());
                Intent intent = new Intent(this, MyBroadcastReceiver.class);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(
                                this.getApplicationContext(), 234324243, intent, 0);
                AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
                alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
                                + (i * 1000), pendingIntent);
                Toast.makeText(this, "Alarm set in " + i + " seconds",
                                Toast.LENGTH_LONG).show();
        }
}

تست اپلیکیشن

اپلیکیشن خود را روی دستگاه تست نمایید. پس از تنظیم زمان، alarm را فراخوانی کنید. اکنون با گذشت تعداد ثانیه های تعیین شده، یک پیغام Toast به نمایش در می آید. لازم به ذکر است که قابلیت vibration در شبیه ساز اندروید کار نمی کند.

تعریف broadcast receiver به صورت dynamic (در زمان اجرای برنامه و به وسیله ی کدهای جاوا)

Receiver هایی که به صورت dynamic اعلان شده

همان طور که قبلا گفته شد می توانید receiver را در فایل manifest اندروید ثبت نمایید که از آن تحت عنوان ثبت به صورت static یاد می شود. در این بخش ایجاد و حذف receiver در زمان اجرای برنامه به وسیله ی متدهای Context.registerReceiver() و Context.unregisterReceiver() را شرح خواهیم داد.

توجه:

لازم است receiver ای که به صورت dynamic تعریف کرده اید را با فراخوانی متد Context.unregisterReceiver() حذف (unregister) نمایید. در صورت عدم فراخوانی متد مذکور، سیستم اندروید پیغام خطای leaked broadcast receiver را صادر می کند. بنابراین اگر receiver را داخل بدنه ی متد onResume() کلاس activity خود اعلان کرده باشید، بایستی آن را داخل onPause() حذف (unregister) نمایید.

استفاده از package manager جهت غیرفعال سازی receiver های static

می توانید با استفاده از کلاس PackageManager به راحتی receiver های تعریف شده در لایه ی XML (فایل AndroidManifest.xml) را فعال/غیرفعال نمایید.

ComponentName receiver = new ComponentName(context, myReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP);

Intent های ماندگار (Sticky broadcast intent)

یک intent معمولا پس از اینکه ارسال شده و سیستم اطلاعات درون آن را پردازش کرد، دیگر در دسترس نبوده و از حافظه پاک خواهد شد. چنانچه مایلید آبجکت مزبور پس از پردازش توسط سیستم همچنان باقی ماند و به اصطلاح ماندگار باشد، کافی است متد sendStickyBroadcast(Intent) را فراخوانی نمایید. در صورت فراخوانی این متد، intent ماندگار بوده و پس از اتمام broadcast، در حافظه باقی می ماند.
سیستم اندروید برای حفظ برخی از اطلاعات سیستمی از sticky broadcast/intent استفاده می کند. برای مثال می توان به وضعیت باتری اشاره کرد که سیستم آن را ارسال نموده و پس از ارسال تا مدتی باقی می ماند. مثال:

// Register for the battery changed event
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
/ Intent is sticky so using null as receiver works fine
// return value contains the status
Intent batteryStatus = this.registerReceiver(null, filter);
// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING
        || status == BatteryManager.BATTERY_STATUS_FULL;
boolean isFull = status == BatteryManager.BATTERY_STATUS_FULL;
// How are we charging?
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;

می توانید داده ها را از مقدار بازگشتی متد registerReceiver(BroadcastReceiver, IntentFilter) بازیابی نمایید. این روش برای BroadcastReceiver که حامل مقدار null می باشد نیز قابل استفاده است.
در دیگر جنبه ها، این متد رفتاری مشابه متد sendBroadcast(Intent) دارد.
استفاده از sticky broadcast ها لازمه ی تنظیم مجوزهای خاصی در لایه ی XML می باشد.

1395/12/08 7596 1998
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...