مروری بر پخش ها

برنامه‌های اندروید، پیام‌های پخش (broadcast) را از سیستم اندروید و سایر برنامه‌های اندروید، مشابه الگوی طراحی انتشار-اشتراک، ارسال و دریافت می‌کنند. سیستم و برنامه‌ها معمولاً زمانی که رویدادهای خاصی رخ می‌دهند، پخش (broadcast) ارسال می‌کنند. به عنوان مثال، سیستم اندروید زمانی که رویدادهای مختلف سیستمی مانند بوت شدن سیستم یا شارژ دستگاه رخ می‌دهد، پخش (broadcast) ارسال می‌کند. برنامه‌ها همچنین پخش‌های سفارشی را ارسال می‌کنند، به عنوان مثال، برای اطلاع‌رسانی به سایر برنامه‌ها در مورد چیزی که ممکن است برای آنها جالب باشد (به عنوان مثال، دانلود داده‌های جدید).

برنامه‌ها می‌توانند برای دریافت پخش‌های خاص ثبت‌نام کنند. وقتی یک پخش ارسال می‌شود، سیستم به‌طور خودکار پخش‌ها را به برنامه‌هایی که برای دریافت آن نوع خاص از پخش مشترک شده‌اند، هدایت می‌کند.

به طور کلی، می‌توان از برادکست‌ها به عنوان یک سیستم پیام‌رسانی در بین برنامه‌ها و خارج از جریان عادی کاربر استفاده کرد. با این حال، باید مراقب باشید که از فرصت پاسخ به برادکست‌ها و اجرای کارها در پس‌زمینه که می‌تواند به کندی عملکرد سیستم منجر شود، سوءاستفاده نکنید.

درباره پخش‌های سیستمی

سیستم به طور خودکار هنگام وقوع رویدادهای مختلف سیستم، مانند زمانی که سیستم به حالت هواپیما (Airplane Mode) تغییر وضعیت می‌دهد و از آن خارج می‌شود، پیام‌هایی را ارسال می‌کند. همه برنامه‌های مشترک این پیام‌ها را دریافت می‌کنند.

شیء Intent پیام پخش را در بر می‌گیرد. رشته action ، رویدادی را که رخ داده است، مانند android.intent.action.AIRPLANE_MODE ، شناسایی می‌کند. اینتنت همچنین ممکن است شامل اطلاعات اضافی باشد که در فیلد اضافی آن قرار گرفته است. به عنوان مثال، اینتنت حالت هواپیما شامل یک مقدار بولی اضافی است که نشان می‌دهد حالت هواپیما روشن است یا خیر.

برای اطلاعات بیشتر در مورد نحوه خواندن intentها و دریافت رشته اکشن از یک intent، به Intentها و فیلترهای Intent مراجعه کنید.

اقدامات پخش سیستم

برای مشاهده لیست کاملی از اقدامات پخش سیستمی، به فایل BROADCAST_ACTIONS.TXT در SDK اندروید مراجعه کنید. هر اقدام پخش دارای یک فیلد ثابت مرتبط با خود است. برای مثال، مقدار ثابت ACTION_AIRPLANE_MODE_CHANGED برابر android.intent.action.AIRPLANE_MODE است. مستندات مربوط به هر اقدام پخش در فیلد ثابت مرتبط با آن موجود است.

تغییرات در پخش‌های سیستمی

با تکامل پلتفرم اندروید، نحوه رفتار پخش‌های سیستمی به صورت دوره‌ای تغییر می‌کند. برای پشتیبانی از همه نسخه‌های اندروید، تغییرات زیر را در نظر داشته باشید.

اندروید ۱۶

در اندروید ۱۶ ، ترتیب تحویل پخش با استفاده از ویژگی android:priority یا IntentFilter.setPriority() در فرآیندهای مختلف تضمین نمی‌شود. اولویت‌های پخش فقط در همان فرآیند برنامه رعایت می‌شوند، نه در تمام فرآیندها.

همچنین، اولویت‌های پخش به طور خودکار به محدوده ( SYSTEM_LOW_PRIORITY + 1، SYSTEM_HIGH_PRIORITY - 1) محدود می‌شوند. فقط اجزای سیستم مجاز به تنظیم SYSTEM_LOW_PRIORITY و SYSTEM_HIGH_PRIORITY به عنوان اولویت پخش هستند.

اندروید ۱۴

