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

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

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

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

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

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

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

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

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

برای مشاهده فهرست کامل کنش‌های همه‌فرستی سیستم، به فایل 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‏ ۲۸) به بعد، پخش NETWORK_STATE_CHANGED_ACTION اطلاعاتی درباره مکان کاربر یا داده‌های شناساننده شخصی دریافت نمی‌کند.

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

Android نسخه ۸.۰

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

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

Android نسخه ۷.۰

‫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) را فراخوانی کنید. وقتی دیگر به گیرنده نیاز ندارید یا بافت دیگر معتبر نیست، حتماً ثبت‌نام گیرنده را لغو کنید.

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

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

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

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

به‌عنوان روال مطلوب در Compose، توصیه می‌کنیم که composable-هایتان را به نسخه‌های باوضعیت و بی‌وضعیت تقسیم کنید. بنابراین، توصیه می‌کنیم که ایجاد گیرنده رویداد ثبت‌شده را از 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) همه‌فرستی‌ها را هربار به یک گیرنده ارسال می‌کند. As each receiver executes in turn, it can propagate a result to the next receiver. همچنین می‌تواند پخش را کاملاً لغو کند تا به گیرنده‌های دیگر نرسد. می‌توانید ترتیب اجرای گیرنده‌ها را در فرایند یک برنامه کنترل کنید. برای انجام این کار، از مشخصه 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 Flows (SharedFlow و StateFlow): یک راهکار مدرن و اصطلاحی در Kotlin برای انتشار و مشاهده جریان‌های رویداد یا به‌روزرسانی‌های وضعیت در سراسر روال‌های مشترک و مؤلفه‌ها در برنامه شما.
    • ViewModel مشترک: هم‌رسانی داده‌ها و رویدادها را بین مؤلفه‌های مختلف واسط کاربر (مانند قطعه‌ها یا مؤلفه‌های قابل‌ترکیب) در یک فعالیت تسهیل می‌کند.
    • فراخوانی‌های برگشتی و شنوندگان: فراخوانی‌های برگشتی رابط استاندارد یا ارجاع‌های تابع که مستقیماً بین مؤلفه‌ها منتقل می‌شوند یا در مخزن یا کنترل‌کننده مرکزی ثبت می‌شوند.
  • رسیدگی به رویدادها، کارها، یا زنگ‌های سیستم: برای مثال، دریافت کار زمان‌بندی‌شده، زنگ، یا فراخوانی سیستم، و سپس ارسال همه‌فرستی برای راه‌اندازی کار واقعی. به‌جای ارسال همه‌فرستی، کار را مستقیماً در آن کار (مثل کارگر JobService یا WorkManager)، کنترل‌کننده هشدار، یا عنصر بازخوان سیستم تکمیل کنید، یا مستقیماً به کلاس‌های منطق کسب‌وکار برنامه‌تان واگذار کنید.

در دستگاه‌های دارای Android 17 QPR2 یا بالاتر، سیستم با برگرداندن همه‌فرستی‌ها به فرایند ارسال، که آن‌ها را به گیرنده‌های خود در رشته اصلی تحویل می‌دهد، همه‌فرستی‌ها را به‌طور کارآمدتری تحویل می‌دهد. ارسال همه‌فرستی به خودتان اهمیت فرایندتان را افزایش نمی‌دهد و از متوقف شدن فرایند ذخیره‌شده در حافظه نهان جلوگیری نمی‌کند، بنابراین برای ادامه اجرای برنامه یا انجام کار در پس‌زمینه به همه‌فرستی به خودتان تکیه نکنید. درعوض، API مناسب را برای تکلیف خود انتخاب کنید، همان‌طور که در نمای کلی تکالیف پس‌زمینه شرح داده شده است. حتی با این بهینه‌سازی، همه‌فرستی‌های خودکار نسبت‌به سازوکارهای ارتباطی درون‌فرایندی کارایی کمتری دارند، بنابراین به‌جای آن از این جایگزین‌ها استفاده کنید.

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

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

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

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

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

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

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

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

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