如果片段可见,则从 Service 或 BroadcastReceiver 更新片段 UI

Update Fragment UI from Service or BroadcastReceiver if Fragment is visible

TL;DR

当 运行 服务在屏幕上可见时,我需要更新片段的 UI(toggleButton)。

背景

我在片段(名为 homeFragment)上有一个 toggleButton。它显示 Stop 文本 if(toggleButton.isChecked),否则显示 Start 文本。 toggleButton 启动和停止服务。

现在,有两种方法可以停止此服务。一种是再次点击 toggleButton(这会停止服务),另一种是通过服务本身。

启动此服务后,toggleButton 显示 'Stop' 文本。 此服务创建粘性通知并在之后调用 this.stopSelf。 此通知有一个关闭此通知的按钮。 此选项调用 BroadcastReceiver 到 stop/cancel 通知。

Problem/Requirement

现在,当通过通知按钮关闭服务时,如果应用程序可见或在前台,我想在 Fragment 上触发 toggleButton 以显示 Start再次发短信。 如果 appActivity 在 background/paused/closed 中,我不需要处理这个(我通过检查 onCreate() 中的 sharedPrefs 来处理)

我试过了:

  1. 使用布尔 SharedPref 键(比如 isServiceRunning)跟踪服务状态。在 onCreate() 中,我保存了一个 true 布尔值。在 onDestoy() 中,我保存了一个 false 布尔值。 然后,我将 onSharedPreferencesChangeListener 注册到 sharedPrefs(inside fragment),它检查 isServiceRunning 的 sharedPrefkey 是否已更改。 如果是,它会调用 toggleButton.setChecked(false);

    这种方法很少奏效。有时有效,有时什么也没有。

  2. 用了一个LocalBroadcastReceiver,但是有很多错误。比如,can't Instantiate Receiver: no empty method。我试图解决, 但发现这需要大量工作。而且,除了在另一个 class 中访问片段视图只是一团糟,因为你必须给他们片段的 layout 任何时候都可以是 null。给这个接收器 class 一个来自 onViewCreated()View 元素是困难的。我试着给 layout + findViewById 充气来做到这一点, 但没有 success/error.

    这个方法没用过,我觉得效率不高

  3. 到处使用static变量。所以,我制作了一个全局 View 元素(比如 activeLayout)。 它是 privatestatic,因此 static 方法(切换 toggleButton 的状态)可以访问片段的布局。 activeLayoutonViewCreated() 方法中分配了一个布局。

    现在,如果我从 stopNotificationReceiver 调用此 static 方法,它会起作用(我只检查了两次)。但是,显示了一个新警告:

    Warning: Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

    所以,我无法将 activeLayout 作为 static 变量,这打破了整个想法。

如果我做错了,请给我一个替代方案。或者纠正我如何做到这一点。

使用广播接收器并在您的片段中注册它

您可以在您的服务中使用以下代码调用广播接收器

Intent intent = new Intent();
intent.putExtra("extra", cappello); 
intent.setAction("com.my.app");
sendBroadcast(intent);

在你的 Fragment 中实现一个 BroadcastReceiver :

private class MyBroadcastReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    Bundle extras = intent.getExtras();
    String state = extras.getString("extra");
    updateView(state);// update your textView in the main layout 
  }
} 

并在Fragment的onResume()中注册:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.my.app");
receiver = new MyBroadcastReceiver();
registerReceiver(receiver, intentFilter);