在Android中从BroadcastReceiver调用Activity方法

39

我正在创建一个只依赖于网络的在线应用程序。

因此,每当出现网络错误时,它必须通知用户。为此,我创建了一个BroadcastReciver,它在网络连接丢失(互联网)时接收调用。

所有这些都运行得很完美。现在我需要的是从这个Broadcast Receiver调用Activity的方法,我在该方法中创建了一个Alert Dialogue。

我已经阅读了stackoverflow.com上的许多答案,可以通过仅使用Activity名称声明该方法静态并进行调用,例如MyActivityName.myMethod()

但是我不能将我的方法声明为静态,因为我在那里使用了Alert Dialogue,它会在以下行显示错误:

AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

无法在静态上下文中使用此方法。

那么,我该如何从广播接收器调用Activity的方法(必须非静态且不启动该Activity)?

并且,我能否从当前正在运行的广播接收器中获取Activity(或片段)的名称?

6个回答

88

尝试这段代码:

你的广播接收器类用于检测网络连接是否丢失:

public class InternetLostReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
    context.sendBroadcast(new Intent("INTERNET_LOST"));
}
}

在你的活动中添加以下内容以调用广播:

public class TestActivity  extends Activity{

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

    registerReceiver(broadcastReceiver, new IntentFilter("INTERNET_LOST"));
}

BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // internet lost alert dialog method call from here...
    }
};

@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(broadcastReceiver);
}
}

感谢你的代码jay,它完美地运行了。 我已经放置了一个用于在onReceive()中创建警报对话框的代码。 只有一个bug存在。 每当互联网断开连接时,会立即创建四个警报对话框,而不是一个。 - Jay Vyas
1
请在打开AlertDialog的条件中加入if语句,如果已经打开了就不要再打开另一个。如果这个方法有效,请接受我的答案。 - Vijju
AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); alertDialog.setMessage("未找到网络。"); alertDialog.setPositiveButton("检查设置", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } });alertDialog.setNegativeButton("取消", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { } }); alertDialog.show(); - Jay Vyas
1
一则小提示:内部的BroadcastReceiver不能与外部的BroadcastReceiver具有相同的意图过滤器。如果这样做,你将会陷入一个无休止的循环中。 - velis
谢谢,你节省了我的时间 :) - Shahzain ali
它完美地运行了,但是你能否向我解释一下背后的逻辑,我没有理解。 - Nick Asher

10

接口:保持BroadCastReceiver和Activity代码的分离!

你可以创建一个CallBackListener接口,该接口将作为BroadcastReceiverActivity之间的桥梁。

1) 创建一个CallbackListener接口

interface ConnectionLostCallback{

      public void connectionLost();

} 

2) 在您的广播接收器中提供ConnectionLostCallback

public class MyBroadcastReceiver extends BroadcastReceiver{

     private ConnectionLostCallback listener;

     public MyBroadcastReceiver(ConnectionLostCallback listener ){

           this.listener = listener     //<-- Initialze it

     }

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

           listener.connectionLost();

     }
}

3) 在您的 Activity 中实现 ConnectionLostCallback 并重写该方法。

YourActvity extends AppcompatActivity implements ConnectionLostCallback{

    // Your Activity related code //
      //    new MyBroadcastReceiver(this);  <-- create instance

    private void showAlertMessage(){
       AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
    } 


    @Override 
    public void connectionLost(){

         showAlertMessage();          //<--- Call the method to shoe alert dialog

    }


}

相关链接:

如果您想知道如何使BroadcastReceiver独立于任何活动,即如何重用相同的BroadcastReceiver与不同的Activity,则请阅读此内容


3
如果无法调用构造函数怎么办?例如,在使用AlarmManager时,广播接收器是从AlarmManager方法中调用的。 - mantc_sdr
如果您想跟踪WiFi状态的更改,情况也是类似的 - 这在清单级别上进行,并捕获系统级别的广播。在这种情况下,设置一个监听器即可。 - Martin
我也在使用AlarmManager,为了解决这个问题,我使用本地广播管理器代替接口作为回调。@mantc_sdr - Ramesh kumar
这是我的广播接收器代码: Intent refreshIntent = new Intent("CALLBACK"); refreshIntent.putExtra("STATUS", byReminderType); LocalBroadcastManager.getInstance(context.getApplicationContext()).sendBroadcast(refreshIntent) - Ramesh kumar

