Обнаружение ИЗМЕНЕНИЯ ПОДКЛЮЧЕНИЯ в Android 7 и выше, когда приложение убито/в фоновом режиме

Проблема:

Поэтому проблема заключается в том, что у меня есть приложение, которое отправляет запрос на наш бэкэнд при подключении WiFi (с подключенным SSID и другой информацией) или при его отключении (через мобильную сеть). Однако с изменениями в Android 7/N и выше CONNECTIVITY_CHANGE и CONNECTIVITY_ACTION больше не работают в фоновом режиме. Теперь в большинстве случаев люди злоупотребляют этой трансляцией, и поэтому я могу полностью понять, почему было сделано изменение. Однако я не знаю, как решить эту проблему в текущем состоянии.

Теперь я совсем не разработчик Android (это для плагина Кордовы), поэтому я рассчитываю на вас, ребята!

Ожидаемое поведение: приложение пробуждается, и запрос отправляется всякий раз, когда WiFi переключается на соединение, даже когда приложение убито/в фоновом режиме.

Текущее поведение: приложение отправляет запрос только тогда, когда приложение находится на переднем плане.

Пробовал до сих пор: до сих пор я переместил неявное намерение прослушать CONNECTIVITY_ACTION из манифеста, чтобы вручную зарегистрировать его в основной части приложения (плагина). Это заставляет его работать до тех пор, пока приложение находится в памяти, но не при холодном загрузке или фактическом фоне

Уже смотрели: Большинство ответов говорят о том, как использовать запланированные задания для замены отсутствующей трансляции. Я вижу, как это работает, например, для повторной загрузки или аналогичного, но не для моего случая (но, пожалуйста, поправьте меня, если я ошибаюсь). Ниже приведены сообщения SO, на которые я уже смотрел:

Обнаружение изменений соединения на Android 7.0 Нуга, когда приложение находится на переднем плане

ConnectivityManager.CONNECTIVITY_ACTION устарел

Обнаружение изменения связи с помощью JobScheduler

Android O - обнаружение изменений подключения в фоновом режиме

Ответ 1

Нуга и выше: мы должны использовать JobScheduler и JobService для изменений соединения.

Все, что я могу разделить на три шага.

Зарегистрировать работу JobScheduler внутри. Кроме того, Start JobService (Service для обработки обратных вызовов из 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 "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 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();

    }
}

Наконец, класс приемника, который проверяет сетевое соединение, изменяется.

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

Вот как я это сделал. Я создал IntentService и метод onCreate и я зарегистрировал networkBroadacst который проверяет подключение к Интернету.

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

Таким образом, вы можете проверить подключение к Интернету, независимо от того, находится ли ваше приложение на переднем плане или фон в нуге.

Ответ 3

Ниже приведена отрывок из документации

Приложения, ориентированные на Android 7.0 (API-уровень 24) и выше, не получают трансляции CONNECTIVITY_ACTION, если объявляют вещательный приемник в своем манифесте. Приложения будут по-прежнему получать трансляции CONNECTIVITY_ACTION, если они регистрируют свой BroadcastReceiver с Context.registerReceiver(), и этот контекст остается в силе.

Таким образом, вы получите эту трансляцию, пока ваш контекст не будет действителен в Android N и выше, явно зарегистрировавшись для этого.

Загрузка завершена:

Вы можете прослушать эфир android.intent.action.BOOT_COMPLETED вам понадобится это разрешение.

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

App Killed Сценарий:

Вы не получите его.

Это очень ожидаемо и по разным причинам

  • Android Oreo имеет ограничения на запуск служб в фоновом режиме, поэтому вы можете столкнуться с этим на устройствах O

  • Режим Doz на Android Marshmallow onwards может привести к этому, он полностью остановит все сетевые операции и уберет блокировки процессора

  • Хотя режим Doze имеет один механизм запроса белого списка приложений, это может быть полезно для вас.