Включить интернет на Android во сне

У меня есть приложение для Android, которое необходимо синхронизировать с Интернетом, но как только телефон переходит в спящий режим, я не могу получить доступ к Интернету. Происходит только когда пользователь использует "режим батареи", когда отключает данные через 15 минут. Я написал тестовое приложение, и оно включает данные, но оно все еще подключается к серверу.

Что я пробовал:

  • Когда я отключаю данные вручную, приложение включает их, и они работают.
  • Я также пробовал WakeLock, но это не помогло.
  • Будильник работает как положено, даже когда телефон засыпает часами

Протестировано на Motorola Atrix Android 2.3.3. Я не могу полагаться на Wi-Fi. В реальной жизни он будет синхронизироваться каждую неделю. Как мы можем сделать это возможным?

Менеджер тревог:

alarm_manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, 
                        PendingIntent.FLAG_UPDATE_CURRENT);
alarm_manager.setRepeating(AlarmManager.RTC_WAKEUP, 
                        System.currentTimeMillis(), 15000, pending);

Приемник тревог:

public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("MYTAG", "RECEIVED getMobileDataEnabled: " + getMobileDataEnabled(context));  
        if (!isOnline(context)) {
            Log.d("MYTAG", "NO INET");
            if (turnOnInet(context)) {
                Log.d("MYTAG", "INET IS ON");
            }
        }

        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost("http://xxx.xxx.xxx.xxx/ping/pong/moto/");
            try {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
                nameValuePairs.add(new BasicNameValuePair("short_code", "ROFL"));
                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                httpclient.execute(httppost);
                Log.d("MYTAG", "POST FINISHED");
            }
            catch (Exception e) {
                Log.e("MYTAG", "MYTAG", e);
            }
    }

    public boolean isOnline(Context context) {
        ConnectivityManager cm = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo != null){
            Log.d("MYTAG", "isAvailable: "+netInfo.isAvailable());
        }
        if (netInfo != null && netInfo.isConnectedOrConnecting()) {
            return true;
        }
        return false;
    }

    public boolean turnOnInet(Context context) {
        ConnectivityManager mgr = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mgr == null) {
            Log.d("MYTAG", "ConnectivityManager == NULL");
            return false;
        }
        try {
            Method setMobileDataEnabledMethod = mgr.getClass().getDeclaredMethod("setMobileDataEnabled", boolean.class);
            if (null == setMobileDataEnabledMethod) {
                Log.d("MYTAG", "setMobileDataEnabledMethod == null");
                return false;
            }    
            setMobileDataEnabledMethod.invoke(mgr, true);
        }
        catch(Exception e) {
            Log.e("MYTAG", "MYTAG", e);
            return false;
        }
        return true;
    }   


    private boolean getMobileDataEnabled(Context context) {
        ConnectivityManager mgr = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mgr == null) {
            Log.d("MYTAG", "getMobileDataEnabled ConnectivityManager == null");
            return false;
        }
        try {
            Method method = mgr.getClass().getMethod("getMobileDataEnabled");
            return (Boolean) method.invoke(mgr);
        } catch (Exception e) {
            Log.e("MYTAG", "MYTAG", e);
            return false;
        }
    }
}

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

person programmersbook    schedule 12.08.2012    source источник
comment
Пожалуйста, не убивайте аккумулятор ваших пользователей.   -  person SLaks    schedule 13.08.2012
comment
В реальной жизни он будет синхронизироваться каждую неделю   -  person programmersbook    schedule 13.08.2012
comment
Вы пытались использовать флаг RTC вместо RTC_WAKEUP? Поскольку пробуждение не принудительное, возможно, фреймворк включит сеть. Поскольку вы используете синхронизацию раз в неделю, вам не нужен флаг WAKEUP.   -  person nandeesh    schedule 15.08.2012
comment
Когда он не просыпается, когда спит. Я хотел бы сохранить время.   -  person programmersbook    schedule 16.08.2012
comment
И если пользователь выключит телефон на две недели (например, отпуск за границей), ваш тайминг будет нарушен.   -  person Morrison Chang    schedule 18.08.2012
comment
stackoverflow.com/a/4304110/726863   -  person Lalit Poptani    schedule 18.08.2012


Ответы (3)


Во-первых, вам нужно получить код HttpPost из BroadcastReceiver в IntentService. Никогда не выполняйте сетевой ввод-вывод в основном потоке приложения, а onReceive() вызывается в основном потоке приложения. Например, если вы потратите слишком много времени, Android прервет ваш код на полпути к работе в Интернете.

Во-вторых, учитывая IntentService, вам нужно использовать WakeLock. Это может побудить вас использовать мой WakefulIntentService, который решает обе проблемы. Или используйте WakefulBroadcastReceiver с той же целью. .

В-третьих, удалите turnOnInet() и getMobileDataEnabled(). Они вам не нужны, они ненадежны, и, в частности, turnOnInet() враждебно настроены к пользователю — если бы пользователь хотел включить мобильные данные, он бы его включил.

Теперь, учитывая все это, в вашем onHandleIntent() из ваших IntentService() (или в вашем doWakefulWork() из ваших WakefulIntentService), если у вас нет подключения к Интернету сразу, в качестве временного обходного пути, SystemClock.sleep() на секунду и повторите попытку, повторив несколько раз в петле. Если через некоторое время вы обнаружите, что получаете доступ к Интернету, вы можете подумать о том, чтобы стать более изощренным (например, прослушивать широковещательные сообщения об изменении подключения, а не опрашивать, хотя это уведет вас от WakefulIntentService к обычному Service с вашим собственным фоновым потоком. и конечный автомат для WakeLock управления). Или просто придерживайтесь sleep() — вряд ли наступит конец света, если вы завяжете эту фоновую нить на несколько секунд. Однако, если вы не можете подключиться в течение небольшого промежутка времени, пожалуйста, не продолжайте попытки бесконечно, так как существует множество причин, по которым вы можете не установить подключение, включая управление пропускной способностью, управляемое пользователем на Android 4.0+.

person CommonsWare    schedule 19.08.2012
comment
Я борюсь уже несколько недель. Любые идеи, почему при правильном использовании вашего WakefulIntentService мой фоновый сервис по-прежнему не выходит в Интернет при пробуждении от глубокого сна, несмотря на многократные попытки подключения со сном? Я планирую службу с помощью диспетчера аварийных сигналов, и я знаю, что должен планировать трансляцию, которая затем фактически запускает службу, независимо от того, что служба срабатывает вовремя, но на устройстве просто нет Интернета (пингование Google) . Я действительно в растерянности. - person Cord Rehn; 04.01.2017
comment
@CordRehn: Режим сна, возможно. Или, если на устройстве нет мобильных данных, для установления соединения WiFi требуется время. - person CommonsWare; 04.01.2017
comment
Спасибо за ваше время, сейчас я изучу режим Doze. Я был великодушен, дав устройству 3 минуты, чтобы успешно пропинговать Google с 10-секундными тайм-аутами и 3-секундными интервалами. Я не думаю, что решение просто ждать дольше? - person Cord Rehn; 04.01.2017
comment
@CordRehn: Наверное, нет. Но с режимом Doze и режимом ожидания приложения то, что ваше приложение может работать (иногда), не означает, что у вас есть доступ в Интернет в эти моменты времени. - person CommonsWare; 04.01.2017
comment
Ах, это имеет смысл, и изучение режима сна все больше похоже на то, что происходит. Мои будильники точны и не повторяются, поскольку они чувствительны ко времени. Я думаю, что единственное реальное решение здесь — предложить пользователям внести мое приложение в белый список и предупредить о недостатках, если они этого не сделают. Как звук? - person Cord Rehn; 04.01.2017

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

Более правильным подходом и прямым решением также было бы время от времени выполнять жесткую/полную синхронизацию всякий раз, когда пользователь открывает ваше приложение или имеет доступ к какому-либо Wi-Fi-соединению (ConnectivityManager — ваш друг) на основе некоторых простых условий (последняя синхронизация длилась более недели, устаревшие сохраненные данные, несоответствия и т. д.) и выполнить мягкую синхронизацию (обновление данные в фоновом режиме) в остальных случаях.

Кроме того, периодическая синхронизация означает потерю пользовательских данных, если этот пользователь не использует приложение. В конечном счете, это превращает ваше приложение в идеального кандидата на закрытие системой время от времени.

Надеюсь, поможет. Держите нас в курсе вашего прогресса.

Читайте также: Оптимизация загрузки для эффективного доступа к сети

person Jose L Ugia    schedule 18.08.2012
comment
Хорошая точка зрения. Кроме того, взгляните на статьи здесь о написании оптимизированных приложений, использующих сеть. Есть статьи о группировке сетевых передач и обработке нескольких подключений. developer.att.com/developer/forward.jsp?passedItemId=7200042 - person Rod Burns; 22.08.2012

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

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

person SamuelDavis    schedule 20.08.2012