Detect CONNECTIVITY CHANGE in Android 7 and above when app is killed/in background

后端 未结 5 1639
礼貌的吻别
礼貌的吻别 2020-11-27 12:56

Problem:

So the problem is that I have an app which sends a request to our backend whenever WiFi is connected (with the connected SSID and other inf

相关标签:
5条回答
  • 2020-11-27 13:04

    Below is excerpt from documentation

    Apps targeting Android 7.0 (API level 24) and higher do not receive CONNECTIVITY_ACTION broadcasts if they declare the broadcast receiver in their manifest. Apps will still receive CONNECTIVITY_ACTION broadcasts if they register their BroadcastReceiver with Context.registerReceiver() and that context is still valid.

    So you will get this Broadcast till your context is valid in Android N & above by explicitly registering for same.

    Boot Completed:

    You can listen android.intent.action.BOOT_COMPLETED broadcast you will need this permission for same.

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

    App Killed Scenario:

    You are not going to receive it.

    That is very much expected and due to various reasons

    • Android Oreo has limitations on running services in background, so you may face this on O devices

    • Doze mode on Android Marshmallow onwards can cause this, it will stop all network operations itself & take away CPU wake locks

    • Though Doze mode have one mechanism for requesting whitelisting of apps, this might be useful for you.

    0 讨论(0)
  • 2020-11-27 13:10

    Nougat and Above: We have to use JobScheduler and JobService for Connection Changes.

    All I can divide this into three steps.

    Register JobScheduler inside activity. Also, Start JobService( Service to handle callbacks from the JobScheduler. Requests scheduled with the JobScheduler ultimately land on this service's "onStartJob" method.)

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

    The service to start and finish the job.

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

    Finally, The receiver class which checks the network connection changes.

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

    Don't forget to add permission and service inside manifest file.

    <?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>
    

    Please refer below links for more info.

    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

    0 讨论(0)
  • 2020-11-27 13:12

    The best way to grab Connectivity change Android Os 7 and above is register your ConnectivityReceiver broadcast in Application class like below, This helps you to get changes in background as well until your app alive.

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

    And then in manifest

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

    Here is your ConnectivityReceiver.java

     public class ConnectivityReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(final Context context, final Intent intent) {
          MLog.v(TAG, "onReceive().." + intent.getAction());
          }
        }
    
    0 讨论(0)
  • 2020-11-27 13:23

    That's how i did it. I have created a IntentService and in onCreate method and I have registered networkBroadacst which check for internet connection.

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

    This is my broadcast class

    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{}
        }
    }
    

    In this way you can check internet connection in whether your app is in foreground or background in nougat.

    0 讨论(0)
  • 2020-11-27 13:24

    Another approach which is simpler and easier when you use 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);
    }
    

    Which is SendAnyRequestService.class is your service class, and you can call your API inside it.

    This code work for Android 6.0 (API 23) and above

    Ref document is here

    0 讨论(0)
提交回复
热议问题