در حالی که برنامه‌ها در حالت کش هستند، سیستم، ارسال اعلان‌ها را برای سلامت سیستم بهینه می‌کند. به عنوان مثال، سیستم اعلان‌های سیستمی کم‌اهمیت‌تر مانند ACTION_SCREEN_ON را در حالی که برنامه در حالت کش است، به تعویق می‌اندازد. به محض اینکه برنامه از حالت کش به چرخه حیات فرآیند فعال می‌رود، سیستم هرگونه اعلان به تعویق افتاده را ارسال می‌کند.

اعلان‌های مهم که در مانیفست اعلام می‌شوند، برنامه‌ها را به طور موقت از حالت کش شده برای تحویل حذف می‌کنند.

اندروید ۹

از اندروید ۹ (سطح API 28)، پخش NETWORK_STATE_CHANGED_ACTION اطلاعاتی در مورد موقعیت مکانی کاربر یا داده‌های شخصی قابل شناسایی دریافت نمی‌کند.

اگر برنامه شما روی دستگاهی با اندروید ۹.۰ (سطح API 28) یا بالاتر نصب شده باشد، سیستم شامل SSIDها، BSSIDها، اطلاعات اتصال یا نتایج اسکن در پخش‌های Wi-Fi نمی‌شود. برای دریافت این اطلاعات، به جای آن، getConnectionInfo() را فراخوانی کنید.

اندروید ۸.۰

با شروع اندروید ۸.۰ (سطح API ۲۶)، سیستم محدودیت‌های بیشتری را بر روی گیرنده‌های اعلام‌شده در manifest اعمال می‌کند.

اگر برنامه شما اندروید ۸.۰ یا بالاتر را هدف قرار می‌دهد، نمی‌توانید از مانیفست برای اعلان گیرنده برای اکثر پخش‌های ضمنی (پخش‌هایی که به طور خاص برنامه شما را هدف قرار نمی‌دهند) استفاده کنید. شما همچنان می‌توانید از یک گیرنده ثبت‌شده در متن، زمانی که کاربر به طور فعال از برنامه شما استفاده می‌کند، استفاده کنید.

اندروید ۷.۰

اندروید ۷.۰ (سطح API ۲۴) و بالاتر، broadcastهای سیستمی زیر را ارسال نمی‌کنند:

همچنین، برنامه‌هایی که اندروید ۷.۰ و بالاتر را هدف قرار می‌دهند، باید پخش CONNECTIVITY_ACTION را با استفاده از registerReceiver(BroadcastReceiver, IntentFilter) ثبت کنند. اعلام یک گیرنده در مانیفست کار نمی‌کند.

دریافت پخش‌ها

برنامه‌ها می‌توانند از دو طریق broadcastها را دریافت کنند: از طریق گیرنده‌های ثبت‌شده در متن و گیرنده‌های اعلان‌شده در مانیفست.

گیرنده‌های ثبت‌شده در متن

گیرنده‌های ثبت‌شده در متن (Context-registered receivers) تا زمانی که متن ثبت‌کننده‌شان معتبر باشد، اعلان‌های پخش (broadcasts) را دریافت می‌کنند. این معمولاً بین فراخوانی‌های registerReceiver و unregisterReceiver است. متن ثبت‌کننده همچنین زمانی نامعتبر می‌شود که سیستم متن مربوطه را از بین ببرد. به عنوان مثال، اگر شما در یک متن Activity ثبت نام کنید، تا زمانی که activity فعال باشد، اعلان‌های پخش را دریافت می‌کنید. اگر با متن Application ثبت نام کنید، تا زمانی که برنامه اجرا می‌شود، اعلان‌های پخش را دریافت می‌کنید.

برای ثبت یک گیرنده با یک context، مراحل زیر را انجام دهید:

  1. در فایل ساخت سطح ماژول برنامه خود، نسخه 1.9.0 یا بالاتر از کتابخانه AndroidX Core را وارد کنید:

    گرووی

    dependencies {
        def core_version = "1.18.0"
    
        // Java language implementation
        implementation "androidx.core:core:$core_version"
        // Kotlin
        implementation "androidx.core:core-ktx:$core_version"
    
        // To use RoleManagerCompat
        implementation "androidx.core:core-role:1.1.0"
    
        // To use the Animator APIs
        implementation "androidx.core:core-animation:1.0.0"
        // To test the Animator APIs
        androidTestImplementation "androidx.core:core-animation-testing:1.0.0"
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation "androidx.core:core-performance:1.0.0"
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation "androidx.core:core-google-shortcuts:1.1.0"
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation "androidx.core:core-remoteviews:1.1.0"
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation "androidx.core:core-splashscreen:1.2.0"
    }

    کاتلین

    dependencies {
        val core_version = "1.18.0"
    
        // Java language implementation
        implementation("androidx.core:core:$core_version")
        // Kotlin
        implementation("androidx.core:core-ktx:$core_version")
    
        // To use RoleManagerCompat
        implementation("androidx.core:core-role:1.1.0")
    
        // To use the Animator APIs
        implementation("androidx.core:core-animation:1.0.0")
        // To test the Animator APIs
        androidTestImplementation("androidx.core:core-animation-testing:1.0.0")
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation("androidx.core:core-performance:1.0.0")
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation("androidx.core:core-google-shortcuts:1.1.0")
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation("androidx.core:core-remoteviews:1.1.0")
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation("androidx.core:core-splashscreen:1.2.0")
    }
  2. یک نمونه از BroadcastReceiver ایجاد کنید:

    کاتلین

    val myBroadcastReceiver = MyBroadcastReceiver()
    

    جاوا

    MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
    
  3. یک نمونه از IntentFilter ایجاد کنید:

    کاتلین

    val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
    

    جاوا

    IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");
    
  4. انتخاب کنید که آیا گیرنده پخش باید صادر شود و برای سایر برنامه‌های روی دستگاه قابل مشاهده باشد یا خیر. اگر این گیرنده به پخش‌های ارسالی از سیستم یا از برنامه‌های دیگر - حتی برنامه‌های دیگری که متعلق به شماست - گوش می‌دهد، از پرچم RECEIVER_EXPORTED استفاده کنید. اگر در عوض، این گیرنده فقط به پخش‌های ارسالی توسط برنامه شما گوش می‌دهد، از پرچم RECEIVER_NOT_EXPORTED استفاده کنید.

    کاتلین

    val listenToBroadcastsFromOtherApps = false
    val receiverFlags = if (listenToBroadcastsFromOtherApps) {
        ContextCompat.RECEIVER_EXPORTED
    } else {
        ContextCompat.RECEIVER_NOT_EXPORTED
    }
    

    جاوا

    boolean listenToBroadcastsFromOtherApps = false;
    int receiverFlags = listenToBroadcastsFromOtherApps
            ? ContextCompat.RECEIVER_EXPORTED
            : ContextCompat.RECEIVER_NOT_EXPORTED;
    
  5. با فراخوانی registerReceiver() گیرنده را ثبت کنید:

    کاتلین

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)
    

    جاوا

    ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);
    
  6. برای متوقف کردن دریافت broadcastها، تابع unregisterReceiver(android.content.BroadcastReceiver) را فراخوانی کنید. حتماً وقتی دیگر به گیرنده نیازی ندارید یا زمینه دیگر معتبر نیست، آن را از حالت ثبت خارج کنید.

گیرنده پخش خود را لغو ثبت کنید

در حالی که گیرنده پخش ثبت شده است، ارجاعی به Context که شما آن را با آن ثبت کرده‌اید، نگه می‌دارد. اگر محدوده ثبت شده گیرنده از محدوده چرخه حیات Context فراتر رود، این امر می‌تواند به طور بالقوه باعث نشت اطلاعات شود. به عنوان مثال، این اتفاق می‌تواند زمانی رخ دهد که شما یک گیرنده را در محدوده Activity ثبت می‌کنید، اما فراموش می‌کنید که وقتی سیستم Activity را از بین می‌برد، آن را لغو ثبت کنید. بنابراین، همیشه گیرنده پخش خود را لغو ثبت کنید.

کاتلین

class MyActivity : ComponentActivity() {
    private val myBroadcastReceiver = MyBroadcastReceiver()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags)
        setContent { MyApp() }
    }

    override fun onDestroy() {
        super.onDestroy()
        // When you forget to unregister your receiver here, you're causing a leak!
        this.unregisterReceiver(myBroadcastReceiver)
    }
}

جاوا

class MyActivity extends ComponentActivity {
    MyBroadcastReceiver myBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ...
        ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags);
        // Set content
    }
}

ثبت گیرنده‌ها در کوچکترین محدوده

