为什么BroadcastReceiver即使应用程序处于后台也能正常工作?

时间:2012-09-05 05:30:45

标签: android broadcastreceiver manifest

我正在使用BroadcastReceiver检查我的应用程序中的Internet连接,如果连接丢失,我会显示一个警告对话框。它工作正常。但我的问题是,即使我的应用程序在背景幕中,BroadcastReceiver也能正常工作。因此,即使用户在其他应用程序中,当互联网连接丢失时,对话框也会弹出。这完全破坏了我的应用程序。

有没有人知道如何限制应用中的广播接收器?

这是我的BroadcastReceiver:

 public class ConnectivityChangedReceiver extends BroadcastReceiver{

    @Override
    public void onReceive( Context context, Intent intent )
     {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE );
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {
    } else {
        try{
            Intent i=new Intent(context, InternetDialogActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(i);
        } catch(Exception e){
            e.printStackTrace();
        }
    }
  }
}

活动是:

 public class InternetDialogActivity extends Activity implements OnClickListener{
   @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.internet_dialog_box);
      getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
      Button retryButton = (Button) findViewById(R.id.retryInternetButton);
      retryButton.setOnClickListener(this);
    }

   public boolean checkConnectivity(){
       ConnectivityManager connectivityManager   = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
           NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

       if (networkInfo!=null && networkInfo.isConnected()) {
           finish();
           return true;
       } else {
           Intent intent = getIntent();
           finish();
           startActivity(intent);
           return false; 
      }
   }

   @Override
   public void onClick(View view) {
      switch(view.getId()){
         case R.id.retryInternetButton:
             try{
                 checkConnectivity();
             } catch(Exception e){
                 e.printStackTrace();
             }
             break;
       }
   }
   }

以下是我在清单中声明接收器和活动的方式:

  <receiver android:name="com.lisnx.service.ConnectivityChangedReceiver"
        android:label="NetworkConnection">
        <intent-filter>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
        </intent-filter>
  </receiver>

  <activity android:name="com.lisnx.activity.InternetDialogActivity"
        android:configChanges="orientation|keyboardHidden"
        android:theme="@android:style/Theme.Dialog" />

我已经读过,我们可以通过不在清单中声明它来限制BroadcastReceiver在应用程序内工作。但我不知道接收器将如何工作呢?请帮我。我被严重困扰了。提前完成。

9 个答案:

答案 0 :(得分:37)

当应用程序处于后台时,BroadcastReceiver可以正常工作,因为接收方接收的事件是全局发送的,并且每个应用程序都已注册以监听这些应用程序,无论它是否正在运行。< / p>

要处理此问题,请在BroadcastReceiver的{​​{1}}代码中检查您的应用是否位于前台。

我知道有一个 - 也是唯一一个 - 持续有效的方法来做到这一点。您需要跟踪应用程序的暂停/恢复操作。请确保在每个活动中进行检查。

有一些示例代码in this answer(解决方案#1)。在您的情况下,您需要先检查onReceive作为验证,然后再从MyApplication.isActivityVisible() == true进行操作。

答案 1 :(得分:17)

您是否尝试从清单中删除Intent过滤器并在活动中注册/取消注册?因此,您可以尝试在onStart()中注册Intent过滤器,并在onStop()方法上注销它。代码有点像这样:

static final String ACTION = "android.net.conn.CONNECTIVITY_CHANGE";


IntentFilter filter = new IntentFilter(ACTION);
this.registerReceiver(ConnectivityChangedReceiver, filter);


unregisterReceiver(ConnectivityChangedReceiver);

如果还不熟悉,您还应该了解Activity Lifecycle

答案 2 :(得分:13)

您应该在每项活动的BroadcastReceiveronPause()中注册/取消注册onResume()。然后你知道当你的应用程序在前台时,接收器只是“监听”。您可以通过创建自己的BaseActivity来扩展Activity并覆盖onPause()onResume()并注册/取消注册您的接收者,从而轻松实现这一目标。只需将您的所有活动延长BaseActivity,并让他们像往常一样致电super.onResume()super.onPause()。这是示例代码:

public class BaseActivity extends Activity {
    // Create an IntentFilter to listen for connectivity change events
    static IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
    // Create an instance of our BroadcastReceiver
    static ConnectivityChangedReceiver receiver = new ConnectivityChangedReceiver();

    @Override
    protected void onPause() {
        super.onPause();
        // Stop listening for connectivity change events
        unregisterReceiver(receiver);
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Listen for connectivity change events
        registerReceiver(receiver, filter);
    }
}

您的所有活动都应延长BaseActivity,如果他们需要在onResume()onPause()中执行任何特殊操作,请务必直接致电super.onXXXXX()

public MyActivity extends BaseActivity {
    @Override
    protected void onResume() {
        super.onResume();
        // Here you do whatever you need to do in onResume() of your activity
        ...
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Here you do whatever you need to do in onPause() of your activity
        ...
    }
}

我没有通过编译器运行代码,所以如果有错字或我错过了什么,我道歉。

答案 3 :(得分:4)

我认为你必须确保在应用程序处于后台时你没有使用接收器。为此,您必须使用onPause()和onResume()方法的每个活动。

答案 4 :(得分:3)

据我所知,如果在manifest.xml中注册了Broadcast接收器,那么只要应用程序存在,就会存在广播接收器。此外,在UI线程上调用动态注册的接收器(即,以编程方式注册您的BroadcastReceiver)。这意味着您的接收器会阻止任何UI处理,因此onReceive()方法应该尽可能快。

但是,我将尝试讨论有关广播接收器的信息。但是,首先应该知道一些信息。首先,广播接收器是一个独立的应用程序组件,这意味着即使其他应用程序组件未运行,它也将继续运行。这就是为什么我们在onPause上取消注册广播接收器的活动。此外,开发人员应在Activity.onResume()实施中注册此内容。

其次,开发人员不应在Activity.onSaveInstanceState()中取消注册,因为如果用户在历史堆栈中移回,则不会调用此方法。我已经从BroadcastReceiver documentation中提取了这些信息。

另一点是BroadcastReceiver对象仅在对onReceive()的调用期间有效。一旦onReceive()方法完成,您的BroadcastReceiver就会终止。

现在,如何以编程方式注册接收器:

public abstract Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter)

