شنود رویدادها در اندروید توسط BroadcastReceiver

آموزش BroadcastReceiver و ارسال پیغام رویداد سفارشی توسط sendBroadcast در برنامه نویسی اندروید
گاهی اوقات لازم است اپلیکیشن ما بر اساس یک وضعیت یا رویداد که در سیستم عامل اندروید یا سایر برنامه‌ها اتفاق افتاده واکنش نشان داده و متناسب آن پاسخ دهد که با استفاده از کامپوننت‌ BroadcastReceiver امکان پذیر است. در واقع برودکست رسیورها به ما اجازه می‌دهند تا رویدادهای سیستم عامل یا برنامه‌ها را دریافت کنیم. در این جلسه به معرفی این کامپوننت می‌پردازیم.

BroadcastReceiver چیست؟

به نام خدا. بهتر است با چند مثال شروع کنم. فرض کنید قصد ساخت اپلیکیشنی را داریم که وابستگی زیادی به شبکه اینترنت دارد و می‌خواهیم قطع و وصل ارتباط اینترنتی را به محض وقوع، به اطلاع کاربر برسانیم. در مثالی دیگر، برنامه‌ای داریم که ورود کاربر توسط دریافت کد تایید از طریق پیامک (SMS) انجام می‌شود و می‌خواهیم بدون نیاز به وارد کردن دستی کد تایید توسط کاربر، به محض دریافت پیامک، کد دریافتی به برنامه منتقل شود.
در این موارد لازم است برنامه ما به رویدادها و وضعیت سیستم عامل دسترسی داشته باشد تا بتواند اتصال یا عدم اتصال به اینترنت و همچنین دریافت پیامک را بررسی کند. در اینجا با استفاده از BroadcastReceiver به پیغام‌های مرتبط با رویداد مدنظرمان گوش داده سپس اقدام مناسب را انجام می‌دهیم.
به عبارتی دیگر، سیستم عامل اندروید برای وقوع تعدادی رویداد و وضعیت خاص، پیغامی همگانی صادر می‌کند که هر برنامه می‌تواند بر اساس نیاز خود، این پیغام‌ها را دریافت کند. البته این پیغام‌ها محدود و منحصر به سیستم عامل نبوده و برنامه‌ها هم می‌توانند پیغام‌های سفارشی خود را منتشر کرده تا سایر برنامه‌ها آنرا دریافت کنند.
واژه Broadcast به معنی “پخش” و “منتشر کردن” و Receiver به معنی “دریافت کننده” است. لذا از نام این کامپوننت هم کاربرد و هدف آن را می‌توان حدس زد.
در ادامه جلسه به نحوه استفاده از برودکست رسیورها در اندروید می‌پردازیم.

پیاده سازی BroadcastReceiver در اندروید

قبل از هر چیز طبق مبحث آموزش ساخت پروژه در اندروید استودیو یک پروژه اندرویدی با نام BroadcastReceiver می‌سازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم.
جهت پیاده سازی BroadcastReceiver و استفاده از آن، دو مرحله را باید انجام دهیم که در قالب مثال بررسی وضعیت اتصال اینترنت و همچنین دریافت پیامک بررسی می‌کنیم.

۱:ساخت BroadcastReceiver

برای ایجاد یک BroadcastReceiver یک کلاس تعریف می‌کنیم که از کلاس BroadcastReceiver مشتق (ارث بری) شده باشد. برای دریافت پیغام‌ها باید متد onReceive() درون این کلاس Override شود. پیغام در قالب intent دریافت می‌شود.
در پکیج پروژه یک کلاس جاوا با نام دلخواه MyReceiver اضافه می‌کنم:

ساخت کلاس MyReceiver که از BroadcastReceiver مشتق شده

عمل مشتق کردن کلاس از BroadcastReceiver را هم در مرحله ساخت کلاس (مانند تصویر فوق) و هم به صورت دستی و بعد از ساخت کلاس می‌توان انجام داد. در نهایت کلاس باید به صورت زیر تکمیل شود:

MyReceiver.java

package ir.android_studio.broadcastreceiver;

import android.content.BroadcastReceiver;

public class MyReceiver extends BroadcastReceiver {
    
}

Override کردن متد onReceive() هم به هر دو صورت دستی و یا خودکار قابل انجام است. روی بدنه کلاس alt + enter زده و متد onReceive() را implement می‌کنم:

Override کردن متد onReceive برای گوش دادن به رویدادهای منتشر شده

Override کردن متد onReceive برای گوش دادن به رویدادهای منتشر شده

متد به اینصورت اضافه شد:

MyReceiver.java

package ir.android_studio.broadcastreceiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        
    }
}

کلاس را به صورت زیر تکمیل کردم:

MyReceiver.java

package ir.android_studio.broadcastreceiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.widget.Toast;

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        if (checkNetwork(context)) {

            Toast.makeText(context, "اتصال برقرار است", Toast.LENGTH_SHORT).show();

        } else {

            Toast.makeText(context, "اتصال برقرار نیست", Toast.LENGTH_SHORT).show();

        }

    }

    private boolean checkNetwork(Context mContext) {

            try {

                ConnectivityManager conManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
                NetworkInfo nInfo = conManager.getActiveNetworkInfo();

                return (nInfo != null && nInfo.isConnected());

            } catch (NullPointerException e) {

                e.printStackTrace();
                return false;

            }

    }

}

پیغامی که سیستم عامل منتشر می‌کند و ما آنرا دریافت می‌کنیم صرفا تغییر وضعیت اتصال را اعلام می‌کند و نه اتصال یا عدم اتصال را. بنابراین وظیفه بررسی نوع وضعیت بر عهده خودمان است.
یک متد با نام دلخواه checkNetwork از نوع boolean در کلاس تعریف کردم که بررسی وضعیت اتصال را بر عهده دارد.

ConnectivityManager conManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo nInfo = conManager.getActiveNetworkInfo();

توسط متد ConnectivityManager و سپس getActiveNetworkInfo() وضعیت اتصال بررسی شده و نتیجه در nInfo ذخیره می‌شود. متد از نوع boolean بوده و قرار است یک نتیجه true یا false برگرداند.

return (nInfo.isConnected());

خط فوق بررسی می‌کند اتصال برقرار است یا خیر. یعنی isConnected مقدار true برمی‌گرداند یا false. در واقع این خط با شرط زیر برابر است:

if (nInfo.isConnected()) {
    return true;
} else {
    return false;
}

کدها درون یک try catch قرار گرفته‌اند تا در صورتی که به هر دلیلی خروجی نال بود، برنامه کرش نکند.
حالا درون متد onReceive() بر اساس خروجی checkNetwork عملیات مدنظر را انجام می‌دهیم که در اینجا صرفا یک پیغام از جنس Toast تعریف کرده‌ام. چنانچه نتیجه true برگردانده شود، پیغام “اتصال برقرار است” و در غیر اینصورت پیغام “اتصال برقرار نیست” اجرا خواهد شد.

۲:ثبت یا رجیستر کردن BroadcastReceiver

بعد از ساخت BroadcastReceiver لازم است آنرا در سیستم عامل ثبت کنیم. تا زمانی که کلاس مدنظر به عنوان یک Receiver ثبت و تعریف نشود، امکان گوش دادن به رویدادهای منتشر شده را نخواهد داشت. ثبت BroadcastReceiver به دو طریق امکان پذیر است که در ادامه هر دو روش را توضیح می‌دهم:

ثبت BroadcastReceiver در مانیفست (روش استاتیک)

مانیفست پروژه را باز کرده و مطابق زیر، یک تگ با نام receiver درون تگ application اضافه می‌کنم:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <receiver android:name=".MyReceiver">
        <intent-filter>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>