گیرنده پخش شما فقط باید زمانی ثبت شود که واقعاً به نتیجه علاقه‌مند باشید. کوچکترین محدوده گیرنده ممکن را انتخاب کنید:

  • متدهای چرخه عمر LifecycleResumeEffect یا activity onResume / onPause : گیرنده اعلان فقط در حالی که برنامه در حالت از سرگیری (resume) خود است، به‌روزرسانی‌ها را دریافت می‌کند.
  • متدهای چرخه عمر LifecycleStartEffect یا activity onStart / onStop : گیرنده پخش فقط در حالی که برنامه در حالت از سرگیری خود است، به‌روزرسانی‌ها را دریافت می‌کند.
  • DisposableEffect : گیرنده‌ی اعلان فقط تا زمانی که composable در درخت ترکیب قرار دارد، به‌روزرسانی‌ها را دریافت می‌کند. این محدوده به محدوده‌ی چرخه‌ی حیات فعالیت متصل نیست. ثبت گیرنده را در زمینه‌ی برنامه در نظر بگیرید. دلیل این امر این است که composable از نظر تئوری می‌تواند از محدوده‌ی چرخه‌ی حیات فعالیت بیشتر عمر کند و فعالیت را نشت دهد.
  • Activity onCreate / onDestroy : دریافت‌کننده‌ی اعلان، به‌روزرسانی‌ها را در حالی که activity در حالت ایجاد شده‌ی خود است، دریافت می‌کند. مطمئن شوید که در onDestroy() لغو ثبت نام کرده‌اید و نه onSaveInstanceState(Bundle) زیرا ممکن است این فراخوانی نشود.
  • یک محدوده سفارشی: برای مثال، می‌توانید یک گیرنده را در محدوده ViewModel خود ثبت کنید، تا در صورت بازتولید فعالیت، از آن استفاده شود. حتماً از زمینه برنامه برای ثبت گیرنده استفاده کنید، زیرا گیرنده می‌تواند از محدوده چرخه حیات فعالیت بیشتر عمر کند و فعالیت را نشت دهد.

ایجاد کامپوننت‌های stateful و stateless

Compose دارای composableهای با وضعیت (stateful) و بدون وضعیت (stateless) است. ثبت یا لغو ثبت یک broadcast receiver درون یک composable، آن را با وضعیت (stateful) می‌کند. composable یک تابع قطعی نیست که با ارسال پارامترهای یکسان، محتوای یکسانی را رندر کند. وضعیت داخلی می‌تواند بر اساس فراخوانی‌های broadcast receiver ثبت شده تغییر کند.

به عنوان یک روش عالی در Compose، توصیه می‌کنیم که Composableهای خود را به نسخه‌های stateful و stateless تقسیم کنید. بنابراین، توصیه می‌کنیم که ایجاد گیرنده پخش را از Composable خارج کنید تا stateless شود:

@Composable
fun MyStatefulScreen() {
    val myBroadcastReceiver = remember { MyBroadcastReceiver() }
    val context = LocalContext.current
    LifecycleStartEffect(true) {
        // ...
        ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, flags)
        onStopOrDispose { context.unregisterReceiver(myBroadcastReceiver) }
    }
    MyStatelessScreen()
}

@Composable
fun MyStatelessScreen() {
    // Implement your screen
}

گیرنده‌های اعلام‌شده توسط مانیفست

اگر در مانیفست خود یک گیرنده پخش (broadcast receiver) تعریف کنید، سیستم هنگام ارسال پخش، برنامه شما را اجرا می‌کند. اگر برنامه از قبل اجرا نشده باشد، سیستم برنامه را اجرا می‌کند.

برای تعریف یک گیرنده پخش در مانیفست، مراحل زیر را انجام دهید:

  1. عنصر <receiver> را در فایل مانیفست برنامه خود مشخص کنید.

    <!-- If this receiver listens for broadcasts sent from the system or from
         other apps, even other apps that you own, set android:exported to "true". -->
    <receiver android:name=".MyBroadcastReceiver" android:exported="false">
        <intent-filter>
            <action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
        </intent-filter>
    </receiver>
    

    فیلترهای intent، اقدامات پخش (broadcast actions) را که گیرنده شما در آنها مشترک می‌شود، مشخص می‌کنند.

  2. زیرکلاس BroadcastReceiver ایجاد کرده و onReceive(Context, Intent) را پیاده‌سازی کنید. گیرنده پخش در مثال زیر، محتوای پخش را ثبت و نمایش می‌دهد:

    کاتلین

    class MyBroadcastReceiver : BroadcastReceiver() {
    
        @Inject
        lateinit var dataRepository: DataRepository
    
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == "com.example.snippets.ACTION_UPDATE_DATA") {
                val data = intent.getStringExtra("com.example.snippets.DATA") ?: "No data"
                // Do something with the data, for example send it to a data repository:
                dataRepository.updateData(data)
            }
        }
    }
    

    جاوا

    public static class MyBroadcastReceiver extends BroadcastReceiver {
    
        @Inject
        DataRepository dataRepository;
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Objects.equals(intent.getAction(), "com.example.snippets.ACTION_UPDATE_DATA")) {
                String data = intent.getStringExtra("com.example.snippets.DATA");
                // Do something with the data, for example send it to a data repository:
                if (data != null) { dataRepository.updateData(data); }
            }
        }
    }
    

مدیر بسته سیستم، هنگام نصب برنامه، گیرنده را ثبت می‌کند. سپس گیرنده به یک نقطه ورود جداگانه در برنامه شما تبدیل می‌شود، به این معنی که سیستم می‌تواند برنامه را اجرا کرده و در صورت عدم اجرای برنامه، پخش را ارائه دهد.

سیستم یک شیء کامپوننت BroadcastReceiver جدید ایجاد می‌کند تا هر پخشی را که دریافت می‌کند، مدیریت کند. این شیء فقط برای مدت زمان فراخوانی onReceive(Context, Intent) معتبر است. به محض اینکه کد شما از این متد برگردد، سیستم کامپوننت را دیگر فعال در نظر نمی‌گیرد.

تأثیرات بر وضعیت فرآیند

اینکه BroadcastReceiver شما فعال باشد یا خیر، بر فرآیند موجود در آن تأثیر می‌گذارد، که می‌تواند احتمال از کار افتادن سیستم توسط آن را تغییر دهد. یک فرآیند پیش‌زمینه، متد onReceive() یک گیرنده را اجرا می‌کند. سیستم این فرآیند را اجرا می‌کند، مگر اینکه تحت فشار شدید حافظه باشد.

سیستم پس از onReceive() ‎، BroadcastReceiver غیرفعال می‌کند. اهمیت فرآیند میزبان گیرنده به اجزای برنامه آن بستگی دارد. اگر آن فرآیند فقط میزبان یک گیرنده اعلام شده در مانیفست باشد، سیستم ممکن است پس از onReceive() ‎ آن را از بین ببرد تا منابع را برای سایر فرآیندهای حیاتی‌تر آزاد کند. این امر برای برنامه‌هایی که کاربر هرگز یا اخیراً با آنها تعامل نداشته است، رایج است.

بنابراین، دریافت‌کننده‌های پخش نباید نخ‌های پس‌زمینه با اجرای طولانی را آغاز کنند. سیستم می‌تواند فرآیند را در هر لحظه پس از onReceive() برای بازیابی حافظه متوقف کند و نخ ایجاد شده را خاتمه دهد. برای زنده نگه داشتن فرآیند، یک JobService از دریافت‌کننده با استفاده از JobScheduler زمان‌بندی کنید تا سیستم بداند که فرآیند هنوز در حال کار است. مرور کلی کار پس‌زمینه جزئیات بیشتری را ارائه می‌دهد.

ارسال پخش

اندروید دو روش برای ارسال broadcast توسط برنامه‌ها ارائه می‌دهد:

  • متد sendOrderedBroadcast(Intent, String) در هر زمان، اعلان‌ها را به یک گیرنده ارسال می‌کند. با اجرای هر گیرنده به نوبت، می‌تواند نتیجه را به گیرنده بعدی منتقل کند. همچنین می‌تواند اعلان را به طور کامل لغو کند تا به گیرنده‌های دیگر نرسد. می‌توانید ترتیب اجرای گیرنده‌ها را در یک فرآیند برنامه کنترل کنید. برای انجام این کار، از ویژگی android:priority مربوط به intent-filter استفاده کنید. گیرنده‌هایی که اولویت یکسانی دارند، به ترتیب دلخواه اجرا می‌شوند.
  • متد sendBroadcast(Intent) پخش‌ها را به ترتیب نامشخصی به همه گیرنده‌ها ارسال می‌کند. به این حالت پخش عادی (Normal Broadcast) می‌گویند. این روش کارآمدتر است، اما به این معنی است که گیرنده‌ها نمی‌توانند نتایج را از گیرنده‌های دیگر بخوانند، داده‌های دریافتی از پخش را پخش کنند یا پخش را لغو کنند.

