GCMNetworkManager не запускает PeriodicTask после перезагрузки

Приложение показывает ожидаемое поведение, если приложение запущено на переднем плане, в фоновом режиме или завершено. Однако после перезагрузки PeriodicTask перестает работать

Ниже приведены соответствующие фрагменты кода:

In AndroidManifest:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<service android:name=".tracking.MyTaskService"
            android:exported="true"
            android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
            <intent-filter>
                <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
            </intent-filter>
        </service>

Конфигурация PeriodicTask:

PeriodicTask task = new PeriodicTask.Builder()
                .setService(MyTaskService.class)
                .setTag(TASK_TAG_PERIODIC)
                .setPeriod(30L)
                .setFlex(10L)
                .setExtras(bundle)
                .setPersisted(true)
                .build();

        mGcmNetworkManager.schedule(task);

В Logcat я получаю следующее:

E/NetworkScheduler.TED: Couldn't start service: Intent 
{ act=com.google.android.gms.gcm.ACTION_TASK_READY
  cmp=xxx.xxxxxx.xxx/.tracking.MyTaskService (has extras) 
}

Прилагая все соответствующие детали:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.example.gcmnetworkmanagerquickstart">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- [START manifest_service] -->
        <service
            android:name=".MyTaskService"
            android:exported="true"
            android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
            <intent-filter>
                <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
            </intent-filter>
        </service>
        <!-- [END manifest_service] -->

    </application>

</manifest>

MainActivity

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int RC_PLAY_SERVICES = 123;
    public static final String TASK_TAG_PERIODIC = "periodic_task";

    private GcmNetworkManager mGcmNetworkManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mGcmNetworkManager = GcmNetworkManager.getInstance(this);

        if(checkPlayServicesAvailable()){
            startPeriodicTask();
        }

    }



    public void startPeriodicTask() {
        Log.d(TAG, "startPeriodicTask");

        PeriodicTask task = new PeriodicTask.Builder()
                .setService(MyTaskService.class)
                .setTag(TASK_TAG_PERIODIC)
                .setPeriod(5)
                .setPersisted(true)
                .build();

        mGcmNetworkManager.schedule(task);
    }

    private boolean checkPlayServicesAvailable() {
        GoogleApiAvailability availability = GoogleApiAvailability.getInstance();
        int resultCode = availability.isGooglePlayServicesAvailable(this);

        if (resultCode != ConnectionResult.SUCCESS) {
            if (availability.isUserResolvableError(resultCode)) {
                // Show dialog to resolve the error.
                availability.getErrorDialog(this, resultCode, RC_PLAY_SERVICES).show();
            } else {
                // Unresolvable error
                Toast.makeText(this, "Google Play Services error", Toast.LENGTH_SHORT).show();
            }

            Log.d(TAG, "Play Services NOT Available");
            return false;
        } else {
            Log.d(TAG, "Play Services Available");
            return true;
        }
    }
}

MyTaskService

public class MyTaskService extends GcmTaskService {

    private static final String TAG = "MyTaskService";

    @Override
    public void onInitializeTasks() {
    }

    @Override
    public int onRunTask(TaskParams taskParams) {
        Log.d(TAG, "onRunTask: " + taskParams.getTag());

        return doPeriodicTask();
    }

    private int doPeriodicTask() {
        Log.d(TAG, "doPeriodicTask Called");
        return GcmNetworkManager.RESULT_SUCCESS;
    }


}