</application>

درست مانند تعریف اکتیویتی در مانیفست، برای تعریف برودکست رسیور اقدام می‌کنیم. ویژگی name همان نام کلاس و تگ action رویدادی که می‌خواهیم بررسی شود را تعریف می‌کنیم.
android.net.conn.CONNECTIVITY_CHANGE تغییر وضعیت اتصال را بررسی می‌کند. برخی اکشن‌ها مانند اکشن فعلی، نیاز به دریافت مجوز دسترسی از سیستم عامل دارند. بنابراین پرمیشن موردنیاز را هم به تگ manifest اضافه می‌کنم:

در مورد حق دسترسی‌ها قبلا در مبحث آموزش کار با کتابخانه Retrofit صحبت کردیم.
کد نهایی مانیفست پروژه:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ir.android_studio.broadcastreceiver">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".MyReceiver">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>

    </application>

</manifest>

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

Intent action های اندروید

به عنوان مثال اکشن android.intent.action.BOOT_COMPLETED بوت شدن سیستم عامل را گوش می‌دهد که به محض بوت شدن دستگاه، پیغام مرتبط از طرف سیستم عامل به برنامه‌ها ارسال می‌گردد.

نکته: تعداد اکشن‌های تعریف شده در تگ receiver محدود به یک مورد نبوده و در صورت نیاز به گوش دادن به چندین رویداد، می‌توان بیشتر از یک اکشن تعریف کرد.
نکته: برودکست رسیورهایی که از طریق مانیفست و تگ receive روی سیستم ثبت می‌شوند برای دریافت پیغام‌ رویدادها نیازی به باز بودن برنامه نداشته و چنانچه اپلیکیشن در پس زمینه اندروید در حال اجرا باشد نیز قادر به دریافت پیغام‌ها هستند. البته از API 11 به بعد حداقل یکبار باید برنامه توسط کاربر اجرا شده باشد. به عبارت دیگر برنامه‌ای که بعد از نصب یکبار باز نشده، امکان گوش دادن به پیغام‌ها را نخواهد داشت.
نکته: اکشن بررسی وضعیت اتصال شبکه از نسخه ۷ اندروید deprecate (منقضی) شده و چنانچه در نسخه‌های جدیدتر به گوش دادن به این رویداد نیاز داریم باید مطابق قسمت بعد، برودکست رسیور را درون اکتیویتی تعریف کنیم و نه مانیفست.

ثبت BroadcastReceiver در اکتیویتی (داینامیک):

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

MainActivity.java

package ir.android_studio.broadcastreceiver;

import androidx.appcompat.app.AppCompatActivity;

import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    MyReceiver myReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

            myReceiver = new MyReceiver();
            registerReceiver(myReceiver, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));

        }

    }

}

درون متد onCreate() یک شرط تعریف کردم که بررسی کند چنانچه برنامه روی اندروید N و به بالا اجرا شد، دستورات زیر اجرا شود:

MyReceiver myReceiver = new MyReceiver();
registerReceiver(myReceiver, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));

ابتدا یک نمونه از کلاس MyReceiver ایجاد کردم. سپس توسط متد registerReceiver() اکشن مدنظر را ثبت (رجیستر) کرده‌ام. این متد دو ورودی می‌گیرد که ورودی اول کلاس رسیور و ورودی دوم نام اکشن یا همان intent مدنظر است.

نکته: برخلاف رویدادهای ثبت شده از طریق مانیفست که در پس زمینه هم قادر به شنود هستند، رویدادهای ثبت شده در اکتیویتی صرفا در زمان اجرای اکتیویتی فعال هستند و با بسته شدن اکتیویتی، رجیستر غیر فعال می‌شود. بنابراین چنانچه بخواهیم عملیات شنود صرفا هنگام اجرای برنامه انجام شود، ثبت رسیور در اکتیویتی کفایت می‌کند اما اگر قصد داریم حداقل در اندروید قبل از ۷ بررسی وضعیت در پس زمینه هم اجرا شود لازم است ثبت به هر دو روش انجام شود.
نکته: تعریف مجوز دسترسی (پرمیشن) ارتباطی به روش ثبت رسیور نداشته و در هر دو صورت در مواردی که گوش دادن به رویداد مدنظر نیاز به دریافت مجوز از سیستم عامل دارد، باید در مانیفست تعریف شود.

برخلاف متد registerReceiver که عملیات ثبت BroadcastReceiver را انجام می‌دهد، متد unregisterReceiver برودکست رسیور ثبت شده را غیر فعال و حذف می‌کند. بنابراین بهتر است این دو متد را به صورت زیر در متدهای onResume() (یا onStart()) و onPause() (یا onDestroy()) اکتیویتی تعریف کنیم. عدم حذف receiver داینامیک می‌تواند موجب بروز خطا گردد.

MainActivity.java

package ir.android_studio.broadcastreceiver;

import androidx.appcompat.app.AppCompatActivity;

import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    MyReceiver myReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    @Override
    protected void onResume() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            myReceiver = new MyReceiver();
            registerReceiver(myReceiver, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));

        }

        super.onResume();
    }

    @Override
    protected void onPause() {

        unregisterReceiver(myReceiver);

        super.onPause();
    }
}

پروژه را اجرا کرده و دسترسی به WiFi را روی حالت‌های روشن و خاموش تست می‌کنم:

دریافت پیغام رویداد CONNECTIVITY_CHANGE بعد از روشن کردن WiFi دیوایس اندرویدی توسط BroadcastReceiver

دریافت پیغام رویداد CONNECTIVITY_CHANGE بعد از خاموش کردن WiFi دیوایس اندرویدی توسط BroadcastReceiver

ملاحظه می‌کنید با فعال شدن WiFi پیغام “اتصال برقرار است” و با غیرفعال شدن آن پیغام “اتصال برقرار نیست” نمایش داده شده.
تاکید می‌کنم متد checkNetwork را صرفا برای بررسی وضعیت اتصال یا عدم اتصال نوشتیم وگرنه صرف دریافت پیغام مرتبط با رویداد نیاز به انجام کار خاصی ندارد. برای مثال کلاس را به صورت زیر امتحان کنید:

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        Toast.makeText(context, "وضعیت اتصال به شبکه تغییر کرد", Toast.LENGTH_SHORT).show();

    }

}

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

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ir.android_studio.broadcastreceiver">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".MyReceiver">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

    </application>

</manifest>

در قدم بعد و در متد onReceive() یک Toast دیگر اضافه می‌کنم:

@Override
public void onReceive(Context context, Intent intent) {

    if (checkNetwork(context)) {

        Toast.makeText(context, "اتصال برقرار است", Toast.LENGTH_SHORT).show();

    } else {

        Toast.makeText(context, " اتصال برقرار نیست ", Toast.LENGTH_SHORT).show();

    }

    Toast.makeText(context, "یک پیامک دریافت شد", Toast.LENGTH_SHORT).show();

}

اگر بخاطر داشته باشید در مبحث آموزش Runtime Permission در اندروید در اندروید گفتیم از اندروید ۶ به بعد، تعدادی از پرمیشن‌ها باید از کاربر تاییده دریافت کنند. یکی از این پرمیشن‌ها، دسترسی به پیامک‌هاست.
ابتدا یک Button در layout اکتیویتی تعریف می‌کنم:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_request"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="پرمیشن پیامک" />

</RelativeLayout>

سپس کدهای مربوط به Runtime Permission را به صورت زیر به اکتیویتی اضافه می‌کنم:

MainActivity.java

