برنامههای Android میتوانند پیامهای پخش را از سیستم Android و سایر برنامههای Android ارسال یا دریافت کنند، مشابه الگوی طراحی انتشار-اشتراک . این پخش ها زمانی ارسال می شوند که یک رویداد مورد علاقه رخ دهد. به عنوان مثال، سیستم Android زمانی که رویدادهای مختلف سیستم رخ می دهد، مانند زمانی که سیستم بوت می شود یا دستگاه شروع به شارژ می کند، پخش ها را ارسال می کند. برنامهها همچنین میتوانند پخشهای سفارشی ارسال کنند، برای مثال، برای اطلاع دادن به برنامههای دیگر از چیزی که ممکن است به آن علاقه داشته باشند (مثلاً برخی از دادههای جدید دانلود شده است).
این سیستم به منظور حفظ سلامت بهینه سیستم، تحویل پخش ها را بهینه می کند. بنابراین زمان تحویل پخش ها تضمین نمی شود. برنامههایی که به ارتباطات بین فرآیندی با تاخیر کم نیاز دارند، باید سرویسهای محدود را در نظر بگیرند.
برنامه ها می توانند برای دریافت پخش های خاص ثبت نام کنند. هنگامی که یک پخش ارسال می شود، سیستم به طور خودکار پخش را به برنامه هایی هدایت می کند که برای دریافت آن نوع پخش خاص مشترک شده اند.
به طور کلی، پخش ها می توانند به عنوان یک سیستم پیام رسانی در بین برنامه ها و خارج از جریان عادی کاربر استفاده شوند. با این حال، باید مراقب باشید که از فرصت پاسخگویی به پخشها و اجرای کارهایی در پسزمینه که میتوانند به کندی عملکرد سیستم کمک کنند، سوء استفاده نکنید.
درباره پخش های سیستمی
هنگامی که رویدادهای مختلف سیستم رخ می دهد، مانند زمانی که سیستم در حالت هواپیما قرار می گیرد و خارج می شود، سیستم به طور خودکار پخش می کند. پخشهای سیستم به همه برنامههایی که برای دریافت رویداد مشترک هستند ارسال میشود.
پیام پخش خود در یک شیء Intent
پیچیده شده است که رشته عمل آن رویداد رخ داده را شناسایی می کند (به عنوان مثال android.intent.action.AIRPLANE_MODE
). این هدف همچنین ممکن است شامل اطلاعات اضافی همراه با فیلد اضافی آن باشد. برای مثال، هدف حالت هواپیما شامل یک بولی اضافی است که نشان میدهد آیا حالت هواپیما روشن است یا خیر.
برای اطلاعات بیشتر در مورد نحوه خواندن intent ها و دریافت رشته عملکرد از یک intent، به Intent و Intent Filters مراجعه کنید.
برای فهرست کامل اقدامات پخش سیستم، فایل BROADCAST_ACTIONS.TXT
را در Android SDK ببینید. هر اقدام پخش دارای یک فیلد ثابت مرتبط با آن است. برای مثال، مقدار ثابت ACTION_AIRPLANE_MODE_CHANGED
android.intent.action.AIRPLANE_MODE
است. اسناد برای هر اقدام پخش در فیلد ثابت مرتبط با آن موجود است.
تغییرات در پخش سیستم
همانطور که پلت فرم اندروید تکامل می یابد، به طور دوره ای نحوه رفتار پخش سیستم را تغییر می دهد. برای پشتیبانی از تمامی نسخه های اندروید، تغییرات زیر را در نظر داشته باشید.
اندروید 14
در حالی که برنامهها در حالت حافظه پنهان هستند، پخش پخش برای سلامت سیستم بهینه شده است. برای مثال، پخشهای کمتر مهم سیستمی مانند ACTION_SCREEN_ON
زمانی که برنامه در حالت حافظه پنهان است به تعویق میافتد. هنگامی که برنامه از حالت حافظه پنهان به چرخه حیات فرآیند فعال میرود، سیستم هر گونه پخش معوقی را ارائه میکند.
پخشهای مهمی که در مانیفست اعلام میشوند، موقتاً برنامهها را برای تحویل از حالت حافظه پنهان حذف میکنند.
اندروید 9
با شروع Android 9 (سطح API 28)، پخش NETWORK_STATE_CHANGED_ACTION
اطلاعاتی درباره مکان کاربر یا دادههای قابل شناسایی شخصی دریافت نمیکند.
علاوه بر این، اگر برنامه شما روی دستگاهی با Android 9 یا بالاتر نصب شده باشد، پخشهای سیستم از Wi-Fi حاوی SSID، BSSID، اطلاعات اتصال یا نتایج اسکن نیست. برای دریافت این اطلاعات، به جای آن، getConnectionInfo()
را فراخوانی کنید.
اندروید 8.0
با شروع Android 8.0 (سطح API 26)، این سیستم محدودیتهای بیشتری را بر گیرندههای اعلام شده توسط مانیفست اعمال میکند.
اگر برنامه شما Android 8.0 یا بالاتر را هدف قرار می دهد، نمی توانید از مانیفست برای اعلام گیرنده برای اکثر پخش های ضمنی (پخش هایی که به طور خاص برنامه شما را هدف قرار نمی دهند) استفاده کنید. هنگامی که کاربر به طور فعال از برنامه شما استفاده می کند، همچنان می توانید از یک گیرنده ثبت شده در زمینه استفاده کنید.
اندروید 7.0
Android 7.0 (سطح API 24) و بالاتر، پخشهای سیستمی زیر را ارسال نکنید:
همچنین، برنامههایی که Android نسخه ۷.۰ و بالاتر را هدف قرار میدهند، باید پخش CONNECTIVITY_ACTION
را با استفاده از registerReceiver(BroadcastReceiver, IntentFilter)
ثبت کنند. اعلام یک گیرنده در مانیفست کار نمی کند.
دریافت پخش
برنامهها میتوانند پخشها را به دو صورت دریافت کنند: از طریق گیرندههای اعلامشده در مانیفست و گیرندههای ثبتشده در زمینه.
گیرنده های اعلام شده
اگر یک گیرنده پخش را در مانیفست خود اعلام کنید، سیستم برنامه شما را (اگر برنامه قبلاً اجرا نشده است) هنگام ارسال پخش راه اندازی می کند.
برای اعلام یک گیرنده پخش در مانیفست، مراحل زیر را انجام دهید:
عنصر
<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="APP_SPECIFIC_BROADCAST" /> </intent-filter> </receiver>
فیلترهای هدف، اقدامات پخشی را مشخص میکنند که گیرنده شما در آنها مشترک است.
Subclass
BroadcastReceiver
و پیاده سازیonReceive(Context, Intent)
. گیرنده پخش در مثال زیر محتویات پخش را ثبت و نمایش می دهد:کاتلین
private const val TAG = "MyBroadcastReceiver" class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { StringBuilder().apply { append("Action: ${intent.action}\n") append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n") toString().also { log -> Log.d(TAG, log) val binding = ActivityNameBinding.inflate(layoutInflater) val view = binding.root setContentView(view) Snackbar.make(view, log, Snackbar.LENGTH_LONG).show() } } } }
جاوا
public class MyBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "\n"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n"); String log = sb.toString(); Log.d(TAG, log); ActivityNameBinding binding = ActivityNameBinding.inflate(layoutInflater); val view = binding.root; setContentView(view); Snackbar.make(view, log, Snackbar.LENGTH_LONG).show(); } }
برای فعال کردن view binding، viewBinding را در فایل build.gradle سطح ماژول خود پیکربندی کنید .
مدیر بسته سیستم گیرنده را هنگام نصب برنامه ثبت می کند. سپس گیرنده به یک نقطه ورودی جداگانه به برنامه شما تبدیل می شود که به این معنی است که اگر برنامه در حال حاضر در حال اجرا نباشد، سیستم می تواند برنامه را راه اندازی کند و پخش را ارائه دهد.
سیستم یک شیء جزء BroadcastReceiver
جدید برای مدیریت هر پخشی که دریافت می کند ایجاد می کند. این شی فقط برای مدت زمان تماس با onReceive(Context, Intent)
معتبر است. هنگامی که کد شما از این روش باز می گردد، سیستم این مؤلفه را دیگر فعال نمی داند.
گیرنده های ثبت شده در زمینه
گیرنده های ثبت شده در زمینه تا زمانی که زمینه ثبت آنها معتبر باشد، پخش را دریافت می کنند. به عنوان مثال، اگر در یک زمینه Activity
ثبت نام کنید، تا زمانی که فعالیت از بین نرود، پخش را دریافت خواهید کرد. اگر در زمینه برنامه ثبت نام کنید، تا زمانی که برنامه در حال اجرا است، پخش را دریافت خواهید کرد.
برای ثبت یک گیرنده با زمینه، مراحل زیر را انجام دهید:
در فایل ساخت ماژول برنامه خود، نسخه 1.9.0 یا بالاتر از کتابخانه AndroidX Core را قرار دهید:
Groovy
dependencies { def core_version = "1.13.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.0.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-alpha01" }
Kotlin
dependencies { val core_version = "1.13.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.0.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-alpha01") }
یک نمونه از
BroadcastReceiver
ایجاد کنید:کاتلین
val br: BroadcastReceiver = MyBroadcastReceiver()
جاوا
BroadcastReceiver br = new MyBroadcastReceiver();
یک نمونه از
IntentFilter
ایجاد کنید:کاتلین
val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
جاوا
IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
انتخاب کنید که آیا گیرنده پخش باید برای سایر برنامه های دستگاه صادر و قابل مشاهده باشد یا خیر. اگر این گیرنده به پخشهای ارسال شده از سیستم یا سایر برنامهها - حتی سایر برنامههای متعلق به شما - گوش میدهد، از پرچم
RECEIVER_EXPORTED
استفاده کنید. اگر در عوض این گیرنده فقط به پخشهای ارسال شده توسط برنامه شما گوش میدهد، از پرچمRECEIVER_NOT_EXPORTED
استفاده کنید.کاتلین
val listenToBroadcastsFromOtherApps = false val receiverFlags = if (listenToBroadcastsFromOtherApps) { ContextCompat.RECEIVER_EXPORTED } else { ContextCompat.RECEIVER_NOT_EXPORTED }
جاوا
boolean listenToBroadcastsFromOtherApps = false; if (listenToBroadcastsFromOtherApps) { receiverFlags = ContextCompat.RECEIVER_EXPORTED; } else { receiverFlags = ContextCompat.RECEIVER_NOT_EXPORTED; }
با فراخوانی
registerReceiver()
گیرنده را ثبت کنید:کاتلین
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
جاوا
ContextCompat.registerReceiver(context, br, filter, receiverFlags);
برای توقف دریافت پخش، با
unregisterReceiver(android.content.BroadcastReceiver)
تماس بگیرید. زمانی که گیرنده دیگر به آن نیاز ندارید یا متن دیگر معتبر نیست، حتماً ثبت نام را لغو کنید.مراقب باشید که گیرنده را در کجا ثبت میکنید و آن را لغو ثبت میکنید، برای مثال، اگر گیرندهای را در
onCreate(Bundle)
با استفاده از زمینه فعالیت ثبت میکنید، باید آن را درonDestroy()
لغو ثبت کنید تا از نشت گیرنده از زمینه فعالیت جلوگیری کنید. اگر گیرندهای را درonResume()
ثبت میکنید، باید آن را درonPause()
لغو ثبت کنید تا از چندین بار ثبت آن جلوگیری کنید (اگر نمیخواهید پخشها را در زمان توقف دریافت کنید، و این میتواند هزینههای غیرضروری سیستم را کاهش دهد). درonSaveInstanceState(Bundle)
لغو ثبت نکنید، زیرا اگر کاربر به پشته تاریخچه برگردد، این نام خوانده نمیشود.
اثرات بر وضعیت فرآیند
اینکه BroadcastReceiver
شما کار میکند یا خیر، بر فرآیند محدود آن تأثیر میگذارد، که میتواند احتمال نابودی سیستم آن را تغییر دهد. یک فرآیند پیش زمینه متد onReceive()
گیرنده را اجرا می کند. سیستم فرآیند را به جز تحت فشار شدید حافظه اجرا می کند.
BroadcastReceiver پس از onReceive()
غیرفعال می شود. فرآیند میزبان گیرنده فقط به اندازه اجزای برنامه آن مهم است. اگر آن فرآیند فقط یک گیرنده اعلام شده توسط مانیفست را میزبانی کند (یک اتفاق مکرر برای برنامههایی که کاربر هرگز با آنها تعامل نداشته یا اخیراً با آنها تعامل نداشته است)، سیستم ممکن است آن را پس از onReceive()
بکشد تا منابع برای سایر فرآیندهای مهمتر در دسترس قرار گیرد.
بنابراین، گیرنده های پخش نباید رشته های پس زمینه طولانی مدت را شروع کنند. سیستم می تواند در هر لحظه پس از onReceive()
فرآیند را متوقف کند تا حافظه را بازیابی کند و رشته ایجاد شده را خاتمه دهد. برای زنده نگه داشتن فرآیند، یک JobService
از گیرنده با استفاده از JobScheduler
برنامه ریزی کنید تا سیستم بداند که این فرآیند هنوز کار می کند. Background Work Overview جزئیات بیشتری را ارائه می دهد.
ارسال پخش
اندروید سه راه برای برنامه ها برای ارسال پخش ارائه می دهد:
- روش
sendOrderedBroadcast(Intent, String)
پخش ها را در یک زمان به یک گیرنده ارسال می کند. همانطور که هر گیرنده به نوبه خود اجرا می شود، می تواند نتیجه را به گیرنده بعدی منتقل کند یا می تواند پخش را به طور کامل متوقف کند تا به گیرنده های دیگر منتقل نشود. گیرنده های سفارش اجرا شده را می توان با ویژگی android:priority مربوط به intent-filter کنترل کرد. گیرنده های با همان اولویت به ترتیب دلخواه اجرا می شوند. - روش
sendBroadcast(Intent)
پخش ها را به ترتیب نامشخصی به همه گیرنده ها ارسال می کند. به این یک پخش عادی می گویند. این کارآمدتر است، اما به این معنی است که گیرندهها نمیتوانند نتایج گیرندههای دیگر را بخوانند، دادههای دریافتی از پخش را منتشر کنند، یا پخش را متوقف کنند.
قطعه کد زیر نحوه ارسال یک پخش را با ایجاد Intent و فراخوانی sendBroadcast(Intent)
نشان می دهد.
کاتلین
Intent().also { intent -> intent.setAction("com.example.broadcast.MY_NOTIFICATION") intent.putExtra("data", "Nothing to see here, move along.") sendBroadcast(intent) }
جاوا
Intent intent = new Intent(); intent.setAction("com.example.broadcast.MY_NOTIFICATION"); intent.putExtra("data", "Nothing to see here, move along."); sendBroadcast(intent);
پیام پخش شده در یک شیء Intent
پیچیده شده است. رشته اکشن intent باید نحو نام بسته جاوای برنامه را ارائه کند و رویداد پخش را به طور منحصربهفرد شناسایی کند. با putExtra(String, Bundle)
می توانید اطلاعات بیشتری را به intent پیوست کنید. همچنین میتوانید با فراخوانی setPackage(String)
روی intent، پخش را به مجموعهای از برنامهها در همان سازمان محدود کنید.
محدود کردن پخش با مجوز
مجوزها به شما این امکان را می دهند که پخش را به مجموعه برنامه هایی که دارای مجوزهای خاصی هستند محدود کنید. می توانید محدودیت هایی را برای فرستنده یا گیرنده پخش اعمال کنید.
ارسال با مجوز
وقتی sendBroadcast(Intent, String)
یا sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
را فرا میخوانید، میتوانید یک پارامتر مجوز را مشخص کنید. فقط گیرندگانی که این مجوز را با
کاتلین
sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND), Manifest.permission.BLUETOOTH_CONNECT)
جاوا
sendBroadcast(new Intent(BluetoothDevice.ACTION_FOUND), Manifest.permission.BLUETOOTH_CONNECT)
برای دریافت پخش، برنامه دریافت کننده باید مطابق شکل زیر درخواست مجوز کند:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
می توانید یک مجوز سیستم موجود مانند BLUETOOTH_CONNECT
را مشخص کنید یا یک مجوز سفارشی را با عنصر <permission>
تعریف کنید. برای اطلاعات در مورد مجوزها و امنیت به طور کلی، به مجوزهای سیستم مراجعه کنید.
دریافت با مجوز
اگر هنگام ثبت یک گیرنده پخش، یک پارامتر مجوز را مشخص کنید (چه با registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
یا در برچسب <receiver>
در مانیفست خود)، پس فقط پخش کنندگانی که مجوز را با <uses-permission>
درخواست کرده اند. برچسب در مانیفست خود (و متعاقباً در صورت خطرناک بودن مجوز به آنها اعطا شد) می تواند یک Intent را برای گیرنده ارسال کند.
به عنوان مثال، فرض کنید برنامه دریافت کننده شما دارای یک گیرنده اعلام شده توسط مانیفست مطابق شکل زیر است:
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.BLUETOOTH_CONNECT">
<intent-filter>
<action android:name="android.intent.action.ACTION_FOUND"/>
</intent-filter>
</receiver>
یا برنامه دریافت کننده شما دارای یک گیرنده ثبت شده در زمینه است که در زیر نشان داده شده است:
کاتلین
var filter = IntentFilter(Intent.ACTION_FOUND) registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null )
جاوا
IntentFilter filter = new IntentFilter(Intent.ACTION_FOUND); registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null );
سپس، برای اینکه بتوانید پخش ها را به آن گیرنده ها ارسال کنید، برنامه ارسال باید مطابق شکل زیر درخواست مجوز کند:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
ملاحظات امنیتی و بهترین شیوه ها
در اینجا برخی از ملاحظات امنیتی و بهترین شیوه ها برای ارسال و دریافت پخش وجود دارد:
اگر بسیاری از برنامهها برای دریافت پخش یکسان در مانیفست خود ثبت نام کرده باشند، میتواند باعث شود که سیستم برنامههای زیادی را راهاندازی کند و تأثیر قابلتوجهی بر عملکرد دستگاه و تجربه کاربر داشته باشد. برای جلوگیری از این امر، استفاده از ثبت متن را به اعلان مانیفست ترجیح دهید. گاهی اوقات، خود سیستم اندروید استفاده از گیرنده های ثبت شده در زمینه را اعمال می کند. به عنوان مثال، پخش
CONNECTIVITY_ACTION
فقط به گیرنده های ثبت شده در زمینه ارائه می شود.اطلاعات حساس را با هدف ضمنی پخش نکنید. اطلاعات را می توان توسط هر برنامه ای که برای دریافت پخش ثبت نام می کند، خواند. سه راه برای کنترل اینکه چه کسی می تواند پخش شما را دریافت کند وجود دارد:
- هنگام ارسال پخش، می توانید مجوزی را تعیین کنید.
- در اندروید 4.0 و بالاتر، میتوانید هنگام ارسال پخش، بستهای را با
setPackage(String)
مشخص کنید. سیستم پخش را به مجموعه برنامه هایی که با بسته مطابقت دارند محدود می کند.
هنگامی که یک گیرنده را ثبت می کنید، هر برنامه ای می تواند پخش های مخرب احتمالی را به گیرنده برنامه شما ارسال کند. راه های مختلفی برای محدود کردن پخش هایی که برنامه شما دریافت می کند وجود دارد:
- هنگام ثبت یک گیرنده پخش، می توانید مجوزی را تعیین کنید.
- برای گیرندههای اعلامشده با مانیفست، میتوانید ویژگی android:exported را روی «false» در مانیفست تنظیم کنید. گیرنده پخش را از منابع خارج از برنامه دریافت نمی کند.
فضای نام برای اقدامات پخش جهانی است. مطمئن شوید که نامهای عملکرد و رشتههای دیگر در فضای نامی که متعلق به شماست نوشته شده باشد، در غیر این صورت ممکن است ناخواسته با برنامههای دیگر تداخل داشته باشید.
از آنجایی که متد
onReceive(Context, Intent)
گیرنده روی رشته اصلی اجرا می شود، باید به سرعت اجرا شود و برگردد. اگر نیاز به انجام کارهای طولانی در حال اجرا دارید، مراقب ایجاد رشته ها یا راه اندازی خدمات پس زمینه باشید زیرا سیستم می تواند کل فرآیند را پس از بازگشتonReceive()
از بین ببرد. برای اطلاعات بیشتر، به تأثیر بر وضعیت فرآیند مراجعه کنید برای انجام کارهای طولانی مدت، توصیه می کنیم:- فراخوانی
goAsync()
در متدonReceive()
گیرنده و ارسالBroadcastReceiver.PendingResult
به رشته پس زمینه. این امر پخش را پس از بازگشت ازonReceive()
فعال نگه می دارد. با این حال، حتی با این رویکرد، سیستم انتظار دارد که پخش را خیلی سریع (زیر 10 ثانیه) به پایان برسانید. این به شما امکان میدهد کار را به رشتهای دیگر منتقل کنید تا از مشکل در رشته اصلی جلوگیری کنید. - برنامه ریزی یک کار با
JobScheduler
. برای اطلاعات بیشتر، برنامهریزی هوشمند شغلی را ببینید.
- فراخوانی
فعالیتها را از گیرندههای پخش شروع نکنید زیرا تجربه کاربر آزاردهنده است. به خصوص اگر بیش از یک گیرنده وجود داشته باشد. در عوض، نمایش یک اعلان را در نظر بگیرید.