AlarmManager、BroadcastReceiver和Service无法正常工作

13

我正在重构一些代码,使我的应用程序能够在指定的时间每天从网站中拉取数据。根据我的研究,似乎使用 AlarmManager 是最合适的方法。

我一直在遵循的教程是:http://mobile.tutsplus.com/tutorials/android/android-fundamentals-downloading-data-with-services/

到目前为止,AlarmManagerBroadcastReceiver 似乎都正常工作,但是 Service 似乎从未启动过(即 onStartCommand 似乎从未被调用)

以下是我目前所拥有的重要代码片段:

MyActivity.java

private void setRecurringAlarm(Context context) {
    Calendar updateTime = Calendar.getInstance();
    updateTime.setTimeZone(TimeZone.getDefault());
    updateTime.set(Calendar.HOUR_OF_DAY, 20);
    updateTime.set(Calendar.MINUTE, 30);

    Intent downloader = new Intent(context, AlarmReceiver.class);
    downloader.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);
    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    // should be AlarmManager.INTERVAL_DAY (but changed to 15min for testing)
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent); 
    Log.d("MyActivity", "Set alarmManager.setRepeating to: " + updateTime.getTime().toLocaleString());
}

AlarmReceiver.java

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent dailyUpdater = new Intent(context, MyService.class);
        context.startService(dailyUpdater);
        Log.d("AlarmReceiver", "Called context.startService from AlarmReceiver.onReceive");
    }
}

MyService.java

public class MyService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("MyService", "About to execute MyTask");
        new MyTask().execute();
        return Service.START_FLAG_REDELIVERY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private class MyTask extends AsyncTask<String, Void, boolean> {
        @Override
        protected boolean doInBackground(String... strings) {
            Log.d("MyService - MyTask", "Calling doInBackground within MyTask");
            return false;
        }
    }
}

AndroidManifest.xml

<application ...>
    ...
    <service android:name="MyService"></service>
    <receiver android:name="AlarmReceiver"></receiver>
</application>

当我在MyActivity中触发setRecurringAlarm时,日志按预期打印,同样地,每15分钟从AlarmReceiver的日志也会显示。 但是,我从未看到MyService的日志:(

以下是我在日志中看到的示例:

DEBUG/MyActivity(688): Set alarmManager.setRepeating to: Jan 29, 2012 8:30:06 PM
DEBUG/AlarmReceiver(688): Called context.startService from AlarmReceiver.onReceive
DEBUG/AlarmReceiver(688): Called context.startService from AlarmReceiver.onReceive

我似乎找不出做错了什么——根据Android Dev Docs,当我在AlarmReceiver中调用context.startService(dailyUpdater)时,应该会依次调用MyService中的onStartCommand方法,但实际上并非如此!

是什么导致MyService根本没有启动呢?


你也遇到了一个非明显的唤醒锁行为,即响应闹钟启动服务。在https://dev59.com/DoPba4cB1Zd3GeqPrmnk的答案中有一些细节和建议。 - ctate
3个回答

15

问题解决了!

MyService 应该继承 IntentService,而不是 Service

做出这个改变后,也意味着不再覆盖 onStartCommand 而是应该覆盖 onHandleIntent(请参阅有关IntentService的文档)。

因此,MyService 现在看起来像这样:

public class MyService extends IntentService {

    public MyService() {
        super("MyServiceName");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d("MyService", "About to execute MyTask");
        new MyTask().execute();
    }

    private class MyTask extends AsyncTask<String, Void, boolean> {
        @Override
        protected boolean doInBackground(String... strings) {
            Log.d("MyService - MyTask", "Calling doInBackground within MyTask");
            return false;
        }
    }
}
注意:从文档中可以得知,onBind 的默认实现会返回 null,因此不需要覆盖它。
有关扩展 IntentService 的更多信息,请访问:http://developer.android.com/guide/topics/fundamentals/services.html#ExtendingIntentService

1
+1 感谢!“Service”类在Android 4.x设备上运行得很好,但我无法弄清楚为什么它在Android 2.x设备上不起作用。当更换为“IntentService”时,它就像魔术般地运行了。吸取了教训! - Felipe Caldas

0

如果按照以下方式更改您的意图,您可以直接通过AlarmManager运行服务,而不必经过BroadcastReceiver:

//Change the intent
Intent downloader = new Intent(context, MyService.class);
    downloader.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

//Change to getService()
PendingIntent pendingIntent = PendingIntent.getService(context, 0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);

这可能解决你的问题!


谢谢建议 - 我之前尝试过(尽管没有使用downloader.addFlags),但并没有成功 - 我会用addFlags再试一下,看看是否有帮助! 只是好奇,如果您可以直接运行服务,为什么还需要'BroadcastReceiver'存在? - pyko
尝试了这个更改 - 没有成功 :( 也尝试了像kumar建议的将context更改为this... 也没有运气! - pyko

0

不要使用

Intent dailyUpdater = new Intent(context, MyService.class);

而是使用

Intent dailyUpdater = new Intent(this, MyService.class);

另一个建议是直接从闹钟启动服务,而不是发送广播并在广播接收器中启动服务。


你尝试过在没有使用downloader.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)的情况下运行你的代码吗? - kumar ranjan
嗨,库马尔,是的,我相信我试过不使用addFlags,但也没有奏效。 - pyko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接