package ir.android_studio.broadcastreceiver;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.DialogInterface;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    MyReceiver myReceiver;
    private final int SMS_REQUEST_CODE = 100;
    private Button requestButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        requestButton = findViewById(R.id.btn_request);

        requestButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED) {

                    requestCameraPermission();

                } else {

                    Toast.makeText(MainActivity.this, "مجوز قبلا دریافت شده", Toast.LENGTH_SHORT).show();

                }

            }
        });

    }

    private void requestCameraPermission() {

        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.RECEIVE_SMS)) {

            new AlertDialog.Builder(this)
                    .setTitle("درخواست مجوز")
                    .setMessage("برای بررسی پیامک مجوز را تایید کنید")
                    .setPositiveButton("موافقم", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {

                            reqPermission();

                        }
                    })
                    .setNegativeButton("لغو", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {

                            dialogInterface.dismiss();

                        }
                    })
                    .create()
                    .show();

        } else {

            reqPermission();

        }

    }

    private void reqPermission() {

        ActivityCompat.requestPermissions(MainActivity.this, new String[] {Manifest.permission.RECEIVE_SMS}, SMS_REQUEST_CODE);

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        if (requestCode == SMS_REQUEST_CODE) {

            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                Toast.makeText(this, "مجوز تایید شد", Toast.LENGTH_SHORT).show();

            } else {

                Toast.makeText(this, "مجوز رد شد", Toast.LENGTH_SHORT).show();

            }

        }

    }

    @Override
    protected void onResume() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

            myReceiver = new MyReceiver();
            registerReceiver(myReceiver, new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"));

        }

        super.onResume();
    }

    @Override
    protected void onPause() {

        unregisterReceiver(myReceiver);

        super.onPause();
    }
}

مجدد پروژه را اجرا کرده و روی دکمه کلیک می‌کنم تا مجوز RECEIVE_SMS را به برنامه بدهم. سپس یک پیامک به دیوایس ارسال می‌کنم:

دریافت پیغام رویداد SMS_RECEIVED بعد از دریافت پیامک در دیوایس اندرویدی توسط BroadcastReceiver

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

مهمترین رویدادهای سیستمی در اندروید

در جدول زیر به تعدادی از مهمترین و پرکاربردترین رویدادهای سیستمی اندروید اشاره شده است:

نام رویداد کاربرد
android.intent.action.BOOT_COMPLETED این پیغام بعد از بوت شدن سیستم عامل منتشر می‌شود
android.intent.action.CALL_BUTTON هنگامی که کاربر روی گزینه Call یا گزینه دیگری با هدف باز کردن صفحه شماره گیر کلیک می‌کند
android.intent.action.REBOOT بعد از راه اندازی مجدد اندروید، پیغام این رویداد منتشر می‌شود
android.intent.action.BATTERY_LOW شارژ کم باتری دستگاه را اعلام می‌کند
android.intent.action.BATTERY_OKAY رسیدن شارژ باتری به حد مطلوب را اعلام می‌کند
android.intent.action.DATE_CHANGED تغییر تاریخ سیستم عامل را اعلام می‌کند
android.intent.action.AIRPLANE_MODE هنگامی که دستگاه روی حالت پرواز قرار می‌گیرد یک پیغام منتشر می‌شود

انتشار پیغام یا intent سفارشی (Custom Intent Broadcasting)

تا اینجای کار ما پیغام یا intent هایی که توسط سیستم عامل منتشر می‌شد را دریافت می‌کردیم. اما علاوه بر دریافت پیغام، امکان ارسال پیغام‌های سفارشی هم وجود دارد. یعنی ما یک پیغام سفارشی را از برنامه خود منتشر کرده و سایر برنامه‌ها این پیغام را دریافت می‌کنند.
فرض کنید با وقوع یک رویداد خاص در برنامه ما (برای مثال کلیک روی یک دکمه) کد زیر فراخوانی می‌شود:

Intent mIntent = new Intent();
mIntent.setAction("ir.android_studio.ir.MY_CUSTOM_BROADCAST");
sendBroadcast(mIntent);
در اینجا توسط <span dir="ltr">setAction()</span> یک اکشن سفارشی با نامی دلخواه ایجاد کرده‌ایم. اکشن تعریف شده توسط <span dir="ltr">sendBroadcast()</span> در سطح سیستم عامل منتشر می‌شود. در صورت نیاز می‌توان اطلاعات بیشتری توسط intent ارسال کرد که به وسیله <span dir="ltr">putExtra()</span> قابل انجام است:
Intent mIntent = new Intent();
mIntent.setAction("ir.android_studio.ir.MY_CUSTOM_BROADCAST");
mIntent.putExtra("data","My additional information");
sendBroadcast(mIntent);

حالا کافیست در یک برنامه دیگر، تگ action کلاس BroadcastReceiver (در حالت استاتیک) به صورت زیر تعریف شود:

و یا ورودی دوم در متد registerReceiver() (در حالت داینامیک):

registerReceiver(myReceiver, new IntentFilter("ir.android_studio.ir.MY_CUSTOM_BROADCAST "));
نکته: علاوه بر sendBroadcast() متد دیگری با نام sendStickyBroadcast() وجود دارد که تفاوت آن با sendBroadcast() در ماندگاری پیغام است. به این معنی که پیغام پس از انتشار در حافظه باقی مانده و از بین نمی‌رود. البته این متد به دلیل مشکلات امنیتی اعم از امکان دستکاری پیغام توسط برنامه‌های مخرب، از API 21 منقضی (Deprecate) شده و قابل استفاده نیست.
نکته: پیغام سفارشی که توسط یک برنامه منتشر می‌شود توسط همان برنامه هم قابل دریافت است. برای مثال پیغام سفارشی که در اینجا ساختیم را مانند پیغام‌های سیستمی اندروید می‌توانیم دریافت کنیم. برای تمرین یک دکمه به اکتیویتی اضافه کنید به نحوی که با لمس آن، یک پیغام سفارشی ارسال شده و توسط یک Toast نتیجه مشاهده شود.

انتشار پیغام به همراه درخواست پرمیشن

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

sendBroadcast(new Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS);

در اینجا علاوه بر نام، مجوز SEND_SMS هم ارسال می‌شود و فقط برنامه‌ای می‌تواند این Broadcast را دریافت (Receive) کند که مجوز دسترسی به SEND_SMS را دریافت کرده باشد.

انتشار پیغام سفارشی در داخل برنامه (درون برنامه‌ای)

در مواردی ممکن است قصد داشته باشیم پیغام سفارشی که درون برنامه ما تولید شده صرفا در همین برنامه قابل دریافت بوده و سایر برنامه‌ها به آن دسترسی نداشته باشند. این کار مزایایی دارد از جمله امنیت بیشتر و عدم شنود پیغام‌های اختصاصی ما توسط برنامه‌های دیگر و دیگری عدم درگیر کردن Broadcast در سطح سیستم عامل.
اندروید کلاس LocalBroadcastManager را برای ارسال پیغام‌های سفارشی درون برنامه‌ای معرفی کرده که البته این کلاس هم به دلایلی از جانب اندروید Deprecate شده و به همین دلیل بیشتر از این پیرامون آن صحبت نمی‌کنم. چنانچه مایل هستید با این کلاس آشنا شوید مقالاتی مانند این لینک را مطالعه کنید. خود اندروید استفاده از روش‌های دیگر مانند LiveData را پیشنهاد کرده است.
در پایان توصیه می‌کنم در خصوص مبحث BroadcastReceiver ها مطالعه بیشتری کرده و مثال‌های بیشتری را بررسی کنید. بررسی تمامی اکشن‌ها از حوصله این آموزش خارج هست.
موفق و پیروز باشید.

مطالعه‌ی بیشتر:

https://developer.android.com/guide/components/broadcasts
https://developer.android.com/reference/android/content/Intent
https://developer.android.com/reference/android/content/BroadcastReceiver
https://developer.android.com/reference/android/content/Context
https://developer.android.com/reference/androidx/localbroadcastmanager/content/LocalBroadcastManager

توجه : سورس پروژه درون پوشه Exercises قرار دارد

دانلود نسخه PDF این آموزش به همراه سورس پروژه
تعداد صفحات : ۲۲
حجم : ۲ مگابایت
قیمت : رایگان
دانلود رایگان با حجم ۲ مگابایت لینک کمکی
این مطلب چقدر برایتان مفید بود؟ لطفا امتیاز دهید
دوره آموزش برنامه نویسی اندروید
دوره آموزش برنامه نویسی اندروید

آموزش‌ها در دو دسته «پایه» و «تکمیلی» منتشر می‌شوند.
آموزش‌های پایه شامل مباحث اصلی و آموزش‌های تکمیلی مطالبی است که می‌بایست در کنار مطالب اصلی از آنها استفاده کنیم.
شما با دریافت این دوره به تمامی آموزش‌های غیر رایگان و رایگان موجود در وب سایت دسترسی دارید که تخفیفی برای آموزش‌های غیر رایگان نیز درنظر گرفته شده. این پکیج به دو صورت دانلودی و ارسال پستی ارائه می‌گردد.
با خرید این دوره، به تمامی آموزش‌های غیر رایگانی که در آینده منتشر می‌شود نیز به صورت رایگان دسترسی خواهید داشت!

یک دیدگاه بنویسید

پرسش‌های زیر تایید و پاسخ داده نخواهد شد:
۱ : جزء موارد پاسخ داده شده در مطلب "مشکلات و پرسش های رایج" باشد
۲ : سوال قبلا توسط کاربران در دیدگاهها مطرح و پاسخ داده شده باشد
۳ : سوال خارج از مبحث آموزشی موجود در این صفحه باشد

  • سپهر گفت:

    ببخشید یه سوال
    آیا همه این موارد باید تو ذهن برنامه نویس بمونه ؟
    مثال میزنم من در لحظه یادگیری recyclerview ، fragment
    database، service، broadcast..
    و الباقی موارد هر کدوم رو کامل متوجه میشم و حتی برنامه های متفاوتی باهاشون کد میزنم
    ولی بعد از چند هفته اگه قرار باشه ازشون استفاده کنم نیاز به یه سرچ مختصر دارم
    میخوام ببینم این حالت عادیه ؟ یا تمرین باید در حدی باشه که تمامشون رو حفظ بشیم ؟

  • محمد گفت:

    سلام خداقوت
    این خطاها در اجرای برنامه هست

    Caused by: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve
    com.android.tools.build:gradle:3.5.3.

  • احسان گفت:

    سلام توی تعریف متد sms نوشتین private void requestCameraPermission() { ما اینجا پرمیشن دوربین نمیخوایم که sms میخواهیم! ضمناً هیچ توضیحی در مورد
    shouldShowRequestPermissionRationale
    AlertDialog ها
    onRequestPermissionsResult
    داده نشده فکر کنم این قسمت رو از قلم انداختین ممنون میشم توضیح بدین

  • سجاد گفت:

    سلام وقتتون بخیر.
    من برای چک کردن اینترنت روش شما رو اجرا کردم و از alert استفاده کردم تا وقتی که اینترنت قطعه همراه با toast نمایش بده و موقعی که اینترنت وصل میشه alert.dismiss() زدم و همه چی درست کار میکنه alert موقعی که اینترنت قطعه درست کار میکنه و نمایش داده میشه ولی وقتی دوباره اینترنت رو وصل میکنم alert بسته نمیشه ولی toast که اینترنت وصل شده رو نمایش میده
    داخل اینترنت هم هرچی گشتم روش هایی بود که همون مشکل رو داشت چجوری باید alert رو ببندم؟
    ممنون میشم راهنمایی کنید

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *