web-dev-qa-db-ja.com

Android BroadcastReceiver onReceive()が2回呼び出されるAndroid 4.0

私はAndroid 4.0.3(4.1.2では正常に動作します)。 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);
}
25
girlOnSledge

簡単な答え:違いはありません。両方のBroadcastReceiverクラスは、Android 2.3.2 r1からです。

同様の問題がありましたが、私にとってはAndroid 2.3.5のHTC Desire HDでした-GCMからの通知は常に2回受信されます。私は管理しませんでしたサーバーで各通知に一意の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*);
    }
}

追加のデータを保存する必要がない場合は、Mapの代わりにHashSetを使用して、IDが含まれているかどうかを確認するだけです。

9
npace

通常、人々は2つの場所でブロードキャストを登録しているため、onReceiveが2回呼び出されていますほとんどの場合、onCreateとonResumeに登録しています。(登録するスポットを1つ選択してください)。

おそらく何十回も行ったかもしれませんが、 Activity Life Cycle を一目見直すことを常にお勧めします。

39
Mercury

同じ問題がありました。

onReceive()は、ブロードキャストレシーバーを2回登録していたため、2回呼び出されます。 1つはアクティビティのonCreate()に、もう1つはmanifestにあります。

onCreate()からブロードキャストレシーバーの登録を削除すると、正常に機能します。そのうちの1つだけに放送受信機を登録します。

これを行うには2つの方法があります。

  • マニフェストファイル内で静的に。
  • コード内で動的に。

どの方法(静的または動的)をいつ使用するかは、何をしようとしているかに完全に依存します。基本的に、システム全体のイベントや他のアプリから送信されたイベントを聞いてステータスバーに通知またはインジケーターを表示することにより、画面(ホーム画面、ランチャー、ステータスバーなど)でいくつかの変更を行いたい場合、静的に登録されたブロードキャストレシーバーを使用することは理にかなっています。同様のイベントに基づいて、ユーザーがアプリを使用しているとき、またはバックグラウンドに配置しているときにアプリで変更を行いたい場合は、動的に登録されたレシーバーを使用するのが理にかなっています。

詳細情報: http://codetheory.in/Android-broadcast-receivers/

21
Sushant

onStart()BroadcastReciverを初期化します。これで問題が解決します。

8
SINIGAMI

onReceive() functionを入力するときに、ブールフラグを設定して実装することもできます。

1
varghesekutty

Service classsendBroadcast(intent)を複数回呼び出していました

それらの1つを削除する問題を修正しました。

1
ymerdrengene

これが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

質問がある場合はお知らせください。

1
Subho

この問題の理由を見つけました。

基本的に、BLUETOOTH_STATE_CHANGEのレシーバーを登録するとき、Turning ON bluetoothの場合、onReceive()メソッドは次の2つのBluetooth状態に対して2回呼び出します。

状態1:STATE_TURNING_ONローカルBluetoothアダプターがオンになっていることを示します。

状態2:STATE_ONローカルBluetoothアダプターがオンで、使用できる状態であることを示します。

同じ間Turing OFF bluetoothの場合、onReceive()メソッドは次の2つのBluetooth状態に対して再度2回呼び出します。

状態1:STATE_TURNING_OFFローカルBluetoothアダプターがオフになっていることを示します。

状態2:STATE_OFFローカルBluetoothアダプターがオフであることを示します。

したがって、基本的にこの状況は次の方法で処理できます。

@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)を確認した後、上記の手順に従うと、その場合でもコードは正常に機能するようです。

0
ayush kaushik

私の場合、ブロードキャストクラスを[BroadcastReceiver(Enabled = true)]で装飾すると同時に、OnResumeに登録しました。 //[BroadcastReceiver(Enabled = true)]をコメントアウトして修正しました

0
Donald O

この問題が発生したばかりで、既存の回答のいずれにも記載されていない解決策を見つけたので、共有したいと思います。

BroadcastReceiverをonCreateに一度だけ登録します

ただし、次のように登録していました。

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

つまり、構築中registerReceiver呼び出し内のboradcastReceiverインスタンスでした。

ただし、BroadcastReceiverを個別のステートメントで構築すると、問題は解決しました。このような:

MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();

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

非常に奇妙な回避策、多分それは後で修正されるバグです。

0
William Kinaan

BroadcastReceiverをオーバーライドした後、データを受け取りたいインテントを開始する前に、レシーバーを登録するのが最善だと思います(そうでなければ、NullPointerExceptionをスローするかもしれません。
_onStop()ではなくonPause()で受信者の登録を解除します。

onPause()でレシーバーを登録解除すると、実行するサービスまたはバックグラウンドタスクがある場合に問題が発生し、アクティビティが一時停止し、ブロードキャストレシーバーの登録が解除されることに気付きました。 (これにより、onRecieve()メソッドが複数回呼び出されます。つまり、受信者が再登録されるたびになります。)

0

カスタムアプリケーションクラスからブロードキャストレシーバーを登録すると、この問題の発生を回避できます。これにより、アプリケーションのライフサイクル全体でブロードキャストレシーバーが1回だけ登録されるようになります。

0
Sameer Ranjan

私は同様の問題に直面しましたが、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);
}
0
E_X

static値を使用してこの問題を修正しました。

以下に私のコードを見つけてください。

私の活動

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