قطعه کد زیر نحوه ارسال یک broadcast را با ایجاد یک Intent و فراخوانی sendBroadcast(Intent) نشان می‌دهد.

کاتلین

val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
    putExtra("com.example.snippets.DATA", newData)
    setPackage("com.example.snippets")
}
context.sendBroadcast(intent)

جاوا

Intent intent = new Intent("com.example.snippets.ACTION_UPDATE_DATA");
intent.putExtra("com.example.snippets.DATA", newData);
intent.setPackage("com.example.snippets");
context.sendBroadcast(intent);

پیام پخش در یک شیء Intent قرار می‌گیرد. رشته‌ی action intent باید سینتکس نام پکیج جاوای برنامه را ارائه دهد و رویداد پخش را به طور منحصر به فرد شناسایی کند. می‌توانید اطلاعات اضافی را با putExtra(String, Bundle) به intent پیوست کنید. همچنین می‌توانید با فراخوانی setPackage(String) روی intent، پخش را به مجموعه‌ای از برنامه‌های موجود در یک سازمان محدود کنید.

محدود کردن پخش با مجوزها

مجوزها به شما امکان می‌دهند پخش‌ها را به مجموعه‌ای از برنامه‌هایی که مجوزهای خاصی دارند محدود کنید. می‌توانید محدودیت‌هایی را هم برای فرستنده و هم برای گیرنده پخش اعمال کنید.

ارسال پخش با مجوزها

وقتی sendBroadcast(Intent, String) یا sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) را فراخوانی می‌کنید، می‌توانید یک پارامتر مجوز مشخص کنید. فقط گیرنده‌هایی که آن مجوز را با برچسب <uses-permission> در مانیفست خود درخواست کرده‌اند، می‌توانند پخش را دریافت کنند. اگر مجوز خطرناک باشد، باید قبل از اینکه گیرنده بتواند پخش را دریافت کند، مجوز را اعطا کنید. به عنوان مثال، کد زیر یک پخش را با مجوز ارسال می‌کند:

کاتلین

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)

جاوا

context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);

برای دریافت پخش، برنامه‌ی گیرنده باید مجوز را به شرح زیر درخواست کند:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

شما می‌توانید یا یک مجوز سیستمی موجود مانند BLUETOOTH_CONNECT را مشخص کنید یا یک مجوز سفارشی با عنصر <permission> تعریف کنید. برای اطلاعات مربوط به مجوزها و امنیت به طور کلی، به System Permissions مراجعه کنید.

دریافت پخش با مجوز

اگر هنگام ثبت یک گیرنده‌ی اعلان (یا با registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) یا در تگ <receiver> در مانیفست خود، یک پارامتر مجوز تعیین کنید، فقط پخش‌کننده‌هایی که با تگ <uses-permission> در مانیفست خود درخواست مجوز کرده‌اند، می‌توانند یک Intent به گیرنده ارسال کنند. اگر مجوز خطرناک باشد، باید به پخش‌کننده نیز این مجوز اعطا شود.

برای مثال، فرض کنید برنامه‌ی دریافت‌کننده‌ی شما یک دریافت‌کننده‌ی اعلان‌شده در مانیفست به شکل زیر دارد:

<!-- If this receiver listens for broadcasts sent from the system or from
     other apps, even other apps that you own, set android:exported to "true". -->
<receiver
    android:name=".MyBroadcastReceiverWithPermission"
    android:permission="android.permission.ACCESS_COARSE_LOCATION"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
    </intent-filter>
</receiver>

یا برنامه‌ی دریافت‌کننده‌ی شما یک گیرنده‌ی ثبت‌شده در متن به صورت زیر دارد:

کاتلین

ContextCompat.registerReceiver(
    context, myBroadcastReceiver, filter,
    android.Manifest.permission.ACCESS_COARSE_LOCATION,
    null, // scheduler that defines thread, null means run on main thread
    receiverFlags
)

جاوا

ContextCompat.registerReceiver(
        context, myBroadcastReceiver, filter,
        android.Manifest.permission.ACCESS_COARSE_LOCATION,
        null, // scheduler that defines thread, null means run on main thread
        receiverFlags
);

