为什么BroadcastReceiver即使在应用程序在后台工作?

我正在使用BroadcastReceiver在我的应用程序中检查Internet连接,如果连接丢失,我会显示一个警告对话框。 它工作正常。 但我的问题是,即使我的应用程序在背后,BroadcastReceiver的工作原理。 因此,即使用户在其他应用程序中,互联网连接丢失,对话框也会popup。 这是完全毁了我的应用程序。

有没有人有任何想法如何在应用程序中限制广播接收器?

这是我的BroadcastReceiver:

public class ConnectivityChangedReceiver extends BroadcastReceiver{ @Override public void onReceive( Context context, Intent intent ) { ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE ); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isConnected()) { } else { try{ Intent i=new Intent(context, InternetDialogActivity.class); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(i); } catch(Exception e){ e.printStackTrace(); } } } } 

活动是:

  public class InternetDialogActivity extends Activity implements OnClickListener{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.internet_dialog_box); getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); Button retryButton = (Button) findViewById(R.id.retryInternetButton); retryButton.setOnClickListener(this); } public boolean checkConnectivity(){ ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); if (networkInfo!=null && networkInfo.isConnected()) { finish(); return true; } else { Intent intent = getIntent(); finish(); startActivity(intent); return false; } } @Override public void onClick(View view) { switch(view.getId()){ case R.id.retryInternetButton: try{ checkConnectivity(); } catch(Exception e){ e.printStackTrace(); } break; } } } 

以下是我如何在清单中声明接收者和活动:

  <receiver android:name="com.lisnx.service.ConnectivityChangedReceiver" android:label="NetworkConnection"> <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> </intent-filter> </receiver> <activity android:name="com.lisnx.activity.InternetDialogActivity" android:configChanges="orientation|keyboardHidden" android:theme="@android:style/Theme.Dialog" /> 

我已经读过,我们可以限制BroadcastReceiver在应用程序内工作,不要在清单中声明它。 但是我不知道接收器如何工作呢? 请帮帮我。 我被卡住了。 提前感谢。

Solutions Collecting From Web of "为什么BroadcastReceiver即使在应用程序在后台工作?"

BroadcastReceiver接收器在应用程序处于后台时起作用,因为接收器拾取的事件是全局发送的,每个应用程序都被注册以监听这些事件,而不pipe它是否在运行。

为了解决这个问题,在你的BroadcastReceiveronReceive代码中,检查你的应用程序是否在前台。

有一个 – 我只知道一个 – 始终如一的有效方法来做到这一点。 您需要跟踪您的应用程序的暂停/恢复操作。 确保你在每一个活动中检查这个。

这个答案中有一些示例代码(解决scheme#1)。 在你的情况下,你需要检查MyApplication.isActivityVisible() == true作为一个validation,然后再从你的BroadcastReceiver做任何事情。

您是否尝试从清单中删除Intentfilter,并在活动中注册/取消注册? 因此,您可以尝试在onStart()中注册Intentfilter,并在onStop()方法上取消注册。 代码有这样的想法:

 static final String ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; IntentFilter filter = new IntentFilter(ACTION); this.registerReceiver(ConnectivityChangedReceiver, filter); unregisterReceiver(ConnectivityChangedReceiver); 

如果还不熟悉,还应该了解活动生命周期 。

您应该在每个活动的onPause()onResume()中注册/取消注册您的BroadcastReceiver 。 那么你知道,当你的应用程序在前台时,接收器只是“聆听”。 你可以通过创build你自己的扩展ActivityBaseActivity来轻松做到这BaseActivity ,并覆盖onPause()onResume()并注册/注销你的接收器。 只需让所有的活动扩展BaseActivity并像往常一样通过super.onResume()super.onPause()调用。 以下是示例代码:

 public class BaseActivity extends Activity { // Create an IntentFilter to listen for connectivity change events static IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"); // Create an instance of our BroadcastReceiver static ConnectivityChangedReceiver receiver = new ConnectivityChangedReceiver(); @Override protected void onPause() { super.onPause(); // Stop listening for connectivity change events unregisterReceiver(receiver); } @Override protected void onResume() { super.onResume(); // Listen for connectivity change events registerReceiver(receiver, filter); } } 

所有的活动都应该扩展BaseActivity ,如果他们需要在onResume()onPause()做任何特殊的事情,只要确保像这样调用super.onXXXXX()

 public MyActivity extends BaseActivity { @Override protected void onResume() { super.onResume(); // Here you do whatever you need to do in onResume() of your activity ... } @Override protected void onPause() { super.onPause(); // Here you do whatever you need to do in onPause() of your activity ... } } 

我没有通过编译器运行代码,所以我很抱歉,如果有一个错字,或者我错过了一些东西。

我想你将不得不确保在应用程序处于后台时不使用接收器。 为此,您将不得不使用onPause()和onResume()方法中的每个活动。

要使广播接收器仅在您运行应用程序时触发,请按照以下代码进行操作。

1.像这样创build您的广播接收器:


 import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class InternetStatusNotifier extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { //Recieve notification here } } 

2.制作一个活动或片段,让广播接收器像这样工作:


 import android.app.Activity; import android.content.IntentFilter; import android.os.Bundle; public class MainActivity extends Activity { private InternetStatusNotifier mInternetStatusNotifier; @Override protected void onCreate(Bundle savedInstanceState) { mInternetStatusNotifier = new InternetStatusNotifier(); super.onCreate(savedInstanceState); } @Override protected void onResume() { registerReceiver(mInternetStatusNotifier, new IntentFilter( "android.net.conn.CONNECTIVITY_CHANGE")); super.onResume(); } @Override protected void onPause() { unregisterReceiver(mInternetStatusNotifier); super.onPause(); } 

注意:这就是你如何以屏幕特定的方式使用广播接收器。 只有屏幕显示将以这种方式接收广播。 当你使用清单文件注册广播时,他们甚至在应用程序closures时收到

据我所知,如果广播接收器是在manifest.xml中注册的,那么只要应用程序存在就存在广播接收器。 此外,dynamic注册的接收器(也就是说,以编程方式注册您的BroadcastReceiver)在UI线程上被调用。 这意味着你的接收器阻止了任何UI处理,因此onReceive()方法应该尽可能快。

不过,我会尝试讨论关于广播接收器的信息。 但是,首先应该知道一些信息。 首先,广播接收器是一个独立的应用程序组件,这意味着即使其他应用程序组件没有运行,它也将继续运行。 这就是为什么我们在活动onPause注销广播接收器。 另外,开发者应该在Activity.onResume()实现中注册这个。

其次,开发者不应该在Activity.onSaveInstanceState()取消注册,因为如果用户移回到历史堆栈中,将不会被调用。 我从BroadcastReceiver文档中提供了这些信息。

另一点是BroadcastReceiver对象仅在调用onReceive()的持续时间内有效。 一旦onReceive()方法完成,您的BroadcastReceiver终止。

现在,如何以编程方式注册您的接收器:

 public abstract Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter) 

这里,当任何广播意图与filter匹配时,BroadcastReceiver-接收器将被调用。
IntentFilter-意图指定你的接收器应该听哪个事件。

寄存器:

 YourConnectionListener receiver; this.reciever = new YourConnectionListener(); IntentFilter filter = new IntentFilter(); filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(this.reciever, filter); 

发送您的广播信息:

 Intent intent = new Intent(); intent.putExtra("Message", "Your connectivity info has Changed!!"); this.sendBroadcast(intent); 

接收器:

现在,需要接收广播。 每当事件发生时,Android都会在所有注册的广播接收器上调用onReceive()方法。 假设你想在连接改变的时候得到通知。

 public class YourConnectionListener extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent){ // your Code } } 

onReceive()有两个参数:

  • 上下文:您可以用来访问附加信息或启动服务或活动的上下文对象。
  • 意图:意图用于注册您的接收器。 该对象包含可以在您的实现中使用的其他信息。
    此外,开发人员应避免在您的BroadcastReceiver任何长期任务。 因此,在静态和dynamic注册的接收机中,开发人员应该在接收机本身中执行较小的任务。对于任何更长的任务,您应该从接收机内启动服务。

这是广播接收机在Android中的工作方式。 如果您在清单中注册广播并且您的应用程序未运行,则Android将启动一个新的进程来处理广播。 从广播接收器直接显示UI通常是一个坏主意,因为这可能会中断其他应用程序。 我也不相信普遍的“连接丢失”对话也是一个好主意。 这可能应该由每个使用networking访问的活动来处理。

至于原来的问题,当你的活动在后台( onPause()等)时,你需要禁用你的接收器,当你来到前台( onResume()等)时启用它。 在您的清单中放入enabled=false ,然后在您的代码中使用类似下面的内容切换它:

  public static void toggle(Context context, boolean enable) { int flag = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; ComponentName receiver = new ComponentName(context, ConnectivityMonitor.class); context.getPackageManager().setComponentEnabledSetting(receiver, flag, PackageManager.DONT_KILL_APP); } 

一个简单的方法来查找应用程序是否在前台

 if((mContext.getPackageName().equalsIgnoreCase( ((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE)) .getRunningTasks(1).get(0).topActivity.getPackageName()))) { //app is in foreground; } 

当有人打开它时,我最好build议你从应用程序中检查互联网设置,这里是一段代码。

 public static boolean isNetworkConnected(Context ctx) { ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo ni = cm.getActiveNetworkInfo(); if (ni == null) { return false; // There are no active networks. } else return true; }