如何从广播接收器更新Activity的用户界面

6

我正在学习与 Android 相关的概念,包括ActivityBroadCastReceiver。我想要在两个不同的Java类中,从BroadtCastReceiver更新Activity的内容。

大致就是这样:

MyActivity.javaMyBroadtCastReceiver.java

在Android中能实现这个功能吗?


1
将您的“BroadcastReceiver”作为您的“Activity”的内部类。 - Squonk
1
@Squonk 不,我不想要一个内部类。我有这样的东西:MyActivity.java 和 MyBroadtCastReceiver.java。难道没有其他方法吗? - N Sharma
1
好的,以下是一些要点/问题...
  1. 为什么你不想要一个内部类?
  2. 你打算如何注册接收器?
  3. 如果接收器没有被“Activity”注册,那么在接收广播时,你怎么知道“Activity”是否真的存在?
  4. 通常来说,答案是 - 不,你不能从“Activity”外部以任何可靠的方式做到这一点,因为“Activity”可能存在也可能不存在,即使它存在,它也将处于不确定的状态(运行、暂停、停止等)。将接收器作为内部类是唯一保证的方法。
- Squonk
6个回答

27

BroadcastReceiver可以用于许多方面,但是当涉及到更新Activity的UI组件这样的特定情况时,在其自己的Java类文件中声明/定义BroadcastReceiver没有太大的优势。

原因在于,BroadcastReceiver必须具有先前对Activity的某些"了解",以及更新UI所需的操作。 实际上,BroadcastReceiverActivity本身相结合,将其声明/定义为内部类是有意义的。

另一个重要方面是,Activity需要处于"运行"(即可见)状态,以保证操作UI组件。 在这种情况下,注册接收器在onResume()中,而在onPause()中取消注册将有助于防止问题的发生。

使用通用模板,我会做以下操作...

class MyActivity extends Activity {

    boolean mIsReceiverRegistered = false;
    MyBroadcastReceiver mReceiver = null;

    // onCreate(...) here

    @Override
    protected void onResume() {

        // Other onResume() code here

        if (!mIsReceiverRegistered) {
            if (mReceiver == null)
                mReceiver = new MyBroadcastReceiver();
            registerReceiver(mReceiver, new IntentFilter("YourIntentAction"));
            mIsReceiverRegistered = true;
        }
    }

    @Override    
    protected void onPause() {
        if (mIsReceiverRegistered) {
            unregisterReceiver(mReceiver);
            mReceiver = null;
            mIsReceiverRegistered = false;
        }

        // Other onPause() code here

    }

    private void updateUI(Intent intent) {
        // Do what you need to do
    }

    private class MyBroadcastReceiver extends BroadcastReceiver {

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

编辑:额外补充一些注意事项...

  1. BroadcastReceiver的生命周期在进入和离开onReceive(...)之间。一旦它从onReceive(...)返回,实例将保持不活动状态等待下一个广播。
  2. 与第1点直接相关 - BroadcastReceiver并不适用于“重活”。基本上,onReceive(...)方法应该尽可能简单。任何它调用的方法也应尽可能轻量级...开始执行你的内容,然后就退出,等待下一个广播。如果更新UI需要一些时间(例如通过重新查询大量数据的数据库来更新ListView),请考虑调用执行异步操作的代码(例如AsyncTask

1
+1 谢谢有用。我们可以使用LocalBroadCastManager来完成相同的任务吗? - N Sharma
当Activity被销毁时,Broadcast Receiver作为Activity内部类是否会导致任何泄漏? - Neanderthal
重要的是还要提到,您不应该从BroadcastReceiver启动长时间运行的后台线程。请参见此处下方的Effects process state:https://developer.android.com/guide/components/broadcasts.html - Rany Albeg Wein
快速提示:registerReceived 对我不起作用。我使用了:LocalBroadcastManager.getInstance(this).registerReceiver( mReceiver, new IntentFilter(Constants.BROADCAST_ACTION)); - Mariano L

1

是的,这是可能的。这就是我的做法。 我从哪个类发送广播(BackgroundActivity.java):

public static final String BROADCAST_BUFFER_SEND_CODE = "com.example.SEND_CODE";

onCreate(){
   bufferIntentSendCode = new Intent(BROADCAST_BUFFER_SEND_CODE);
}

private void sendBufferingBroadcastSendCode() {
   bufferIntentSendCode.putExtra("buffering", "1");
   sendBroadcast(bufferIntentSendCode);
}

该类将接收广播(SendCode.java):
onResume(){
        registerReceiver(broadcastBufferReceiver, new IntentFilter(BackgroundActivity.BROADCAST_BUFFER_SEND_CODE));
}

// set up broadcast receiver
private BroadcastReceiver broadcastBufferReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent bufferIntent) {
        SendCode.this.LoadMessages(alarmNumber);
    }
};

我在onPause中取消注册它。

this.unregisterReceiver(broadcastBufferReceiver);

0

Squonk的答案只在Activity当前处于活动状态时有效。 如果您不想在其他Activity中声明/定义BroadcastReceiver(BR),或者如果您想进行一些更改,即使应用程序不在前台,那么您的解决方案将类似于以下内容。

首先,您声明BR并保存或覆盖在Acitvity中显示所需的数据。

public class MyBR extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // override the data. Eg: save to SharedPref
    }
}

