web-dev-qa-db-ja.com

BroadcastReceiverが1つのイベントに対して複数の同一のメッセージを受信する

ネットワークイベントをリッスンするレシーバーを登録しました。

<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

それらはすべて「接続済み/接続済み」です(それらはCONNECTING/OBTAINING_IPADDRなどのようなものではありません)。したがって、問題は、実際に接続されていることをどのように確認できるかです。 wifiが実際に接続されているときに作成したいルーチンがいくつかあり、それらを3回続けて呼び出さないでください。

PS:3Gはメッセージを1つだけ送信するため、ここでは問題ありません。

更新:

デバイス固有の問題のようです。

テストでは、2つの欲望HDと4つのランダムAndroid電話(さまざまなAquosモデルといくつかの名前のない中国語のもの)を使用しました。DHDとwifi接続の1つのランダム電話の両方で、3つのメッセージを受け取りました。残りの電話では、メッセージは1つしかありませんでした。

33
Sver

複数のブロードキャストの受信はデバイス固有の問題です。一部の電話は1つのブロードキャストを送信し、他の電話は2または3を送信します。しかし、回避策があります:

Wifiが切断されたときに切断メッセージが表示されると仮定すると、最初の1つが正しいものであり、他の2つは何らかの理由でエコーしていると思います。

メッセージが呼び出されたことを知るには、接続と切断を切り替える静的なブール値を使用し、接続を受信して​​ブール値がtrueの場合にのみサブルーチンを呼び出すことができます。何かのようなもの:

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;
        }
    }
}
56
brianestey

最後に処理された接続タイプを静的フィールドにキャッシュして、着信ブロードキャストをチェックすることもできます。この方法では、接続タイプごとに1つのブロードキャストのみが取得されます。

接続タイプが変更されると、明らかに機能します。デフォルトの場合と同様に、デバイスが接続から外れると、activeNetworkInfoはnullになり、currentTypeNO_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;
        }
}
9
Aleksandar Ilic

私の場合、BroadcastReceiversをonResumeに登録し、onDestroy.でのみ登録解除していました

これにより、アクティビティが再開する回数に応じて、各ブロードキャストレシーバーが3または4回登録されました。

ブロードキャストレシーバーをアクティビティライフサイクルの適切な場所に設定すると、混乱を招く複数の呼び出しの発生を防ぐことができます。

3
Simon

LocalBroadcastreceiverをoncreate()ではなくonResume()自体に登録します。 onDestroyで未登録

1
Sreenivasulu Y

Aleksanderによって提案されたアプローチに関する私の懸念は、同じタイプのネットワーク変更を考慮しないことです。あるWiFiネットワークから別の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 = "";
    }
0
checkmate711

ユーザーがオンラインに戻ったときにデータをアップロードするアプリケーションがあります。私の放送受信機はインテントを複数回受信できるため、データが複数回アップロードされる可能性があります。これを処理するために、すでに実行されている場合は何もしないサービスを使用します。

放送受信機:

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);
    }
}
0
John