نمای کلی همه‌فرستی‌ها

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

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

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

درباره همه‌فرستی‌های سیستم

وقتی رویدادهای مختلف سیستم رخ می‌دهد، سیستم به‌طور خودکار همه‌فرستی ارسال می‌کند، مثلاً وقتی سیستم وارد «حالت هواپیما» می‌شود یا از آن خارج می‌شود. همه برنامه‌های مشترک این همه‌فرستی‌ها را دریافت می‌کنند.

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

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

کنش‌های همه‌فرستی سیستم

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

تغییرات در همه‌فرستی‌های سیستم

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

‫Android 16

در Android 16، ترتیب ارائه همه‌فرستی بااستفاده از مشخصه android:priority یا IntentFilter.setPriority() در فرایندهای مختلف تضمین نخواهد شد. اولویت‌های همه‌فرستی فقط در فرایند برنامه یکسان رعایت می‌شود نه در همه فرایندها.

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

Android 14

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

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

Android 9

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

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

Android نسخه ۸.۰

از Android 8.0 (میانای برنامه‌سازی کاربردی سطح ۲۶) به بعد، سیستم محدودیت‌های بیشتری بر گیرنده‌های اعلام‌شده در مانیفست اعمال می‌کند.

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

Android 7.0

‫Android 7.0 (سطح میانای برنامه کاربردی ۲۴) و نسخه‌های بالاتر هم‌پخش‌های سیستم زیر را ارسال نمی‌کنند:

همچنین، برنامه‌هایی که Android 7.0 و بالاتر را هدف‌یابی می‌کنند باید CONNECTIVITY_ACTION همه‌فرستی را بااستفاده از registerReceiver(BroadcastReceiver, IntentFilter) ثبت کنند. اعلام گیرنده در مانیفست کار نمی‌کند.

دریافت همه‌فرستی‌ها

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

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

گیرنده‌های ثبت‌شده در بافت تا زمانی که بافت ثبت آن‌ها معتبر باشد، همه‌فرستی‌ها را دریافت می‌کنند. این معمولاً بین تماس با registerReceiver و unregisterReceiver است. وقتی سیستم زمینه مربوطه را ازبین می‌برد، زمینه ثبت نیز نامعتبر می‌شود. برای مثال، اگر در بافت Activity ثبت‌نام کنید، تا زمانی که فعالیت فعال باشد، همه‌فرستی‌ها را دریافت می‌کنید. اگر با بافت «برنامه» ثبت‌نام کنید، تا زمانی که برنامه اجرا می‌شود همه‌فرستی دریافت می‌کنید.

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

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

    گرووی

    dependencies {
        def core_version = "1.19.1"
    
        // 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.19.1"
    
        // 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. برای توقف دریافت همه‌فرستی‌ها، با unregisterReceiver(android.content.BroadcastReceiver) تماس بگیرید. وقتی دیگر به گیرنده نیاز ندارید یا زمینه دیگر معتبر نیست، حتماً گیرنده را لغو ثبت کنید.

لغو ثبت گیرنده رویداد ثبت‌شده

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

کاتلین

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 یا فعالیت onResume/onPause چرخه حیات روش‌ها: گیرنده همه‌فرستی فقط زمانی به‌روزرسانی دریافت می‌کند که برنامه در وضعیت ازسرگرفته‌شده باشد.
  • LifecycleStartEffect یا فعالیت onStart/onStop چرخه حیات روش‌ها: گیرنده همه‌فرستی فقط زمانی به‌روزرسانی دریافت می‌کند که برنامه در وضعیت ازسرگرفته‌شده باشد.
  • DisposableEffect: گیرنده همه‌فرستی فقط زمانی به‌روزرسانی دریافت می‌کند که ترکیب‌پذیر در درخت ترکیب باشد. این محدوده به محدوده چرخه حیات فعالیت پیوست نشده است. درنظر داشته باشید گیرنده را در بافت برنامه ثبت کنید. دلیل این امر این است که عنصر ترکیبی می‌تواند ازنظر نظری از محدوده چرخه حیات فعالیت فراتر رود و فعالیت را فاش کند.
  • فعالیت onCreate/onDestroy: گیرنده همه‌فرستی به‌روزرسانی‌ها را دریافت می‌کند درحالی‌که فعالیت در وضعیت ایجادشده است. حتماً در onDestroy() لغو ثبت کنید و نه در onSaveInstanceState(Bundle) زیرا ممکن است این مورد فراخوانی نشود.
  • محدوده سفارشی: برای مثال، می‌توانید گیرنده‌ای را در ViewModel محدوده خود ثبت کنید تا پس‌از بازسازی فعالیت باقی بماند. حتماً از بافت برنامه برای ثبت گیرنده استفاده کنید، زیرا گیرنده می‌تواند از محدوده چرخه حیات فعالیت فراتر رود و فعالیت را فاش کند.

ساختن عنصر ترکیبی با وضعیت و بدون وضعیت

«نوشتن» دارای عناصر ترکیبی باحالت و بدون حالت است. ثبت یا لغو ثبت گیرنده رویداد در داخل عنصر ترکیبی آن را حالت‌دار می‌کند. ترکیب‌پذیر تابع معینی نیست که وقتی پارامترهای یکسانی به آن داده می‌شود محتوای یکسانی ارائه کند. وضعیت داخلی می‌تواند براساس تماس‌های گیرنده همه‌فرستی ثبت‌شده تغییر کند.

به‌عنوان یک روال مطلوب در «نگارش»، توصیه می‌کنیم عناصر ترکیبی خود را به نسخه‌های با وضعیت و بدون وضعیت تقسیم کنید. بنابراین، توصیه می‌کنیم که ایجاد گیرنده همه‌فرستی را از یک Composable خارج کنید تا آن را بدون حالت کنید:

@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
}

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

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