然后在Activity中展示数据

TextView tv = findViewById(R.id.tv);
tv.setText(/*get the data Eg: from SharedPref*/);

你应该使用一个计时器来刷新电视:
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            runOnUiThread(new Runnable() {
                @Override
                 public void run() {
                    TextView tv = findViewById(R.id.tv);
                    tv.setText(/*get the data Eg: from SharedPref*/);
                }
            });
        }
    }, REFRESH_RATE, REFRESH_RATE);

REFRESH_RATE 可以设定为 1 秒,但由您决定。


0
You can do like this:

public class MyActivity extends Activity{

// used to listen for intents which are sent after a task was
// successfully processed
private BroadcastReceiver mUpdateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        new UpdateUiTask().execute();
    }
};

@Override
public void onResume() {        
    registerReceiver(mUpdateReceiver, new IntentFilter(
            YOUR_INTENT_ACTION));
    super.onResume();
}

@Override
public void onPause() {     
    unregisterReceiver(mUpdateReceiver);
    super.onPause();
}


// used to update the UI
private class UpdateUiTask extends AsyncTask<Void, Void, String> {

    @Override
    protected void onPreExecute() {

    }

    @Override
    protected String doInBackground(Void... voids) {
        Context context = getApplicationContext();
        String result = "test";
        // Put the data obtained after background task. 
        return result;
    }

    @Override
    protected void onPostExecute(String result) {
        // TODO: UI update          
    }
}  

}

0
在您的活动中使用与MyBroadtCastReceiver相同的意图过滤器注册一个新的BroadcastReceiver对象。由于BroadcastReceiver和MyBroadtCastReceiver具有相同的意图过滤器,它们的onReceive()都将被调用。您想要在Activity中进行的任何更新都可以在BroadcastReceiver的onReceive()中完成。

-3

你可以试试这样做,可能会有帮助。

在你想要更新UI的Activity的onCreate方法中定义这个方法:

    BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    //your code to update ui
                }
            };
   LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("giveyourappname"));

在你想要更新UI的地方定义这个操作。

try{
        ActivityManager am = (ActivityManager) this .getSystemService(ACTIVITY_SERVICE);
        List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
        ComponentName componentInfo = taskInfo.get(0).topActivity;
        Log.d("Activity", "Current Activity ::" + taskInfo.get(0).topActivity.getClassName());
        Log.d("Package", "Package Name :  "+ componentInfo.getPackageName());

        if(componentInfo.getPackageName().equals("your application package name")){
             Intent intent = new Intent("giveyourappname");
                //add data you wnat to pass in intent
                LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
        }
    }catch(Throwable e){
        e.printStackTrace();
    }

4
getRunningTasks()的文档中得知:注意:此方法仅用于调试和展示任务管理用户界面。这不应该用于应用程序核心逻辑,例如根据此处找到的信息决定不同行为。此类用法不受支持,并且将来可能会出现问题。例如,如果多个应用程序可以同时运行,则基于此处数据含义的控制流假设将是不正确的。 - LordRaydenMK
@LordRaydenMK 我该如何实现我的需求呢?请帮忙。 - N Sharma
1
@Williams:请重新阅读我在你的问题中的第一条评论,并在ActivityonResume()onPause()方法中注册接收器。这样,当接收器的onReceive(...)方法被调用时,您可以保证您的Activity处于“运行”状态。为一个需要更新特定UI元素的BroadcastReceiver创建一个单独的Java类并没有任何优势。 - Squonk
@Squonk 谢谢,明白了 :) - N Sharma
@Squonk,您能否编写答案,以便我可以接受您的回答。 - N Sharma
显示剩余7条评论

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