Фоновая служба не запускается Android

Я пытаюсь использовать фоновую службу, чтобы проверять наличие нового контента в приложении каждые 30 минут и уведомлять пользователя уведомлением о появлении нового контента. Проблема в том, что эта служба вообще не запускается. Я не совсем уверен, что я не так.

Я следил за этой статьей, чтобы реализовать уведомление служба и этот вопрос также - Попытка запустить службу при загрузке на Android.

AndroidManifest.xml

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

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

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <receiver android:name="com.____.BootReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    <service android:name="com.____.NotificationService"/>
</application>

BootReceiver должен запустить службу.

public class BootReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent startServiceIntent = new Intent(context, NotificationService.class);
        context.startService(startServiceIntent);
    }
}

Служба NotificationService прямо сейчас настроена на отображение простого уведомления.

public class NotificationService extends Service {

    private WakeLock mWakeLock;

    /**
     * Simply return null, since our Service will not be communicating with
     * any other components. It just does its work silently.
     */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * This is where we initialize. We call this when onStart/onStartCommand is
     * called by the system. We won't do anything with the intent here, and you
     * probably won't, either.
     */
    private void handleIntent(Intent intent) {
        // obtain the wake lock
        PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My App");
        mWakeLock.acquire();

        // check the global background data setting
        ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        if (cm.getActiveNetworkInfo() == null) {
            stopSelf();
            return;
        }

        // do the actual work, in a separate thread
        new PollTask().execute();
    }

    private class PollTask extends AsyncTask<Void, Void, Void> {
        /**
         * This is where YOU do YOUR work. There's nothing for me to write here
         * you have to fill this in. Make your HTTP request(s) or whatever it is
         * you have to do to get your updates in here, because this is run in a
         * separate thread
         */
        @SuppressLint("NewApi") @Override
        protected Void doInBackground(Void... params) {
            // do stuff!

            // get last added date time of offer
            // every 60 minutes check for new offers

            Notification notification = new Notification.Builder(getApplicationContext())
                .setContentText("New Content Available")
                .setSmallIcon(R.drawable.ic_launcher)
                .setWhen(0)
                .build();

            return null;
        }

        /**
         * In here you should interpret whatever you fetched in doInBackground
         * and push any notifications you need to the status bar, using the
         * NotificationManager. I will not cover this here, go check the docs on
         * NotificationManager.
         *
         * What you HAVE to do is call stopSelf() after you've pushed your
         * notification(s). This will:
         * 1) Kill the service so it doesn't waste precious resources
         * 2) Call onDestroy() which will release the wake lock, so the device
         *    can go to sleep again and save precious battery.
         */
        @Override
        protected void onPostExecute(Void result) {
            // handle your data
            stopSelf();
        }
    }

    /**
     * This is deprecated, but you have to implement it if you're planning on
     * supporting devices with an API level lower than 5 (Android 2.0).
     */
    @Override
    public void onStart(Intent intent, int startId) {
        handleIntent(intent);
    }

    /**
     * This is called on 2.0+ (API level 5 or higher). Returning
     * START_NOT_STICKY tells the system to not restart the service if it is
     * killed because of poor resource (memory/cpu) conditions.
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handleIntent(intent);
        return START_NOT_STICKY;
    }

    /**
     * In onDestroy() we release our wake lock. This ensures that whenever the
     * Service stops (killed for resources, stopSelf() called, etc.), the wake
     * lock will be released.
     */
    public void onDestroy() {
        super.onDestroy();
        mWakeLock.release();
    }
}

Отладка приложения с точками останова в BootRecevier и NotificationService никогда не запускается. Возможно, я неправильно понимаю, как работают фоновые службы.

ОБНОВЛЕНИЕ

Я нашел это статья о том, почему не вызывается BroadcastRecevier. Одним из упомянутых моментов является отсутствие PendingIntent requestCode.

Я обновил BootRecevier следующим образом, чтобы проверить службу... вызываемую каждую минуту:

@Override
public void onReceive(Context context, Intent intent) {
    int minutes = 1;
    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent i = new Intent(context, NotificationService.class);
    PendingIntent pi = PendingIntent.getService(context, 54321, i, 0);
    am.cancel(pi);

    if (minutes > 0) {
        am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
            SystemClock.elapsedRealtime() + minutes*60*1000,
            minutes*60*1000, pi);
    }
}

Изменение 0 на «уникальный» requestCode 54321 каким-то образом начало запускать службу. Проблема в том, что служба не продолжает работать, когда приложение закрыто... не уверен, что это совсем другое.

ОБНОВЛЕНИЕ 2

Я обновил метод doInBackground в NotificationService, используя этот пример отображения уведомлений:

@SuppressLint("NewApi") @Override
protected Void doInBackground(Void... params) {

    Context mContext = getApplicationContext();

    // invoke default notification service
    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mContext);

    mBuilder.setContentTitle("[Notification Title]");
    mBuilder.setContentText("[Notification Text]");
    mBuilder.setTicker(mContext.getString(R.string.app_name));
    mBuilder.setSmallIcon(R.drawable.ic_launcher);

    Intent resultIntent = new Intent(mContext, MainActivity.class);

    TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
    stackBuilder.addParentStack(MainActivity.class);

    stackBuilder.addNextIntent(resultIntent);
    PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_ONE_SHOT);

    mBuilder.setContentIntent(resultPendingIntent);

    NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(1, mBuilder.build());

    return null;
}

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


person j.grima    schedule 07.10.2014    source источник
comment
Вы можете попробовать запустить службу вручную, если хотите, чтобы она запускалась немедленно. (У меня есть служба липких сообщений, когда ОС ее убивает, перезапуск занимает некоторое время, но если мне нужно запустить немедленно, я запускаю этот код) context.startService(new Intent(context, YourService.class));   -  person Warwicky    schedule 08.10.2014


Ответы (2)


Вам нужно объявить разрешение на получение намерения в вашем манифесте.

AndroidManifest.xml

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

http://developer.android.com/reference/android/Manifest.permission.html#RECEIVE_BOOT_COMPLETED

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

person Blundell    schedule 07.10.2014
comment
Да, это уже там... Я обновлю свой вопрос на всякий случай. - person j.grima; 08.10.2014
comment
Вы должны объявить службу внутри тега <application. Трудно угадать ваши проблемы, если вы не дадите полный код :-) - person Blundell; 08.10.2014

Недавно у меня возникла такая же проблема, и я использую смартфон Android 5.1.1. Способ, которым я решил эту проблему, заключался в использовании ic_launcher in mipmap для замены ic_launcher in drawable при вызове setSmallIcon().

Однако по точной причине, я думаю, это как-то связано с разницей между mipmap и drawable.

Ссылка: mipmap против папок с возможностью рисования

person Lina Qiu    schedule 10.04.2016