سپس، برای اینکه بتواند به آن گیرنده‌ها پخش (broadcast) ارسال کند، برنامه‌ی فرستنده باید به شرح زیر درخواست مجوز کند:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

ملاحظات امنیتی

در اینجا به برخی از ملاحظات امنیتی برای ارسال و دریافت broadcastها اشاره می‌کنیم:

  • اگر برنامه‌های زیادی برای دریافت یک broadcast در manifest خود ثبت نام کرده باشند، می‌تواند باعث شود سیستم برنامه‌های زیادی را اجرا کند و این امر تأثیر قابل توجهی بر عملکرد دستگاه و تجربه کاربر می‌گذارد. برای جلوگیری از این امر، استفاده از context registration را به manifest declaration ترجیح دهید. گاهی اوقات، خود سیستم اندروید استفاده از receiver های context-registered را اجباری می‌کند. به عنوان مثال، broadcast CONNECTIVITY_ACTION فقط به receiver های context-registered ارسال می‌شود.

  • اطلاعات حساس را با استفاده از یک هدف ضمنی پخش نکنید. هر برنامه‌ای می‌تواند اطلاعات را بخواند اگر برای دریافت پخش ثبت‌نام کند. سه راه برای کنترل اینکه چه کسی می‌تواند پخش‌های شما را دریافت کند وجود دارد:

    • می‌توانید هنگام ارسال یک پخش، مجوزی را مشخص کنید.
    • در اندروید ۴.۰ (سطح API ۱۴) و بالاتر، می‌توانید هنگام ارسال یک broadcast، یک بسته را با استفاده از setPackage(String) مشخص کنید. سیستم، broadcast را به مجموعه‌ای از برنامه‌هایی که با بسته مطابقت دارند، محدود می‌کند.
  • وقتی یک گیرنده ثبت می‌کنید، هر برنامه‌ای می‌تواند پیام‌های مخرب بالقوه را به گیرنده برنامه شما ارسال کند. چندین راه برای محدود کردن پیام‌های دریافتی برنامه شما وجود دارد:

    • شما می‌توانید هنگام ثبت نام یک گیرنده پخش، مجوز مربوطه را مشخص کنید.
    • برای گیرنده‌هایی که در مانیفست تعریف شده‌اند، می‌توانید ویژگی android:exported را در مانیفست روی "false" تنظیم کنید. گیرنده، داده‌های منتشر شده از منابع خارج از برنامه را دریافت نمی‌کند.
  • فضای نام برای اکشن‌های پخش سراسری است. مطمئن شوید که نام اکشن‌ها و سایر رشته‌ها در فضای نامی که خودتان دارید نوشته شده‌اند. در غیر این صورت، ممکن است سهواً با برنامه‌های دیگر تداخل ایجاد کنید.

  • از آنجا که متد onReceive(Context, Intent) یک گیرنده روی نخ اصلی اجرا می‌شود، باید به سرعت اجرا و برگردانده شود. اگر نیاز به انجام کارهای طولانی مدت دارید، در مورد ایجاد نخ‌ها یا شروع سرویس‌های پس‌زمینه مراقب باشید زیرا سیستم می‌تواند پس از بازگرداندن onReceive() کل فرآیند را از بین ببرد. برای اطلاعات بیشتر، به Effect on process state مراجعه کنید. برای انجام کارهای طولانی مدت، توصیه می‌کنیم:

    • فراخوانی goAsync() در متد onReceive() گیرنده و ارسال BroadcastReceiver.PendingResult به یک نخ پس‌زمینه. این کار پخش را پس از بازگشت از onReceive() فعال نگه می‌دارد. با این حال، حتی با این رویکرد، سیستم انتظار دارد که پخش را خیلی سریع (کمتر از 10 ثانیه) تمام کنید. این به شما امکان می‌دهد کار را به نخ دیگری منتقل کنید تا از اختلال در نخ اصلی جلوگیری شود.
    • زمان‌بندی یک کار با استفاده از JobScheduler . برای اطلاعات بیشتر، به زمان‌بندی هوشمند کار مراجعه کنید.
  • فعالیت‌ها را از گیرنده‌های پخش شروع نکنید زیرا تجربه کاربری نامطلوب است؛ به خصوص اگر بیش از یک گیرنده وجود داشته باشد. در عوض، نمایش یک اعلان را در نظر بگیرید.