簡體   English   中英

Android BroadcastReceiver onReceive() 在 android 4.0 上調用了兩次

[英]Android BroadcastReceiver onReceive() called twice on android 4.0

我在 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);
}

通常 onReceive 會被調用兩次,因為人們在2 個位置注冊廣播,很可能您正在 onCreate 和 onResume 中注冊。 (選擇一處報名)。

盡管您可能已經完成了十幾次 - 始終建議您再看一眼Activity Life Cycle

我有同樣的問題。

onReceive()被調用兩次,因為我注冊了兩次廣播接收器。 一個在我的活動的onCreate() ,另一個在manifest

我從onCreate()刪除了廣播接收器注冊,它工作正常。 僅在其中之一中注冊您的廣播接收器。

有兩種方法可以做到這一點:

  • 靜態地在清單文件中。
  • 在代碼中動態地。

何時使用哪種方法(靜態或動態)完全取決於您要做什么。 基本上,當您想在屏幕(主屏幕、啟動器、狀態欄等)上進行一些更改時,通過偵聽系統范圍的事件或其他應用程序發送的事件,在狀態欄中顯示一些通知或某些指示器,那么使用靜態注冊的廣播接收器是有意義的。 而基於類似的事件,您希望在用戶使用應用程序或將其置於后台時直接在應用程序中進行更改,然后使用動態注冊的接收器是有意義的,該接收器將持續到注冊組件被銷毀。

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

簡短的回答:沒有區別。 兩者的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。

onStart()上初始化BroadcastReciver 這解決了我的問題。

對於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)
    {
    }
}

我刪除了屬性,問題解決了!

您也可以通過設置布爾標志來實現它,當您進入onReceive()函數時

我相信這是確定 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");


        }

    }

    }

此代碼僅在您的應用處於 onresume 狀態且處於 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

如果您有任何疑問,請告訴我。

我在Service 類中多次調用sendBroadcast(intent)

刪除其中之一解決了問題。

我遇到了類似的問題,但是我的BroadcastReceiver是靜態的,因為我想將它用作內部類,我所做的是注冊接收器(為了創建靜態實例),然后取消注冊相同的接收器,這使它成為由AlarmManager計時器調用一次, 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);
}

我剛剛遇到了這個問題,我找到了一個在任何現有答案中都沒有提到的解決方案,所以我想分享一下。

我只在onCreate 中注冊了 BroadcastReceiver

但是,我是這樣注冊的:

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

換句話說,我在registerReceiver調用中構建了 boradcastReceiver 實例。

但是當我在單獨的語句中構造 BroadcastReceiver 時,問題就解決了。 像這樣:

MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();

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

很奇怪的解決方法,也許這是一個稍后會修復的錯誤。

我通過使用靜態值解決了這個問題。

請在下面找到我的代碼。

我的活動

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

我覺得最好在覆蓋BroadcastReceiver和開始要從中接收數據的意圖之前注冊接收器。(否則它可能會拋出NullPointerException
onStop()而不是onPause()取消注冊接收器。

我注意到,如果您有要執行的服務或后台任務,在onPause()中取消注冊接收器會產生問題,這將暫停活動並因此取消注冊您的廣播接收器。 (這將導致多次調用onRecieve()方法,即每次重新注冊接收器時。)

在我的例子中,我用[BroadcastReceiver(Enabled = true)]裝飾了廣播類,同時在OnResume注冊了它。 我通過注釋掉//[BroadcastReceiver(Enabled = true)]來修復它

如果您從自定義 Application 類注冊廣播接收器,則可以避免出現此問題。 這將確保您的廣播接收器在整個應用程序生命周期內只注冊一次。

我已經找到了這個問題的原因。

基本上,當我們為 BLUETOOTH_STATE_CHANGE 注冊接收器時,如果我們打開藍牙,那么它的 onReceive() 方法將針對以下兩個藍牙狀態調用兩次:

STATE 1: STATE_TURNING_ON 表示本地藍牙適配器正在開啟。

STATE 2: STATE_ON 表示本地藍牙適配器已開啟,可以使用。

關閉藍牙時相同,然后它的 onReceive() 方法將再次為以下兩個藍牙狀態調用兩次:

STATE 1: STATE_TURNING_OFF 表示本地藍牙適配器正在關閉。

STATE 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)后,似乎如果您遵循上述過程,您的代碼在這種情況下也能正常工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM