Cannot do work in background periodically with alarmmanager

会有一股神秘感。 提交于 2021-01-27 10:39:50

问题


I want to call a service in background by the hour.

first problem is alarm manager is not working smoothly. Timer is terrible, sometimes early sometimes later.

second problem is, RemoteServiceException : Context.startForegroundService() did not then call Service.startForeground() , I cant understand why I get this exception

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        Intent intent1 = new Intent(MainActivity.this, AlarmReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
        AlarmManager am = (AlarmManager) MainActivity.this.getSystemService(MainActivity.this.ALARM_SERVICE);
        am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 60 * 60 * 1000L, pendingIntent);
    }
}

AlarmReceiver

public class AlarmReceiver extends BroadcastReceiver {

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onReceive(Context context, Intent intent) {

        Intent downloadService = new Intent(context, CallService.class);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(downloadService);
        } else {
            context.startService(downloadService);
        }

    }

}

CallService

public class CallService extends IntentService {

    public CallService() {
        super("");
    }

    public CallService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(Intent workIntent) {
        Log.d("Now : ", Calendar.getInstance().getTime().toString());



    }
}

Manifest

 <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">

        <receiver android:name=".AlarmReceiver"/>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service
            android:name=".CallService"
            android:exported="false"/>

Sorry for my terrible english. Thanks for any help or suggestion. I dont know how can I do different way. I just only call a service periodically


回答1:


AlarmManager will be works normally on api 27 (oreo 8.1) and less. Just setup with something like that:

public void setupAlarm(Context context, long triggerAtMillis) {
    PendingIntent pendingIntent = getPendingIntent(context);
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        alarmManager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmIntentBuilder.buildPendingIntent(context, uri));
    } else if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmIntentBuilder.buildPendingIntent(context, uri));
    } else {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmIntentBuilder.buildPendingIntent(context, uri));
    }
}

Don't forget that you need to setup your alarm again every time when your AlarmReceiver triggered. But that code will not be working on api 28 and more (pie 9.0). This is happening because of Power management feature introduced in Android Pie. Strict restrictions are introduced on the apps running in background. These restrictions are explained here. Try to use JobScheculer to invoke your foreground IntentService.

As about your second question. You need to make a notification in your CallService. Just add this onCreate to your IntentService:

@Override
public void onCreate() {
    super.onCreate();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel chan = new NotificationChannel("test", "your_channel_name", NotificationManager.IMPORTANCE_NONE);
        NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        if (manager != null) {
            manager.createNotificationChannel(chan);
        }

        Notification.Builder notificationBuilder = new Notification.Builder(context, "test");
        notificationBuilder.setOngoing(true);
        notificationBuilder.setContentTitle("Updatig")
                .setContentText("Wait for finish updating")
        startForeground(NOTIFICATION_ID, notificationBuilder.build());
    }
}

RemoteServiceException will be thrown if you will not call startForeground in service (you have 5 seconds to make this call) after startForegroundService.




回答2:


I had same problem with alarm manager. You can switch to Interval of RxJava.

protected fun startCountdown(period: Long, timeUnit: TimeUnit): Observable<Long> {
        return Observable.interval(period, timeUnit)
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
    }

usage;

startCountdown(MEDIA_LOCATION_CHANGE_PERIOD, TimeUnit.MILLISECONDS)
        .subscribe {
            fireSomethingUp()
        }


来源:https://stackoverflow.com/questions/54007289/cannot-do-work-in-background-periodically-with-alarmmanager

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!