Android 为什么即使应用程序在后台时 BroadcastReceiver 也能工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12274997/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why BroadcastReceiver works even when app is in background ?
提问by Yogesh Somani
I am checking Internet connectivity in my app using BroadcastReceiver and I show an alert dialog if the connection is lost. It works fine. But my problem is that BroadcastReceiver works even if my app is in backgroung. So dialog pops up when internet connection is lost even if user is in some other app. This is totally ruining my app.
我正在使用 BroadcastReceiver 检查我的应用程序中的 Internet 连接,如果连接丢失,我会显示一个警报对话框。它工作正常。但我的问题是即使我的应用程序在后台,BroadcastReceiver 也能工作。因此,即使用户在其他应用程序中,当互联网连接丢失时也会弹出对话框。这完全毁了我的应用程序。
Has anyone got any idea how to restrict Broadcast receiver in the app only?
有没有人知道如何仅在应用程序中限制广播接收器?
Here is my BroadcastReceiver :
这是我的 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();
}
}
}
}
And the activity is:
活动是:
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;
}
}
}
Here is how I declared receiver and activity in manifest:
这是我在清单中声明接收器和活动的方式:
<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" />
I have read that we can restrict BroadcastReceiver to work within the app by not declaring it in the manifest. But I don't know how will receiver work then? Please help me. I am stuck on it badly. Thanx in advance.
我已经读过我们可以通过不在清单中声明它来限制 BroadcastReceiver 在应用程序中工作。但我不知道接收器将如何工作?请帮我。我被严重困住了。提前谢谢。
回答by Eric
A BroadcastReceiver
works when the app is in the background because the event that the receiver picks up are sent globally, and each app is registered to listen in on these, regardless of whether or not it is running.
ABroadcastReceiver
在应用程序处于后台时工作,因为接收器接收的事件是全局发送的,并且每个应用程序都注册以侦听这些事件,无论它是否正在运行。
To deal with this, in your BroadcastReceiver
's onReceive
code, check if your app is in the foreground.
为了解决这个问题,在你BroadcastReceiver
的onReceive
代码中,检查你的应用程序是否在前台。
There is one--and only one that I know of--consistently effective method to do this. You need to keep track of your pause/resume actions for your application. Ensure that you check this in everyactivity.
有一种——而且我只知道一种——始终有效的方法来做到这一点。您需要跟踪应用程序的暂停/恢复操作。确保在每项活动中都检查这一点。
There is some sample code in this answer(solution #1). In your case, you would want to check MyApplication.isActivityVisible() == true
as a validation before doing anything from your BroadcastReceiver
.
这个答案中有一些示例代码(解决方案#1)。在您的情况下,您希望MyApplication.isActivityVisible() == true
在从BroadcastReceiver
.
回答by soreal
Have you tried to remove the Intent filter from the manifest and register/unregister it in activity? So you can try to register Intent filter in onStart() and unregister it on onStop() methods. The code goes somethink like this:
您是否尝试从清单中删除 Intent 过滤器并在活动中注册/取消注册它?因此,您可以尝试在 onStart() 中注册 Intent 过滤器并在 onStop() 方法中取消注册。代码有点像这样:
static final String ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
IntentFilter filter = new IntentFilter(ACTION);
this.registerReceiver(ConnectivityChangedReceiver, filter);
unregisterReceiver(ConnectivityChangedReceiver);
You should also learn about Activity Lifecycle, if it's not familiar yet.
如果还不熟悉,您还应该了解Activity Lifecycle。
回答by David Wasser
You should register/unregister your BroadcastReceiver
in onPause()
and onResume()
of each activity. Then you know that the receiver is only "listening" when your app is in the foreground. You can easily do that by creating your own BaseActivity
that extends Activity
and overrides onPause()
and onResume()
and registers/unregisters your receiver. Just have all your activities extend BaseActivity
and have them call through to super.onResume()
and super.onPause()
as usual. Here's example code:
您应注册/注销您BroadcastReceiver
在onPause()
和onResume()
每个活动的。然后您就知道接收器仅在您的应用程序处于前台时才“聆听”。您可以轻松地做到这一点通过创建自己的BaseActivity
延伸Activity
和覆盖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);
}
}
All your activities should extend BaseActivity
and if they need to do anything special in onResume()
or onPause()
just make sure to call through to super.onXXXXX()
like this:
你所有的活动都应该扩展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
...
}
}
I didn't run the code through a compiler so I apologize if there's a typo or I missed something.
我没有通过编译器运行代码,所以如果有拼写错误或遗漏了什么,我深表歉意。
回答by Final Destination
I think you will have to make sure that you are not using the receiver when app is in background. For that you will have to use every activities onPause() and onResume() methods.
我认为您必须确保当应用程序处于后台时您没有使用接收器。为此,您必须使用 onPause() 和 onResume() 方法的每个活动。
回答by Ratna Halder
As far as I know, if Broadcast receiver is registered inside manifest.xml then broadcast receiver exists as long as application exists. Also, Dynamically registered receivers (that means, Register your BroadcastReceiver programmatically) are called on the UI thread. This means that your receivers blocks any UI handling and thus the onReceive()
method should be as fast as possible.
据我所知,如果广播接收器在 manifest.xml 中注册,那么只要应用程序存在,广播接收器就存在。此外,在 UI 线程上调用动态注册的接收器(即以编程方式注册您的 BroadcastReceiver)。这意味着您的接收器会阻止任何 UI 处理,因此该onReceive()
方法应该尽可能快。
However, I will try to discuss information about Broadcast Receiver. But, first should know some information. Firstly, Broadcast receiver is a standalone application component which means it will continue running even when other application component are not running. That's why we unregister broadcast receiver in onPause on the activity. Also, Developer should register this in Activity.onResume()
implementation.
但是,我将尝试讨论有关广播接收器的信息。但是,首先应该了解一些信息。首先,广播接收器是一个独立的应用程序组件,这意味着即使其他应用程序组件没有运行,它也会继续运行。这就是我们在活动的 onPause 中取消注册广播接收器的原因。此外,开发人员应该在Activity.onResume()
实现中注册它。
Secondly, Developer should not unregister in Activity.onSaveInstanceState()
, because this won't be called if the user moves back in the history stack. I have put that information from BroadcastReceiver documentation.
其次,Developer 不应在 中注销Activity.onSaveInstanceState()
,因为如果用户移回历史堆栈,则不会调用此方法。我已经将这些信息来自BroadcastReceiver 文档。
Another point is that a BroadcastReceiver object is only valid for the duration of the call to onReceive(). As soon as the onReceive() method is finished, your BroadcastReceiver terminates.
另一点是 BroadcastReceiver 对象仅在 onReceive() 调用期间有效。一旦 onReceive() 方法完成,您的 BroadcastReceiver 就会终止。
Now, how to register your receiver programmatically:
现在,如何以编程方式注册您的接收器:
public abstract Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter)
Here, BroadcastReceiver- receiver will be call when any broadcast intent match with filter.
And IntentFilter- Intent specifies which event your receiver should listen to.
在这里,当任何广播意图与过滤器匹配时,将调用 BroadcastReceiver-receiver。
并且 IntentFilter- Intent 指定您的接收器应该侦听哪个事件。
Register:
登记:
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);
Sent your Broadcast Info:
发送您的广播信息:
Intent intent = new Intent();
intent.putExtra("Message", "Your connectivity info has Changed!!");
this.sendBroadcast(intent);
Receiver:
接收者:
Now, need to receive the Broadcast. Android calls the onReceive() method on all registered broadcast receivers whenever the event occurs. Say you want to be notified whenever the connection is changed.
现在,需要接收广播。每当事件发生时,Android 都会在所有注册的广播接收器上调用 onReceive() 方法。假设您希望在连接更改时收到通知。
public class YourConnectionListener extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent){
// your Code
}
}
onReceive()
has two arguments:
onReceive()
有两个参数:
- context: The Context object you can use to access additional information or to start services or activities.
- intent: Intent used to register your receiver. This object contains additional information that you can use in your implementation.
Additionally, Developer should avoid any long-lasting tasks in your BroadcastReceiver. So, In statically and dynamically registered receivers, Developer should do minor tasks in the receiver itself.For any longer tasks you should start a service from within your receiver.
- 上下文:可用于访问附加信息或启动服务或活动的上下文对象。
- 意图:用于注册您的接收器的意图。此对象包含您可以在实现中使用的附加信息。
此外,开发人员应避免在您的 BroadcastReceiver 中执行任何长期任务。因此,在静态和动态注册的接收器中,开发人员应该在接收器本身中执行次要任务。对于任何更长的任务,您应该从接收器内部启动服务。
回答by Sheraz Ahmad Khilji
To make a Broadcast Receiverthat fires only when you app is running follow the below code.
要制作仅在您的应用程序运行时触发的广播接收器,请遵循以下代码。
1.Create your Broadcast Receiver like this:
1.像这样创建你的广播接收器:
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.Make an activity or fragment where you want the Broadcast Receiver to work like this:
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();
}
Note:That is how you use broadcasts receiver in a screen specific manner. Only the screen displaying will receive broadcasts in this way. When you register broadcast using manifest file then they are even received when app is closed
注意:这就是您以特定于屏幕的方式使用广播接收器的方式。只有屏幕显示才会以这种方式接收广播。当您使用清单文件注册广播时,它们甚至会在应用程序关闭时收到
回答by Nikolay Elenkov
That is the way broadcast receivers work in Android. If you register for a broadcast in the manifest and your app is not running, Android will start a new process to handle the broadcast. It is generally a bad idea to directly show a UI from a broadcast receiver, because this may interrupt other apps. I'm also not convinced that a universal 'connection lost' dialog is a good idea either. This should probably be handled by each activity that uses network access.
这就是广播接收器在 Android 中的工作方式。如果您在清单中注册广播并且您的应用程序没有运行,Android 将启动一个新进程来处理广播。直接从广播接收器显示 UI 通常是一个坏主意,因为这可能会中断其他应用程序。我也不相信通用的“连接丢失”对话框是个好主意。这可能应该由每个使用网络访问的活动来处理。
As for the original question, you need to disable your receiver when your activity goes in the background (onPause()
, etc.) and enable it when you come to the foreground (onResume()
, etc). Put enabled=false
in your manifest and then use something like this in your code to toggle it as necessary:
至于最初的问题,当您的活动进入后台(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);
}
回答by yubaraj poudel
I better suggest you to check the internet setting from the application when someone opens it, here is the piece of code how i do it.
我最好建议你在有人打开应用程序时检查它的互联网设置,这是我如何做的一段代码。
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;
}
回答by Naren
A simple way of finding whether the app is in foreground or not
查找应用程序是否在前台的简单方法
if((mContext.getPackageName().equalsIgnoreCase(
((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE))
.getRunningTasks(1).get(0).topActivity.getPackageName())))
{
//app is in foreground;
}