此处,当任何广播意图与过滤器匹配时,将调用BroadcastReceiver- receiver         并且IntentFilter- Intent指定接收者应该听哪个事件。

寄存器:

YourConnectionListener receiver;
this.reciever = new YourConnectionListener();
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(this.reciever, filter);

发送了您的广播信息:

Intent intent = new Intent();
intent.putExtra("Message", "Your connectivity info has Changed!!"); 
this.sendBroadcast(intent);

接收器:

现在,需要接收广播。每当事件发生时,Android就会在所有已注册的广播接收器上调用onReceive()方法。假设您希望在连接发生更改时收到通知。

public class YourConnectionListener extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent){
         // your Code
    }
}   

onReceive()有两个参数:

  • context:您可以用来访问其他信息或启动服务或活动的Context对象。
  • intent:用于注册接收器的意图。此对象包含可在实现中使用的其他信息。
    此外,开发人员应避免在BroadcastReceiver中执行任何持久的任务。因此,在静态和动态注册的接收器中,Developer应该在接收器本身中执行小任务。对于任何更长的任务,您应该从接收器内启动服务。

答案 5 :(得分:3)

要制作仅在应用运行时启动的广播接收器,请按照以下代码进行操作。

1. :像这样创建广播接收器:


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class InternetStatusNotifier extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        //Recieve notification here

    }

}

2。制作一个活动或片段,让广播接收器像这样工作:


import android.app.Activity;
import android.content.IntentFilter;
import android.os.Bundle;

public class MainActivity extends Activity {
    private InternetStatusNotifier mInternetStatusNotifier;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mInternetStatusNotifier = new InternetStatusNotifier();
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onResume() {
        registerReceiver(mInternetStatusNotifier, new IntentFilter(
                "android.net.conn.CONNECTIVITY_CHANGE"));
        super.onResume();
    }

    @Override
    protected void onPause() {
        unregisterReceiver(mInternetStatusNotifier);
        super.onPause();
    }

注意:这就是您以屏幕特定方式使用广播接收器的方式。只有屏幕显示才会以这种方式接收广播。当您使用清单文件注册广播时,它们甚至会在应用关闭时收到

答案 6 :(得分:1)

这就是广播接收器在Android中的工作方式。如果您在清单中注册广播并且您的应用未运行,Android将启动新流程来处理广播。从广播接收器直接显示UI通常是一个坏主意,因为这可能会中断其他应用程序。我也不相信一个普遍的连接会丢失&#39;对话框也是一个好主意。这可能应该由使用网络访问的每个活动处理。

至于原始问题,您需要在活动进入后台(onPause()等)时禁用接收器,并在到达前台时启用它(onResume()等) 。将enabled=false放入清单中,然后在代码中使用类似的内容根据需要进行切换:

   public static void toggle(Context context, boolean enable) {
        int flag = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
        ComponentName receiver = new ComponentName(context,
                ConnectivityMonitor.class);

        context.getPackageManager().setComponentEnabledSetting(receiver, flag,
                PackageManager.DONT_KILL_APP);
   }

答案 7 :(得分:0)

查找应用程序是否在前台的简单方法

if((mContext.getPackageName().equalsIgnoreCase(
                ((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE))
                        .getRunningTasks(1).get(0).topActivity.getPackageName())))
{
//app is in foreground;
}

答案 8 :(得分:0)

我建议你在有人打开它的时候检查应用程序中的互联网设置,这是我如何做的代码。

public static boolean isNetworkConnected(Context ctx) {  
        ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);  
        NetworkInfo ni = cm.getActiveNetworkInfo();  
        if (ni == null) {  
            return false; // There are no active networks.  
        } else  
            return true;  
    }