android - 按下后退按钮后如何解决 IntentReceiverLeaked

标签 android android-activity memory-leaks wifi-direct

我在使用 android 6 时遇到 WifiDirect 连接问题。直接连接 WIFI 并将后退按钮推到我的应用程序后。该应用程序泄漏了 intentreceiver

public class WifiDirectController
        extends ServerController
        implements WifiP2pManager.ConnectionInfoListener,
                   WifiP2pManager.ChannelListener,
                   WiFiDirectBroadcastReceiver.WiFiDirectBroadcastListener,
                   WifiDirectServer.WifiDirectServerListener {
  private static final String TAG = "WP2P.Controller";

  private WifiP2pManager mManager;
  private WifiP2pManager.Channel mChannel;
  private WifiP2pDevice mDevice;

  private BroadcastReceiver mReceiver;

  private boolean isWifiP2pEnabled = false;
  private boolean isDiscovering = false;
  private boolean isConnected = false;
  private boolean retryChannel = false;
  //added by Frank
  private boolean isReceiverRegistered = false;

  private final IntentFilter mIntentFilter = new IntentFilter();

  private Handler mHandler;

  private WifiDirectServer serverThread = null;

  public WifiDirectController(ServerListener listener) {
    super(listener);

    //mManager = (WifiP2pManager) mListener.getSystemService(Context.WIFI_P2P_SERVICE);
    mManager = (WifiP2pManager) mListener.getApplicationContext().getSystemService(Context.WIFI_P2P_SERVICE);
    mChannel = mManager.initialize(listener.getApplicationContext(), mListener.getMainLooper(), this);

    //  Indicates a change in the Wi-Fi P2P status.
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
    // Indicates the state of Wi-Fi P2P connectivity has changed.
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
    // Indicates this device's details have changed.
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
    // Indicates that discovery has started or stopped
    mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);

    mHandler = new Handler();
  }

  @Override
  public void onStart()
  {
    Log.d(TAG, "onStart() called");

  }

  @Override
  public void onResume() {
    Log.d(TAG, "onResume() called");
    Log.e(TAG, "FRANK onResume() called");
    mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
    if (!isReceiverRegistered) {
      mListener.registerReceiver(mReceiver, mIntentFilter);
      isReceiverRegistered = true;
    }
  }

  @Override
  public void onPause()
  {
    Log.d(TAG, "onPause() called");
    mListener.unregisterReceiver(mReceiver);
    isReceiverRegistered = false;
    stopDiscovery();
  }

  @Override
  public void onStop()
  {
    Log.d(TAG, "onStop() called");
    //added frank
    if (isReceiverRegistered) {
        mListener.unregisterReceiver(mReceiver);
        isReceiverRegistered = false;
    }
    //
    cancelDisconnect();
    stopServer();
  }

  @Override
  public void restart()
  {
    Log.d(TAG, "restart() called");
    Log.e(TAG, "FRANK onRestart() called");
    cancelDisconnect();
    stopServer();
    startDiscovery();
  }

  private boolean startServer()
  {
    Log.d(TAG, " = startServer() called");
    Log.e(TAG, "FRANK startServer() called");
    this.serverThread = new WifiDirectServer(this);
    this.serverThread.start();
    return true;
  }

ActivityThread:Activity com.android.settings.SubSettings 泄露了最初在此处注册的 IntentReceiver android.net.wifi.WifiManager$1@61ae52a。您是否错过了对 unregisterReceiver() 的调用? android.app.IntentReceiverLeaked:Activity com.android.settings.SubSettings 泄露了最初在此处注册的 IntentReceiver android.net.wifi.WifiManager$1@61ae52a。您是否错过了对 unregisterReceiver() 的调用?

最佳答案

您必须使用一种方法来检查接收器是否已注册,这是因为当您按下后退按钮时, Activity 被销毁,因此变量返回到其原始值,因此在 Resume 方法中变量 isReceiverRegistered始终为 false,并尝试再次注册接收者。

  • 您可以在 OnPause 方法中取消注册接收者,而不是 OnStop 方法。
  • 或者你可以存储覆盖变量的值 onSaveInstanceState 方法和 onRestoreInstanceState 中的 recoverit 方法。
  • 或者您可以使用 ViewModel 来保存变量的值。
  • 或者您可以尝试使用一个类来管理您的接收器。 slinden77 在这个 thread 中给出了一个可能的解决方案
    public class ReceiverManager {

        private static List<BroadcastReceiver> receivers = new ArrayList<BroadcastReceiver>();  
        private static ReceiverManager ref;
        private Context context;

        private ReceiverManager(Context context){
            this.context = context;
        }

        public static synchronized ReceiverManager init(Context context) {      
            if (ref == null) ref = new ReceiverManager(context);
            return ref;
        }

        public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter intentFilter){
            receivers.add(receiver);
            Intent intent = context.registerReceiver(receiver, intentFilter);
            Log.i(getClass().getSimpleName(), "registered receiver: "+receiver+"  with filter: "+intentFilter);
            Log.i(getClass().getSimpleName(), "receiver Intent: "+intent);
            return intent;
        }

        public boolean isReceiverRegistered(BroadcastReceiver receiver){
            boolean registered = receivers.contains(receiver);
            Log.i(getClass().getSimpleName(), "is receiver "+receiver+" registered? "+registered);
            return registered;
        }

        public void unregisterReceiver(BroadcastReceiver receiver){
            if (isReceiverRegistered(receiver)){
                receivers.remove(receiver);
                context.unregisterReceiver(receiver);
                Log.i(getClass().getSimpleName(), "unregistered receiver: "+receiver);
            }
        }
    }

希望对你有帮助

关于android - 按下后退按钮后如何解决 IntentReceiverLeaked,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57397993/

相关文章:

android - Android 中强制关闭

java - 为什么 onAttach 不能识别正确的 instanceof 上下文?

java - 如何在 ScreenSlidePagerAdapter 中启动新 Activity ?

r - 使用 RDCOMClient 打开数据帧时内存泄漏

javascript - 没有 Hash 或 History 的 Ionic 框架

java - 存储 arraylist sharedpreferences android

javascript - javascript中的内存泄漏,循环内的函数

ios - 使用 WKScriptMessageHandler 时内存泄漏

android - 是否有适用于 Android 的精简版 SVG 查看器?

java - 如何修复 ListView 中的 NullPointerException?