برای اعلام گیرنده همه‌فرستی در مانیفست، مراحل زیر را انجام دهید:

  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>
    

    فیلترهای هدف کنش‌های رویداد ثبت‌شده‌ای را که گیرنده شما در آن‌ها مشترک می‌شود مشخص می‌کنند.

  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() گیرنده را اجرا می‌کند. سیستم این فرایند را اجرا می‌کند مگر اینکه فشار حافظه شدید باشد.

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

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

ارسال همه‌فرستی‌ها

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

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

تکه کد زیر نحوه ارسال همه‌فرستی را با ایجاد یک 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 هدف باید دستورگان نام بسته جاوا برنامه را ارائه دهد و رویداد همه‌فرستی را به‌صورت منحصربه‌فرد شناسایی کند. می‌توانید اطلاعات تکمیلی را با putExtra(String, Bundle) به هدف پیوست کنید. همچنین می‌توانید با فراخوانی setPackage(String) در هدف، همه‌فرستی را به مجموعه‌ای از برنامه‌ها در همان سازمان محدود کنید.

محدود کردن همه‌فرستی‌ها با اجازه‌ها

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

ارسال همه‌فرستی با اجازه‌ها

وقتی با 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> تعریف کنید. برای کسب اطلاعات درباره اجازه‌ها و امنیت به‌طور کلی، اجازه‌های سیستم را ببینید.

دریافت همه‌فرستی‌ها با اجازه‌ها

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

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

<!-- 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
);

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

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

از همه‌فرستی در همان فرایند خودداری کنید

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

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

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

    • جریان‌های Kotlin (SharedFlow و StateFlow): راهکاری مدرن و اصطلاحی در Kotlin برای انتشار و مشاهده جریان‌های رویداد یا به‌روزرسانی‌های وضعیت در سراسر روال‌های مشترک و عناصر در برنامه شما.
    • مشترک ViewModel: هم‌رسانی داده و رویداد بین عناصر مختلف رابط کاربری (مثل قطعه‌ها یا عناصر ترکیبی) در یک فعالیت را تسهیل می‌کند.
    • بازخوان‌ها و شنوندگان: بازخوان‌های رابط استاندارد یا ارجاع‌های تابع که مستقیماً بین عناصر منتقل می‌شوند یا در مخزن مرکزی یا کنترل‌کننده ثبت می‌شوند.
  • مدیریت رویدادهای سیستم، کارها، یا زنگ‌های ساعت: برای مثال، دریافت کار زمان‌بندی‌شده، زنگ ساعت، یا بازخوانی سیستم، و سپس ارسال همه‌فرستی برای راه‌اندازی کار واقعی. به‌جای ارسال همه‌فرستی، کار را مستقیماً در آن کار (مثل کارگر JobService یا WorkManager)، کنترل‌کننده هشدار، یا عنصر بازخوان سیستم تکمیل کنید، یا مستقیماً به کلاس‌های منطق کسب‌وکار برنامه‌تان واگذار کنید.

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

برای کسب اطلاعات بیشتر درباره طراحی ارتباط بین عناصر برنامه، راهنمای معماری برنامه را ببینید.

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

در اینجا چند ملاحظه امنیتی برای ارسال و دریافت همه‌فرستی آورده شده است:

  • اگر برنامه‌های زیادی برای دریافت یک همه‌فرستی یکسان در مانیفست خود ثبت کرده باشند، می‌تواند باعث شود سیستم برنامه‌های زیادی را راه‌اندازی کند و این امر تأثیر قابل‌توجهی بر عملکرد دستگاه و تجربه کاربری خواهد داشت. برای جلوگیری از این اتفاق، بهتر است از ثبت بافت به‌جای اظهارنامه مانیفست استفاده کنید. گاهی اوقات، خود سیستم Android استفاده از گیرندگان ثبت‌شده در زمینه را اجباری می‌کند. برای مثال، همه‌فرستی CONNECTIVITY_ACTION فقط به گیرنده‌های ثبت‌شده در بافت تحویل داده می‌شود.

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

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

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

  • چون روش onReceive(Context, Intent) گیرنده در رشته اصلی اجرا می‌شود، باید سریع اجرا شود و برگردد. اگر نیاز به انجام کار طولانی‌مدت دارید، در ایجاد رشته‌ها یا شروع سرویس‌های پس‌زمینه‌ای دقت کنید زیرا سیستم می‌تواند کل فرایند را پس‌از بازگشت onReceive() متوقف کند. برای اطلاعات بیشتر، به تأثیر بر وضعیت فرایند مراجعه کنید. برای انجام کار طولانی‌مدت، توصیه می‌کنیم:

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