4
在您打开AlertDialog的活动中添加一个布尔变量。
boolean isDialogOpened = false;

// in broadcast recever check 
if(isDialogOpened) {
    alertDialog();
}

请将您的alertdialog代码替换为以下代码。
public void alertDialog() {
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

    alertDialog.setMessage("Network not found.");
    alertDialog.setPositiveButton("Check Setting",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                }
            });
    alertDialog.setNegativeButton("Cancel",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                }
            });

    alertDialog.setOnDismissListener(new OnDismissListener() {
        @Override
        public void onDismiss(DialogInterface dialog) {
            isDialogOpened = false;
        }
    });

    alertDialog.setOnCancelListener(new OnCancelListener() {
        @Override
        public void onCancel(DialogInterface dialog) {
            isDialogOpened = false;
        }
    });

    alertDialog.show();
}

抱歉,但您的代码无法运行, 在alertDialog.setonDismissListenet上告诉我“NoSuchMethodException”。 - Jay Vyas

1
将您的Activity上下文传递给BroadcastReceiver的构造函数。
public class ResponseReceiver extends BroadcastReceiver{

    MainActivity ma; //a reference to activity's context

    public ResponseReceiver(MainActivity maContext){
        ma=maContext;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        ma.brCallback("your string"); //calling activity method
    }

}

在你的MainActivity中。
public class MainActivity extends AppCompatActivity {
    ...
    public void onStart(){
        ...        
    ResponseReceiver responseReceiver = new ResponseReceiver(this); //passing context
    LocalBroadcastManager.getInstance(this).registerReceiver(responseReceiver,null);
        ...
    }

    public void brCallback(String param){
        Log.d("BroadcastReceiver",param);
    }
}

希望它有所帮助。

你尝试过这个吗?我不认为会起作用,因为 brCallback 不是 AppCompatActivity 的有效消息。当然,你可以将你的活动降级,但这样就会出现耦合。 - mayid

1
使用lambda表达式。一个Consumer会做到这一点。
protected void onCreate(Bundle savedInstanceState) {
    ...

    receiver = new LocationBroadcastReceiver((whatever) -> doSomething(whatever));
    registerReceiver(receiver, new IntentFilter("YOUR_MESSAGE"));
}

doSomething将是您的Activity中的一个方法。

...

class YourBroadcastReceiver extends BroadcastReceiver {

    private Consumer<Whatever> callback;

    public LocationBroadcastReceiver(Consumer<Whatever> callback) {
        this.callback = callback;
    }

    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void onReceive(Context context, Intent intent) {
            
        this.callback.accept(new Whatever());
    }
}

这是与其他所有方法的替代方案:

将方法声明为静态的,并仅使用Activity名称进行调用。

除了您解释的内容之外,这是一种耦合的方式。

将您的Activity上下文传递给BroadcastReceiver的构造函数。

那样行不通,因为您想要调用的方法不是AppCompatActivity的一部分。是的,您可以向下转换,但这样就会导致与活动耦合。

改用另一个广播或本地广播

好吧,那样只能传递一堆基元。如果您想传递对象怎么办?此外,声明新的BroadcastReceiver变得相当冗长,可能难以跟踪。


0

与Vijju的答案相同,但使用本地广播代替

public class SampleReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent intentToBroadcast =  new Intent("YOUR_ACTION_HERE");
        LocalBroadcastManager.getInstance(context).sendBroadcast(intentToBroadcast);
    }
}

在你的活动中添加这个:
public class SampleActivity extends Activity {

    @Override
    protected void onResume() {
        super.onResume();
        LocalBroadcastManager.getInstance(this).registerReceiver(mSampleReceiver, new IntentFilter(YOUR_ACTION_HERE));
    }

    @Override
    protected void onPause() {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mSampleReceiver);
        super.onPause();
    }

    private SampleReceiver mSampleReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // your code here
        }
    };
}

注意,如果您想在活动位于后台时也能收到通知,请将注册/注销调用移至onCreate/onDestroy中。


当我想像你一样声明一个变量时:"private SampleReceiver mSampleReceiver = new BroadcastReceiver()",它会给我一个错误,要求类型为:SampleReceiver,但提供的是BroadcastReceiver。 - H Hooshyar
尝试将 new BroadcastReceiver() 替换为 new SampleReceiver()。 - Adeel Ahmad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接