Android BroadcastReceiver onReceive() 在 android 4.0 上调用了两次
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21314126/
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
Android BroadcastReceiver onReceive() called twice on android 4.0
提问by girlOnSledge
I faced to one problem on android 4.0.3 (on 4.1.2 it works fine). I have in my Activity BroadcastReceiver. When I send a broadcast, method onReceive() called always twice. Please give me any suggestions about BroadcastReceiver differences on 4.0 and 4.1
我在 android 4.0.3 上遇到了一个问题(在 4.1.2 上它工作正常)。我的 Activity BroadcastReceiver 中有。当我发送广播时,方法 onReceive() 总是调用两次。请给我关于 4.0 和 4.1 上的 BroadcastReceiver 差异的任何建议
private final GcmBroadcastReceiver gcmReceiver = new GcmBroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action != null && action.equals(MyBroadcastReceiver.ACTION)) {
Log.d("tag", intent.getStringExtra("alert"));
}
}
};
};
@Override
protected void onPause() {
unregisterReceiver(gcmReceiver);
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
registerGcmReceiver();
}
private void registerGcmReceiver() {
IntentFilter filter = new IntentFilter(MyBroadcastReceiver.ACTION);
filter.setPriority(2);
filter.addCategory("com.android.mypackage");
registerReceiver(gcmReceiver, filter);
}
采纳答案by npace
Short answer: there isn't a difference. The BroadcastReceiver
class for both is from Android 2.3.2 r1.
简短的回答:没有区别。BroadcastReceiver
两者的类都来自 Android 2.3.2 r1。
I've had a similar problem, but for me it was with an HTC Desire HD with Android 2.3.5 on it - the notifications from GCM would always get received twice. I didn't manage to find the root of the problem, but there is a workaround. You can generate a unique ID for each notification in the server and send it along with the actual data. Then, in your receiver, you can update a mapping of unique ID to notification data, and if there is already data for the given ID, just ignore it.
我遇到了类似的问题,但对我来说,它是带有 Android 2.3.5 的 HTC Desire HD - 来自 GCM 的通知总是会收到两次。我没有设法找到问题的根源,但有一个解决方法。您可以为服务器中的每个通知生成一个唯一 ID,并将其与实际数据一起发送。然后,在您的接收器中,您可以更新唯一 ID 到通知数据的映射,如果给定 ID 已经有数据,则忽略它。
I probably didn't make that very clear, so here's an example:
我可能没有说清楚,所以这里有一个例子:
public void onReceive(Context context, Intent intent) {
String id = intent.getStringExtra("notificationID");
if (myMap.get(id) != null)
return;
final String action = intent.getAction();
if (action != null && action.equals(MyBroadcastReceiver.ACTION)) {
Log.d("tag", intent.getStringExtra("alert"));
myMap.put(id, *any value you need*);
}
}
If you don't need to store additional data, you can use a HashSet instead of a Map and just check if it contains the ID.
如果您不需要存储额外的数据,您可以使用 HashSet 而不是 Map 并检查它是否包含 ID。
回答by Mercury
Usually onReceive is being called twice since people are registering the broadcast in 2 locations,
Most likely you are registering in your onCreate and in your onResume.(Choose one spot to register).
Although you might have done it probably a dozen of times - it is always recommended to take another glance at the Activity Life Cycle.
通常 onReceive 会被调用两次,因为人们在 2 个位置注册广播,
很可能您正在 onCreate 和 onResume 中注册。(选择一处报名)。
尽管您可能已经完成了十几次 - 始终建议您再看一眼Activity Life Cycle。
回答by Sushant
I had same issue.
我有同样的问题。
onReceive()
gets called twice because i was registering Broadcast receiver twice. One in my activity's onCreate()
and another in manifest
.
onReceive()
被调用两次,因为我注册了两次广播接收器。一个在我的活动中onCreate()
,另一个在manifest
.
I remove broadcast receiver registration from onCreate()
and it's working fine. Register your broadcast receiver in only one of them.
我删除了广播接收器注册onCreate()
,它工作正常。仅在其中之一中注册您的广播接收器。
There are two ways to do this:
有两种方法可以做到这一点:
- Statically in the manifest file.
- Dynamically in the code.
- 静态地在清单文件中。
- 在代码中动态地。
Which method (static or dynamic) to use when depends completely upon what you're trying to do. Basically when you want to do some changes right on the screen (home screen, launcher, status bar, etc.) by showing up some notification or some indicator in the status bar by listening to system wide events or maybe those sent by other apps, then it make sense to use statically registered broadcast receivers. Whereas based on similar events you want to do changes right in your app when the user is using it or maybe it's put in the background, then it makes sense to use dynamically registered receivers which'll last till the registering components are destroyed.
何时使用哪种方法(静态或动态)完全取决于您要做什么。基本上,当您想在屏幕(主屏幕、启动器、状态栏等)上进行一些更改时,通过侦听系统范围的事件或其他应用程序发送的事件,在状态栏中显示一些通知或某些指示器,那么使用静态注册的广播接收器是有意义的。而基于类似的事件,您希望在用户使用应用程序或将其置于后台时直接在应用程序中进行更改,然后使用动态注册的接收器是有意义的,该接收器将持续到注册组件被销毁。
For more info: http://codetheory.in/android-broadcast-receivers/
回答by SINIGAMI
Inatialize BroadcastReciver
on onStart()
. This fix my problem.
InatializeBroadcastReciver
上onStart()
。这解决了我的问题。
回答by VahidShir
For Xamarindevelopers:
对于Xamarin开发人员:
I had a similar problem and for me it was because I registered the broadcast receiver in two places, one somewhere in my codes:
我有一个类似的问题,对我来说这是因为我在两个地方注册了广播接收器,一个在我的代码中:
Android.App.Application.Context.RegisterReceiver(myReceiver, new IntentFilter("MY_INTENT_ACTION"));
And the other via Attributes:
另一个通过属性:
[BroadcastReceiver(Enabled = true, Exported = true)]
[IntentFilter(new[] { "MY_INTENT_ACTION" })]
public class MyReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
}
}
I removed Attributes and the problem solved!
我删除了属性,问题解决了!
回答by ymerdrengene
I was calling sendBroadcast(intent)
multiple times in my Service class
我sendBroadcast(intent)
在我的服务类中多次调用
Removing one of themfixed the problem.
删除其中之一解决了问题。
回答by varghesekutty
You can also implement it by setting a boolean flag, when you enter to onReceive()
function
您还可以通过设置布尔标志来实现它,当您输入onReceive()
函数时
回答by Subho
I believe this is the correct way to determine if Wifi has connected or disconnected.
我相信这是确定 Wifi 是否已连接或断开连接的正确方法。
The onReceive() method in the BroadcastReceiver:Just put a Simple checking(Its a logic implemented by SharedPreferences):
BroadcastReceiver 中的 onReceive() 方法:只需简单检查即可(这是 SharedPreferences 实现的逻辑):
package com.example.broadcasttest;
import java.util.List;
import java.util.Random;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import android.widget.Toast;
public class CustomReceiver extends BroadcastReceiver {
boolean isInternetPresent = false;
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
ActivityManager am = (ActivityManager) context
.getSystemService(Activity.ACTIVITY_SERVICE);
String packageName = am.getRunningTasks(1).get(0).topActivity
.getPackageName();
@SuppressWarnings("deprecation")
List<RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
if(packageName.equals("com.example.broadcasttest") && taskInfo.get(0).topActivity.getClassName().toString().equals("com.example.broadcasttest.MainActivity"))
{
// SharedPreferences sharedPreferences=context.getSharedPreferences("FLAG", Context.MODE_PRIVATE);
// String check=sharedPreferences.getString("check","");
ConnectionDetector cd = new ConnectionDetector(context);
// get Internet status
isInternetPresent = cd.isConnectingToInternet();
// check for Internet status
if (isInternetPresent) {
SharedPreferences sharedPreferences=context.getSharedPreferences("FLAG", Context.MODE_PRIVATE);
String check=sharedPreferences.getString("check","");
if(check.equals("chkd1")){
Log.d("activity name", "CURRENT Activity ::" + taskInfo.get(0).topActivity.getClassName()+" Package Name : "+componentInfo.getPackageName());
String abc = context.getClass().toString();
Toast.makeText(context, "hiiii "+abc, Toast.LENGTH_SHORT).show();
Log.e("ghorar kochu", "checking:");
MainActivity m = new MainActivity();
m.abc(context);
}
else if(check.equals("chkd"))
{
Log.e("Thanks For the checking", "checking:"+check);
}
}
else if (!isInternetPresent) {
SharedPreferences sharedPreferences1=context.getSharedPreferences("FLAG", Context.MODE_PRIVATE);
SharedPreferences.Editor editor1=sharedPreferences1.edit();
editor1.putString("check","chkd1");
editor1.commit();
}
}
}
}
This is actual receiver class.Now coming to the main activity.
这是实际的接收器类。现在进入主要活动。
package com.example.broadcasttest;
import java.net.URLEncoder;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends Activity {
Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences sharedPreferences1=getSharedPreferences("FLAG", Context.MODE_PRIVATE);
SharedPreferences.Editor editor1=sharedPreferences1.edit();
editor1.putString("check","chkd1");
editor1.commit();
btn = (Button)findViewById(R.id.btn);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent in =new Intent(MainActivity.this,Second.class);
startActivity(in);
}
});
}
public void abc(Context context){
try{
SharedPreferences sharedPreferences1=context.getSharedPreferences("FLAG", Context.MODE_PRIVATE);
SharedPreferences.Editor editor1=sharedPreferences1.edit();
editor1.putString("check","chkd");
editor1.commit();
}
catch(NullPointerException e)
{
e.printStackTrace();
}
new JsonForTimeLineList().execute();
}
class JsonForTimeLineList extends AsyncTask<Void, Void, Void> {
private String msg = null;
@Override
protected void onPreExecute() {
super.onPreExecute();
// pDialog.show();
}
@Override
protected Void doInBackground(Void... params) {
return null;
}
@Override
protected void onPostExecute(Void resultaaa) {
Log.e("hi", "helo");
}
}
}
This code only works when your app is onresume state and when it is in MainActivity.
此代码仅在您的应用处于 onresume 状态且处于 MainActivity 时有效。
Here is my ConnectionDetector class.Through which i am checking the connection of wifi.
这是我的 ConnectionDetector 类。通过它我正在检查 wifi 的连接。
package com.example.broadcasttest;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
public class ConnectionDetector {
private Context _context;
public ConnectionDetector(Context context){
this._context = context;
}
public boolean isConnectingToInternet(){
ConnectivityManager connectivity = (ConnectivityManager) _context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null)
{
NetworkInfo[] info = connectivity.getAllNetworkInfo();
if (info != null)
for (int i = 0; i < info.length; i++)
if (info[i].getState() == NetworkInfo.State.CONNECTED)
{
return true;
}
}
return false;
}
}
And here is my Manifest.xml
这是我的 Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcasttest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Second"
android:label="@string/app_name" >
</activity>
<receiver android:name="com.example.broadcasttest.CustomReceiver">
<intent-filter >
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
</application>
</manifest>
And here is my log when i am enable the wifi.
这是我启用 wifi 时的日志。
11-19 20:13:11.474: D/activity name(25417): CURRENT Activity ::com.example.broadcasttest.MainActivity Package Name : com.example.broadcasttest
11-19 20:13:11.481: E/ghorar kochu(25417): checking:
11-19 20:13:11.542: E/hi(25417): helo
11-19 20:13:11.573: V/RenderScript(25417): Application requested CPU execution
11-19 20:13:11.580: V/RenderScript(25417): 0xb90a7850 Launching thread(s), CPUs 4
11-19 20:13:17.158: E/Thanks For the checking(25417): checking:chkd
If you have any query just let me know.
如果您有任何疑问,请告诉我。
回答by William Kinaan
I have just had this problem and I found a solution that wasn't mentioned in any of the existed answers, so I would like to share it.
我刚刚遇到了这个问题,我找到了一个在任何现有答案中都没有提到的解决方案,所以我想分享一下。
I register the BroadcastReceiver just once in onCreate
我只在onCreate 中注册了 BroadcastReceiver
However, I was registering it like this:
但是,我是这样注册的:
LocalBroadcastManager.getInstance(this).registerReceiver(new MyBroadcastReceiver(), mStatusIntentFilter);
In other words, I was constructingthe boradcastReceiver instance inside the registerReceivercall.
换句话说,我在registerReceiver调用中构建了 boradcastReceiver 实例。
But when I construct the BroadcastReceiver in a separate statementthe problem was solved. Like this:
但是当我在单独的语句中构造 BroadcastReceiver 时,问题就解决了。像这样:
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
LocalBroadcastManager.getInstance(this).registerReceiver(myBroadcastReceiver , mStatusIntentFilter);
Very weird workaround, maybe it's a bug that will be fixed later.
很奇怪的解决方法,也许这是一个稍后会修复的错误。
回答by E_X
I faced a similar problem, but my BroadcastReceiver
was static because I wanted to use it as an inner class, what I did was to register the receiver (in order to create the static instance), then unregister the same receiver, that made it be called once which is by the AlarmManager
timer, ofcourse code is always much explanatory :
我遇到了类似的问题,但我BroadcastReceiver
是静态的,因为我想将它用作内部类,我所做的是注册接收器(为了创建静态实例),然后取消注册相同的接收器,这使得它被调用一旦是AlarmManager
计时器,当然代码总是有很多解释:
public void setAlarm(Context context) {
Log.d("EX", "Alarm SET !!");
Intent intent = new Intent("com.example.START_ALARM");
IntentFilter myIf = new IntentFilter("com.example.START_ALARM");
PendingIntent sender = PendingIntent.getBroadcast(context, 192837,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
myBroadcastReceiver mbr = new myBroadcastReceiver();
// ****Here goes the trick.****
context.registerReceiver(mbr, myIf);
context.unregisterReceiver(mbr);
// Get the AlarmManager service
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Long firstTime = SystemClock.elapsedRealtime()
+ TimeUnit.SECONDS.toMillis(70);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstTime,
TimeUnit.SECONDS.toMillis(70), sender);
}