BroadcastReceiver 为一个事件接收多个相同的消息

Posted

技术标签:

【中文标题】BroadcastReceiver 为一个事件接收多个相同的消息【英文标题】:BroadcastReceiver receives multiple identical messages for one event 【发布时间】:2012-01-14 19:27:29 【问题描述】:

我注册了一个监听网络事件的接收器:

<receiver 
    android:label="NetworkConnection"
    android:name=".ConnectionChangeReceiver" >
    <intent-filter >
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
</receiver>

接收器也很简单:

public class ConnectionChangeReceiver extends BroadcastReceiver 
    @Override
    public void onReceive(Context context, Intent intent) 
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) 
                Log.v("@@@","Receiver : " + activeNetInfo);
         else 
            Log.v("@@@","Receiver : " + "No network");
        
    

问题是,当 Wifi 连接时,我连续收到 3 条相同的消息,如下所示:

Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true
Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true
Receiver : NetworkInfo: type: WIFI[], state: CONNECTED/CONNECTED, reason: (unspecified), extra: (none), roaming: false, failover: false, isAvailable: true

它们都是“CONNECTED/CONNECTED”(不应该是CONNECTING/OBTAINING_IPADDR等),所以问题是如何判断它何时真正连接?当wifi实际连接时,我有一些我想做的例程,我不希望它们被连续调用三次。

PS:3G只发送一条消息,所以这里没有问题。

更新:

似乎是特定于设备的问题。

为了测试,我拿了 2 部 Desire HD 和 4 部随机的安卓手机(不同的 Aquos 型号和一些无名中文的东西)。在 DHD 和 wifi 连接上的一部随机手机上,我收到 3 条消息,在其余手机上,我只收到一条消息。见鬼。

【问题讨论】:

日志之间的时间间隔是多少?你能看到事件发生时的 currentTimeMillis() 吗?我想知道它们是否发生得如此紧密,以至于 getActiveNetworkInfo() 实际上返回了同一个对象(一个静态对象),该对象被记录了 3 次。 17:51:50.023 17:51:50.414 17:51:50.617 我拿了你的代码并在我的手机(三星 Galaxy S2)上试了一下。它正在提供与移动网络和 wifi 相关的消息。当我关闭数据包数据时,每次打开或关闭 wifi 时我只会收到一条消息。奇怪的是,你的消息都一样,而我的却都不同。 嗯,看到了..我会尝试只用这个接收器制作干净的项目,看看会发生什么。 哇,是的,我是设备问题。我更新了我的问题。 【参考方案1】:

接收多个广播是设备特定的问题。有些手机只发送一个广播,而其他手机发送 2 或 3 个。但有一种解决方法:

假设您在 wifi 断开连接时收到断开消息,我猜第一个是正确的,而另外两个只是出于某种原因的回声。

要知道消息已被调用,您可以有一个静态布尔值,在连接和断开之间切换,并且仅在您收到连接并且布尔值为真时才调用您的子例程。比如:

public class ConnectionChangeReceiver extends BroadcastReceiver 
    private static boolean firstConnect = true;

    @Override
    public void onReceive(Context context, Intent intent) 
        final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        final NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) 
            if(firstConnect)  
                // do subroutines here
                firstConnect = false;
            
        
        else 
            firstConnect= true;
        
    

【讨论】:

谢谢!这是一个非常好的和简单的想法。您需要注意两件事。首先 - 将“firstConnect”存储在某处,例如在共享首选项中,其次是当您从 3G 更改为 WiFi 时,没有实际断开连接,因此最好分别处理 3G 和 WiFi 事件。 如何从这个类中调用 Activity 方法?它不是由活动发起的,因此我无法向此类发送上下文。 使用这个技巧开发了一个应用程序。一团糟。但是“特定于设备的问题”只是针对网络广播,还是任意广播会被重复? 谢谢@Sver。这很有帮助。【参考方案2】:

您还可以在静态字段中缓存最后处理的连接类型并检查传入的广播。这样一来,每种连接类型只能获得一个广播。