build.gradle (модуль приложения)

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.0"

    defaultConfig {
        applicationId "com.google.example.gcmnetworkmanagerquickstart"
        minSdkVersion 14
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

allprojects {
    repositories {
        jcenter()
        google()
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:26.0.0-beta2'

    compile 'com.squareup.okhttp:okhttp:2.7.0'

    compile 'com.google.android.gms:play-services-gcm:11.0.2'
}

Edit1: После нескольких дней анализа я выяснил следующее:

  1. Это проблема конкретного устройства. Например, этого не происходит на устройствах Nexus.
  2. Это часть более серьезной проблемы. Устройства, демонстрирующие такое поведение, также не работают должным образом с AlarmManager, FirebaseJobScheduler и RECEIVE_BOOT_COMPLETED broadcast receiver.
  3. Одним из способов обхода проблемы является это решение. Однако у этого решения есть как минимум 2 проблемы. (1) Когда вы убиваете приложение, AccessibilityService разрешение сбрасывается. Это означает, что каждый раз, когда вы открываете приложение после этого, вы должны будете давать разрешение вручную. (2) Если приложение убито, перезагрузки после этого не дойдут до RECEIVE_BOOT_COMPLETED broadcast receiver
  4. Безумный вывод: на устройствах типа «один плюс», если ваше приложение содержит слово test в структуре пакета, все работает !!
  5. Если вы внесете свое приложение в белый список, выбрав «Настройки»> «Приложения» (его расположение и имя могут отличаться на разных устройствах), все будет работать должным образом.
  6. Приложения для запуска, в которые вам нужно вручную добавить свое приложение, содержат хорошо известные приложения, такие как WhatsApp, Facebook, Instagram и многие другие. Когда вы устанавливаете эти приложения, они автоматически добавляются в этот список! Я еще не видел специального API, опубликованного любым из этих производителей для этого. Это заставляет меня думать, что эти приложения внесены в белый список со стороны производителей.

person ranjjose    schedule 12.07.2017    source источник
comment
Изучая журналы, я наткнулся на тот факт, что собственное приложение Google Youtube для детей также имеет ту же проблему: E / NetworkScheduler.TED: Не удалось запустить службу: Intent {act = com.google.android.gms. gcm.ACTION_TASK_READY cmp = com.google.android.apps.youtube.kids / com.google.android.libraries.youtube.common.gcore.gcoreclient.gcm.impl.GcmTaskServiceDelegator (есть дополнения)}   -  person ranjjose    schedule 16.07.2017
comment
это происходит на нескольких устройствах?   -  person Pararth    schedule 17.07.2017
comment
Да .. это действительно происходит на нескольких устройствах. Я испытал это на одном плюсе, устройствах MI.   -  person ranjjose    schedule 17.07.2017
comment
Вы пробовали стандартные устройства Android? Nexus, Moto, Pixel и т. Д.? иногда это настраиваемое поведение (например, устройства MI принудительно закрывают приложение при его `` убийстве '') для ОС, реализованной OEM-производителями, возможно, вам нужно будет выбрать обходной путь   -  person Pararth    schedule 17.07.2017
comment
Хм .. Все это немного странно. В рамках реализации этой функции я скопировал код из github.com/googlesamples/android-gcmnetworkmanager и приложение работает должным образом в первом проекте, который я сделал. Когда я скопировал изменения в наш продукт, он перестал работать. Даже сейчас, на этом PoC, он все еще работает. Проблема больше нигде не работает !!   -  person ranjjose    schedule 17.07.2017
comment
который снимает фактор устройства / OEM, может стать случаем сравнения различий ... например, вы возвращаете super для runTask или переопределяете его   -  person Pararth    schedule 17.07.2017
comment
Я не вносил никаких изменений в GcmTaskService (подкласс) демонстрации. Только изменения касаются версий Gradle. И они такие же как в рабочей версии, так и в нерабочей версии проекта !!   -  person ranjjose    schedule 17.07.2017
comment
также проверьте записи манифеста и класс обслуживания, я не могу думать о многом прямо сейчас   -  person Pararth    schedule 17.07.2017
comment
Хорошо спасибо! Проверил все эти места; Я добавлю все эти детали в вопрос для справки. Надеюсь, это поможет.   -  person ranjjose    schedule 17.07.2017
comment
Добавлена ​​вся соответствующая информация   -  person ranjjose    schedule 17.07.2017
comment
У меня есть шанс выполнить это на планшете nexus 9. И, вероятно, поскольку это стандартное устройство Android, оно работает отлично, как и ожидалось.   -  person ranjjose    schedule 18.07.2017


Ответы (2)


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

Лучший совет, который я могу дать, - сделать сервис как можно минимальным для другого процесса (чтобы он не был убит, когда приложение будет убито), а также при загрузке проверьте, включен ли он, а если нет - используйте система уведомлений, обеспечивающая быстрый доступ к экрану настроек специальных возможностей Android для облегчения включения.


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

В AndroidManifest.xml:

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

    <receiver android:name=".BootListener">
        <intent-filter>
            <category android:name="android.intent.category.DEFAULT"/>

            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>

И создать класс

public class BootListener extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      // If your bootstrap is in Application class, then it will be called anyway - nothing to do here.
      // else - call your bootsrap here
    }
}

я полагаю, что

(2) Если приложение убито, перезагрузка после этого не затронет широковещательный приемник RECEIVE_BOOT_COMPLETED.

Неправильно, при перезагрузке Android не знает, что ваше приложение было убито. Для моего приложения это никогда не было проблемой (+1,2 миллиона пользователей).

person Amir Uval    schedule 23.07.2017
comment
Спасибо, Auval. Я уже пробовал с android.intent.category.DEFAULT фильтром. К сожалению, он не работал на трех-четырех тестовых устройствах (один плюс 3T, MI, redmi). Перезагрузки после того, как приложение убивает проблему, касается Accessibility service - person ranjjose; 23.07.2017
comment
Я не видел BroadcastReceiver в опубликованном вами коде. - person Amir Uval; 23.07.2017
comment
Также я прочитал о accessibility service, что когда вы убиваете приложение, оно сбрасывает разрешение службы специальных возможностей. - person ranjjose; 23.07.2017
comment
stackoverflow.com/a/26349254/1180898 говорит, что отключение службы доступности немедленно перезапускает ее. Возможно, вы имеете в виду отключение службы пользователем? - person Amir Uval; 23.07.2017
comment
Неа. Я говорю об убийстве приложения, для которого вы дали разрешение службы специальных возможностей. Также большинство из этих поведений зависит от устройства. - person ranjjose; 23.07.2017

Конфигурация PeriodicTask:

PeriodicTask task = new PeriodicTask.Builder()
                .setService(MyTaskService.class)
                .setTag(TASK_TAG_PERIODIC)
                .setPeriod(30L)
                .setFlex(10L)
                .setExtras(bundle) **/* you put this line here */**
                .setPersisted(true)
                .build();


    PeriodicTask task = new PeriodicTask.Builder()
                    .setService(MyTaskService.class)
                    .setTag(TASK_TAG_PERIODIC)
/* you don't have ".setExtras(bundle)" line here */ 
/* try adding this line from above, as logcat is showing this */
                    .setPeriod(5)
                    .setPersisted(true)
                    .build();
person Uddhav Gautam    schedule 20.07.2017
comment
Спасибо, что заметили это. Однако проблема не в этом. Думаю, когда я скопировал вставку, я скопировал не тот файл. Проблема зависит от устройства. Например, в устройствах oneplus и MI эти проблемы возникают. Это тоже непоследовательно. Фактически, в oneplus (3T), если у вас есть слово test в пакете boot_completed, удаляется !! В МИ ничего не работает. На данный момент у меня есть работа со службами доступности, как указано здесь stackoverflow.com/a/41627296/1349159 - person ranjjose; 20.07.2017
comment
Да, это очень специфические случаи устройств. Многие производители делают вид Android и имеют собственный уровень безопасности, который останавливает запуск фоновой службы приложения до тех пор, пока пользователь не выйдет и не предоставит эксклюзивное разрешение. Мы воспроизвели это поведение на мобильных устройствах Xiomi в нашей разработке. В этом случае вы должны предоставить пользователю какой-то справочный документ. - person Devesh; 23.07.2017