代码之家  ›  专栏  ›  技术社区  ›  Remy Kabel

当应用程序被终止/在后台时,检测Android 7及以上版本中的连接更改

  •  40
  • Remy Kabel  · 技术社区  · 6 年前

    问题:

    所以问题是,我有一个应用程序,它可以在连接WiFi(使用连接的SSID和其他信息)或断开连接(通过移动网络)时向我们的后端发送请求。然而,随着Android 7/N及以上版本的更改,CONNECTIVITY\u CHANGE和CONNECTIVITY\u ACTION不再在后台工作。现在,在大多数情况下,人们滥用了这种广播,因此,我完全理解为什么会做出这种改变。然而,我不知道在目前的状态下如何解决这个问题。

    现在我根本不是一个Android开发人员(这是一个Cordova插件),所以我指望你们!

    预期行为: 无论何时WiFi切换连接,应用程序都会被唤醒并发送请求,即使应用程序在后台被关闭。

    当前行为: 应用程序仅在应用程序位于前台时发送请求。

    迄今为止已尝试: 到目前为止,我已经将侦听CONNECTIVITY\u操作的隐含意图从清单中转移到在应用程序(插件)的主要部分手动注册它。这样,只要应用程序在内存中,而不是在冷启动或实际后台,它就可以工作

    已查看: 大多数答案都是关于使用预定的作业来代替丢失的广播。我知道这是如何工作的,例如,重试下载或类似的,但不适用于我的情况(如果我错了,请纠正我)。以下是我已经看过的SO帖子:

    Detect connectivity changes on Android 7.0 Nougat when app is in foreground

    ConnectivityManager.CONNECTIVITY_ACTION deprecated

    Detect Connectivity change using JobScheduler

    Android O - Detect connectivity change in background

    5 回复  |  直到 6 年前
        1
  •  65
  •   Jitesh Mohite    6 年前

    牛轧糖及以上: 我们必须使用JobScheduler和JobService来更改连接。

    我只能把它分为三个步骤。

    在活动内注册JobScheduler。此外,启动JobService( 用于处理来自JobScheduler的回调的服务。已计划的请求 通过JobScheduler最终登录到该服务的“onStartJob” 方法。)

    public class NetworkConnectionActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_network_connection);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
    
            scheduleJob();
    
        }
    
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        private void scheduleJob() {
            JobInfo myJob = new JobInfo.Builder(0, new ComponentName(this, NetworkSchedulerService.class))
                    .setRequiresCharging(true)
                    .setMinimumLatency(1000)
                    .setOverrideDeadline(2000)
                    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                    .setPersisted(true)
                    .build();
    
            JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
            jobScheduler.schedule(myJob);
        }
    
        @Override
        protected void onStop() {
            // A service can be "started" and/or "bound". In this case, it's "started" by this Activity
            // and "bound" to the JobScheduler (also called "Scheduled" by the JobScheduler). This call
            // to stopService() won't prevent scheduled jobs to be processed. However, failing
            // to call stopService() would keep it alive indefinitely.
            stopService(new Intent(this, NetworkSchedulerService.class));
            super.onStop();
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            // Start service and provide it a way to communicate with this class.
            Intent startServiceIntent = new Intent(this, NetworkSchedulerService.class);
            startService(startServiceIntent);
        }
    }
    

    启动和完成作业的服务。

    public class NetworkSchedulerService extends JobService implements
            ConnectivityReceiver.ConnectivityReceiverListener {
    
        private static final String TAG = NetworkSchedulerService.class.getSimpleName();
    
        private ConnectivityReceiver mConnectivityReceiver;
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(TAG, "Service created");
            mConnectivityReceiver = new ConnectivityReceiver(this);
        }
    
    
    
        /**
         * When the app's NetworkConnectionActivity is created, it starts this service. This is so that the
         * activity and this service can communicate back and forth. See "setUiCallback()"
         */
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i(TAG, "onStartCommand");
            return START_NOT_STICKY;
        }
    
    
        @Override
        public boolean onStartJob(JobParameters params) {
            Log.i(TAG, "onStartJob" + mConnectivityReceiver);
            registerReceiver(mConnectivityReceiver, new IntentFilter(Constants.CONNECTIVITY_ACTION));
            return true;
        }
    
        @Override
        public boolean onStopJob(JobParameters params) {
            Log.i(TAG, "onStopJob");
            unregisterReceiver(mConnectivityReceiver);
            return true;
        }
    
        @Override
        public void onNetworkConnectionChanged(boolean isConnected) {
            String message = isConnected ? "Good! Connected to Internet" : "Sorry! Not connected to internet";
            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
    
        }
    }
    

    最后,检查网络连接的receiver类 变化。

    public class ConnectivityReceiver extends BroadcastReceiver {
    
        private ConnectivityReceiverListener mConnectivityReceiverListener;
    
        ConnectivityReceiver(ConnectivityReceiverListener listener) {
            mConnectivityReceiverListener = listener;
        }
    
    
        @Override
        public void onReceive(Context context, Intent intent) {
            mConnectivityReceiverListener.onNetworkConnectionChanged(isConnected(context));
    
        }
    
        public static boolean isConnected(Context context) {
            ConnectivityManager cm = (ConnectivityManager)
                    context.getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
            return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
        }
    
        public interface ConnectivityReceiverListener {
            void onNetworkConnectionChanged(boolean isConnected);
        }
    }
    

    不要忘记在清单文件中添加权限和服务。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.yourpackagename">
    
        <uses-permission android:name="android.permission.INTERNET"/>
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    
    
        <!-- Always required on api < 21, needed to keep a wake lock while your job is running -->
        <uses-permission android:name="android.permission.WAKE_LOCK"/>
        <!-- Required on api < 21 if you are using setRequiredNetworkType(int) -->
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <!-- Required on all api levels if you are using setPersisted(true) -->
        <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity
                android:name=".connectivity.NetworkConnectionActivity"
                android:theme="@style/AppTheme.NoActionBar">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
    
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
    
    
            <!-- Define your service, make sure to add the permision! -->
            <service
                android:name=".connectivity.NetworkSchedulerService"
                android:exported="true"
                android:permission="android.permission.BIND_JOB_SERVICE"/>
        </application>
    
    </manifest>
    

    请参考以下链接了解更多信息。

    https://github.com/jiteshmohite/Android-Network-Connectivity

    https://github.com/evant/JobSchedulerCompat

    https://github.com/googlesamples/android-JobScheduler

    https://medium.com/@iiro.krankka/its-time-to-kiss-goodbye-to-your-implicit-broadcastreceivers-eefafd9f4f8a

        2
  •  6
  •   Guruprasad    6 年前

    获取连接更改Android Os 7及以上版本的最佳方法是在应用程序类中注册您的ConnectivityReceiver广播,如下所示,这有助于您在应用程序激活之前获取背景更改。

    public class MyApplication extends Application {
    
          private ConnectivityReceiver connectivityReceiver;
    
          private ConnectivityReceiver getConnectivityReceiver() {
              if (connectivityReceiver == null)
                   connectivityReceiver = new ConnectivityReceiver();
    
              return connectivityReceiver;
           }
           @Override
           public void onCreate() {
             super.onCreate();
             registerConnectivityReceiver();
           }
    
         // register here your filtters 
         private void registerConnectivityReceiver(){
           try {
              // if (android.os.Build.VERSION.SDK_INT >= 26) {
              IntentFilter filter = new IntentFilter();
              filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
              //filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
              //filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
              //filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
              registerReceiver(getConnectivityReceiver(), filter);
           } catch (Exception e) {
             MLog.e(TAG, e.getMessage());
           }
     }
    
    }
    

    然后在舱单中

         <application
          android:name=".app.MyApplication"/>
    

    这是您的ConnectivityReceiver。Java语言

     public class ConnectivityReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(final Context context, final Intent intent) {
          MLog.v(TAG, "onReceive().." + intent.getAction());
          }
        }
    
        3
  •  3
  •   A. Badakhshan    6 年前

    我就是这样做的。我创建了一个 IntentService 并且在 onCreate 方法和我已注册 networkBroadacst 检查internet连接。

    public class SyncingIntentService extends IntentService {
        @Override
        public void onCreate() {
            super.onCreate();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                networkBroadcast=new NetworkBroadcast();
                registerReceiver(networkBroadcast,
                      new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
            }
        }
    
        @Override
        public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
            onHandleIntent(intent);
            return START_STICKY;
        }
    }
    

    这是我的广播课

    public class NetworkBroadcast extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Constants.isInternetConnected(context)) {
    //            Toast.makeText(context, "Internet Connect", Toast.LENGTH_SHORT).show();
               context.startService(new Intent(context, SyncingIntentService.class));
            }
            else{}
        }
    }
    

    通过这种方式,您可以检查您的应用程序在牛轧糖中是处于前台还是后台的互联网连接。

        4
  •  2
  •   Akhil    6 年前

    下面是 excerpt from documentation

    针对Android 7.0(API级别24)及更高版本的应用程序不会收到 CONNECTIVITY\u ACTION在声明广播接收器时广播 在他们的舱单上。应用仍将接收CONNECTIVITY\u操作 如果他们将其广播接收器注册到 上下文registerReceiver()和该上下文仍然有效。

    因此,您将获得此广播,直到您的上下文在Android N&中有效为止;通过显式注册相同。

    启动已完成:

    你可以听 android.intent.action.BOOT_COMPLETED 广播 您需要此权限才能进行相同操作。

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    

    应用程序终止场景:

    你不会收到它的。

    这是非常令人期待的 各种原因

        5
  •  0
  •   Frank Nguyen    5 年前

    当您使用 registerNetworkCallback (NetworkRequest, PendingIntent) :

    NetworkRequest.Builder builder = new NetworkRequest.Builder();
    builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
    builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
    builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
    builder.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
    
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    Intent intent = new Intent(this, SendAnyRequestService.class);
    
    PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    if (connectivityManager != null) {
        NetworkRequest networkRequest = builder.build();
        connectivityManager.registerNetworkCallback(networkRequest, pendingIntent);
    }
    

    这就是 SendAnyRequestService.class 是您的服务类,您可以在其中调用API。

    此代码适用于Android 6.0(API 23)及以上版本

    参考文件为 here