当连接类型发生变化时,它显然会起作用。当设备断开连接时,activeNetworkInfo 将为空,currentType 将默认为NO_CONNECTION_TYPE

public class ConnectivityReceiver extends BroadcastReceiver 

    /** The absence of a connection type. */
    private static final int NO_CONNECTION_TYPE = -1;

    /** The last processed network type. */
    private static int sLastType = NO_CONNECTION_TYPE;

    @Override
    public void onReceive(Context context, Intent intent) 
        ConnectivityManager connectivityManager = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);

        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
        final int currentType = activeNetworkInfo != null
                ? activeNetworkInfo.getType() : NO_CONNECTION_TYPE;

        // Avoid handling multiple broadcasts for the same connection type
        if (sLastType != currentType) 
            if (activeNetworkInfo != null) 
                boolean isConnectedOrConnecting = activeNetworkInfo.isConnectedOrConnecting();
                boolean isWiFi = ConnectivityManager.TYPE_WIFI == currentType;
                boolean isMobile = ConnectivityManager.TYPE_MOBILE == currentType;

                // TODO Connected. Do your stuff!
             else 
                // TODO Disconnected. Do your stuff!
            

            sLastType = currentType;
        

【讨论】:

【参考方案3】:

在我的例子中,我在 onResume 中注册了我的 BroadcastReceivers,并且只在 onDestroy. 中取消注册它们

这导致每个广播接收器注册 3 或 4 次,具体取决于活动恢复的次数。

根据活动生命周期将广播接收器设置在正确的位置将使您不再接到多个令人困惑的呼叫。

【讨论】:

是的,这也是我的情况!【参考方案4】:

oncreate() 中注册您的LocalBroadcastreceiver,而不是在onResume() 中。在onDestroy未注册

【讨论】:

【参考方案5】:

我有一个应用程序可以在用户重新联机时上传数据。由于我的广播接收器可以多次接收意图,因此可能导致数据被多次上传。为了处理这个问题,我使用了一个服务,如果它已经在运行,它不会做任何事情。

广播接收器:

public class ConnectionChangeReceiver extends BroadcastReceiver 
    private static boolean firstConnect = true;

    @Override
    public void onReceive(Context context, Intent intent) 
        final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        final NetworkInfo activeNetInfo = connectivityManager.getActiveNetworkInfo();
        if (activeNetInfo != null) 
            startService();
           
    

服务:

public class MyService extends Service 
    private boolean mRunning;

    @Override
    public void onCreate() 
        super.onCreate();
        mRunning = false;
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) 
        if (!mRunning) 
            mRunning = true;
            uploadTheData();
        
        return super.onStartCommand(intent, flags, startId);
    

【讨论】:

【参考方案6】:

我对 Aleksander 提出的方法的担忧是它没有考虑相同类型的网络更改,例如从一个 WiFi 网络到另一个。

我建议比较活动网络的 extraInfo,其中包含网络名称,例如WiFi SSID 或移动网络名称,如 VZW

 String currentNetworkName = "";

 ConnectivityManager connectivityManager =
            ((ConnectivityManager) context.getSystemService(
                    Context.CONNECTIVITY_SERVICE));

    NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
    boolean connected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
    if (connected) 
        // prevent duplicate connect broadcasts
        String extraInfo = activeNetwork.getExtraInfo();
        if(! currentNetworkName.equals(extraInfo)) 
            // to do: handle network changes
            currentNetworkName = extraInfo;
        
     else 
        Log.d(TAG, "is not connected");
        isConnected = false;
        currentNetworkName = "";
    

【讨论】:

以上是关于BroadcastReceiver 为一个事件接收多个相同的消息的主要内容,如果未能解决你的问题,请参考以下文章

BroadcastReceiver

Android BroadcastReceiver

自定义广播(BroadcastReceiver)事件 --Android开发

Android 基础知识 -- BroadcastReceiver

BroadCastReceiver

BroadcastReceiver和EventBus区别是什么