Android BroadcastReceiver onReceive()在android 4.0上调用了两次

时间:2014-01-23 16:37:57

标签: android broadcastreceiver

我遇到了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);
}

15 个答案:

答案 0 :(得分:39)

通常onReceive被调用两次,因为人们在2个地点注册广播, 您最有可能在onCreate和onResume中注册。 (选择一个注册地点)。

虽然你可能已经做过十几次了 - 但总是建议再看一眼Activity Life Cycle

答案 1 :(得分:19)

我有同样的问题。

onReceive()被调用两次因为我正在两次注册广播接收器。一个在我的活动中onCreate(),另一个在manifest

我从onCreate()删除了广播接收者注册,并且它正常工作。只在其中一个中注册您的广播接收器。

有两种方法可以做到这一点:

  • 静态文件中的静态文件。
  • 在代码中动态显示。

使用哪种方法(静态或动态)完全取决于您要做的事情。基本上当你想通过在系统范围的事件或其他应用程序发送的事件中显示状态栏中的一些通知或某些指示器时,在屏幕上(主屏幕,启动器,状态栏等)进行一些更改,那么使用静态注册的广播接收器是有意义的。虽然基于类似事件,您希望在用户使用它时在应用程序中进行更改,或者可能将其放在后台,然后使用动态注册的接收器是有意义的,这些接收器将持续到注册组件被销毁。

欲了解更多信息: http://codetheory.in/android-broadcast-receivers/

答案 2 :(得分:9)

简短回答:没有区别。两者的BroadcastReceiver类来自Android 2.3.2 r1。

我遇到了类似的问题,但对我而言,它是搭配安装了Android 2.3.5的HTC Desire HD - 来自GCM的通知总是会收到两次。我没有设法找到问题的根源,但有一个解决方法。您可以为服务器中的每个通知生成唯一ID,并将其与实际数据一起发送。然后,在您的接收器中,您可以更新唯一ID到通知数据的映射,如果已经有给定ID的数据,则忽略它。

我可能没有说清楚,所以这是一个例子:

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*);
    }
}

如果您不需要存储其他数据,则可以使用HashSet而不是Map,只检查它是否包含ID。

答案 3 :(得分:8)

BroadcastReciver上初始化onStart()。这解决了我的问题。

答案 4 :(得分:2)

对于 Xamarin 开发人员:

我有一个类似的问题,对我来说是因为我在两个地方注册了广播接收器,其中一个在我的代码中某个地方:

Android.App.Application.Context.RegisterReceiver(myReceiver, new IntentFilter("MY_INTENT_ACTION"));

另一个通过属性:

[BroadcastReceiver(Enabled = true, Exported = true)]
[IntentFilter(new[] { "MY_INTENT_ACTION" })]
public class MyReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
    }
}

我删除了属性,问题解决了!

答案 5 :(得分:1)

当您输入onReceive()函数

时,您也可以通过设置布尔标志来实现它

答案 6 :(得分:1)

我认为这是确定Wifi是否已连接或断开连接的正确方法。

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();
        }

    }




}



}

这是实际的接收器类。现在进入主要活动。

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");


        }

    }

    }

此代码仅在您的应用处于休眠状态且处于MainActivity时才有效。

这是我的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;
        }


}

这是我的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>

当我启用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

如果您有任何疑问,请告诉我。

答案 7 :(得分:1)

我在服务类

中多次致电sendBroadcast(intent)

删除其中一个解决了问题。

答案 8 :(得分:0)

我遇到了类似的问题,但我的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);
}

答案 9 :(得分:0)

我刚遇到这个问题,我找到了一个在任何现有答案中都没有提到的解决方案,所以我想分享一下。

我在 onCreate

中只注册了一次BroadcastReceiver

但是,我正在这样注册:

LocalBroadcastManager.getInstance(this).registerReceiver(new MyBroadcastReceiver(), mStatusIntentFilter);

换句话说,我在 registerReceiver 调用中构建 boradcastReceiver实例。

但是当我在单独的声明中构建BroadcastReceiver时,问题就解决了。像这样:

MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();

LocalBroadcastManager.getInstance(this).registerReceiver(myBroadcastReceiver , mStatusIntentFilter);

非常奇怪的解决方法,也许这是一个稍后会修复的错误。

答案 10 :(得分:0)

我已使用静态值解决了此问题。

请在下面找到我的代码。

我的活动

private static Snackbar mSnackbar;
private NetworkChangeReceiver mNetworkChangeReceiver;

@Override
protected void onResume() {
    super.onResume();
    registerReceivers();
}
@Override
protected void onPause() {
    super.onPause();
    unRegisterReceivers();
}
private void registerReceivers() {
    mNetworkChangeReceiver = new NetworkChangeReceiver() {
        @Override
        protected void onNetworkChange(boolean status) {
            if (!status) {
                if (mSnackbar == null) {
                    mSnackbar = Snackbar
                            .make(drawer, R.string.no_internet, Snackbar.LENGTH_INDEFINITE)
                            .setActionTextColor(Color.YELLOW)
                            .setAction(R.string.ok, new View.OnClickListener() {
                                @Override
                                public void onClick(View view) {
                                }
                            });
                    mSnackbar.show();
                }
            } else {
                if (mSnackbar != null) {
                    mSnackbar.dismiss();
                    mSnackbar = null;
                }
            }
        }
    };
    IntentFilter nwStateChangeFilter = new IntentFilter();
    nwStateChangeFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
    nwStateChangeFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    registerReceiver(mNetworkChangeReceiver, nwStateChangeFilter);
}
private void unRegisterReceivers() {
    unregisterReceiver(mNetworkChangeReceiver);
}

<强> NetworkChangeReceiver.Class

public abstract class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, final Intent intent) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
        if (activeNetwork != null) { // connected to the internet
                if (activeNetwork.getType() == ConnectivityManager.TYPE_WIFI){
                    // connected to wifi
                    onNetworkChange(true);
                } else if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
                    // connected to the mobile provider's data plan
                    onNetworkChange(true);
                } else {
                    // not connected to the internet
                    onNetworkChange(false);
                }
        } else {
            // not connected to the internet
            onNetworkChange(false);
        }
    }
    protected abstract void onNetworkChange(boolean status);
}

答案 11 :(得分:0)

我觉得最好在覆盖BroadcastReceiver之后和启动想要接收数据的意图之前注册接收器。(否则它可能会抛出NullPointerException。<登记/> 取消注册onStop()而不是onPause()中的接收者。

我注意到,如果您要执行服务或后台任务,取消注册onPause()中的接收器会产生问题,这将暂停活动,从而取消注册您的广播接收器。 (这将导致多次调用onRecieve()方法,即每次重新注册接收者时。)

答案 12 :(得分:0)

在我的情况下,我用[BroadcastReceiver(Enabled = true)]装饰广播课程,同时在OnResume注册。我通过评论//[BroadcastReceiver(Enabled = true)]

来修复它

答案 13 :(得分:0)

如果您从自定义应用程序类注册广播接收器,则可以避免出现此问题。这将确保您的广播接收器仅在整个应用程序生命周期内注册一次。

答案 14 :(得分:0)

我已找到此问题的原因。

基本上,当我们为BLUETOOTH_STATE_CHANGE注册接收器时,如果我们打开蓝牙,则其onReceive()方法将为以下两个蓝牙状态调用两次:

状态1:STATE_TURNING_ON 表示本地蓝牙适配器已打开。

状态2:STATE_ON 表示本地蓝牙适配器已打开并且可以使用。

关闭蓝牙相同,然后其onReceive()方法将再次为以下两个蓝牙状态调用两次:

状态1:STATE_TURNING_OFF 表示本地蓝牙适配器已关闭。

状态2:STATE_OFF 表示本地蓝牙适配器已关闭。

因此,基本上,我们可以通过以下方式处理这种情况:

@Override
public void onReceive(Context context, Intent intent) {
    final String action = intent.getAction();
    if (action != null && action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
        if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_ON) {

            // When Bluetooth adapter is on, and ready for use
            //DO YOUR CODE HERE FOR "BLUETOOTH ON" CASE


        }
        else if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) {

            //When local Bluetooth adapter is off
            //DO YOUR CODE HERE FOR "BLUETOOTH OFF" CASE

        }

    }
} 

查看您的代码(@girlOnSledge)后,似乎您可以按照上述步骤进行操作,那么在这种情况下,您的代码也可以正常工作。