Android每4秒运行一次任务

时间:2016-12-30 11:28:55

标签: java android multithreading

嗨我需要每隔4秒调用一个方法,即使设备处于休眠状态,我也使用带有服务Start_stick的警报管理器,服务名称为TransactionService。当设备处于活动状态并且每隔4秒调用一次方法时,代码运行良好,但是当屏幕被锁定且设备休眠时,调用变得不准确。所以这个方法现在每2秒调用一次,有时每1秒调用一次,5 ....

这就是我每隔4秒运行一次调用方法的方法

    AlarmManager mgr = (AlarmManager) getApplicationContext().getSystemService(
            Context.ALARM_SERVICE);
        Intent notificationIntent = new Intent(getApplicationContext(),
                TransactionService.class);
        PendingIntent pendingIntent = PendingIntent.getService(
                getApplicationContext(), 0, notificationIntent, 0);
        mgr.setRepeating(AlarmManager.RTC_WAKEUP,
                System.currentTimeMillis(), 4000, pendingIntent);

这是设备处于活动状态并且屏幕处于打开状态时调用方法的日志

12-30 13:23:00.565 17397-17479/com.ids.simcardrefill D/url: calling
 12-30 13:23:04.565 17397-17537/com.ids.simcardrefill D/url:calling
 12-30 13:23:08.565 17397-17411/com.ids.simcardrefill D/url:calling
 12-30 13:23:12.565 17397-17655/com.ids.simcardrefill D/url:calling

这就是设备休眠时方法的调用方式

12-30 13:09:12.565 17397-17655/com.ids.simcardrefill D/url:calling
12-30 13:09:17.785 17397-17598/com.ids.simcardrefill D/url:calling
12-30 13:09:20.565 17397-17479/com.ids.simcardrefill D/url:calling
12-30 13:09:25.775 17397-17537/com.ids.simcardrefill D/url:calling
12-30 13:09:28.565 17397-17411/com.ids.simcardrefill D/url:calling

这里调用之间的区别是不准确的:2秒,5秒,3秒

这就是服务的样子:

public int onStartCommand(Intent intent, int flags, int startId) {


    mshared = PreferenceManager
            .getDefaultSharedPreferences(getApplicationContext());
    edit = mshared.edit();
    hostname = mshared.getString(
            getApplicationContext().getString(R.string.hostname), "0");
    contin = true;
    cost
   = mshared.getString(getString(R.string.test), "0.09");
            if (contin) {
                getTransactions get = new    getTransactions(getApplicationContext());
                get.execute(hostname);
            }

    return START_STICKY;
}

`

任何解决方案??

2 个答案:

答案 0 :(得分:3)

您应该为后台工作提供服务:https://developer.android.com/guide/components/services.html

您应该使用try { // Register the user in the system // Process the credit card through Stripe // If process fails throw custom exception }catch( CustomException $e) { //revert the Register process //Throw exception } catch (\Exception $e) { // Throw exception with error message } 来实现Handler功能。

every 4 second
编辑:我已经尝试了下面的代码,它适用于我

<强> MyService.java

Handler handler = new Handler();
Runnable test = new Runnable() {
    @Override
    public void run() {
        //do work
        handler.post(test, 4000); //wait 4 sec and run again
    }
};

public void stopTest() {
    handler.removeCallbacks(test);
}

public void startTest() {
    handler.post(test,0); //wait 0 ms and run
}

<强>的AndroidManifest.xml

public class MyService extends Service {
    Handler handler;
    Runnable test;
    public MyService() {
        handler = new Handler();
        test = new Runnable() {
            @Override
            public void run() {
                Log.d("foo", "bar");
                handler.postDelayed(test, 100); //100 ms you should do it 4000
            }
        };

        handler.postDelayed(test, 0);
    }

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

<强> MainActivity.java

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true"></service>

请记住,如果你想要 start-stop 功能,请在我的第一个例子中获取战利品。

答案 1 :(得分:0)

正确的方法是使用Handler (正如其他答案中已经提到的那样),但我会冒昧地为它添加几点。

问题

我有类似的情况,其中AlarmManager不规律地被解雇。深入研究这个问题让我明白,由于AlarmManager操作通过保持CPU唤醒锁定来唤醒CPU并且电池密集(假设设备处于非活动状态),操作系统会尝试批处理不同的警报应用程序并在设备唤醒时触发所有待处理的警报。这导致AlarmManager的不稳定行为。该文档还指定我们不应该使用它来触发完全时间戳的事件。 Android API应该用于精确的时间间隔,例如 AlarmManager.setExact() ,但如果间隔持续时间小于一分钟,操作系统会优化自己以忽略准确性。 [未记录,但根据我的个人经历说话]

修复

我使用Handler修复了问题,在另一个答案中共享。但有一点需要注意。在处理程序被杀死的边缘情况下(由于任何原因),它不会触发自身并且您的轮询将停止。

警告

后备是保持一个AlarmManager,每分钟运行一次,以便在操作系统提前停止时再次触发Handler。所以,你每隔n秒运行一次处理程序。存储上次在SharedPreferences中调用Handler时的时间戳。每隔x分钟运行一次备用AlarmManager(理想情况下x = 5 * n,这样你就不会错过5次以上的轮询)。 AlarmManager检查Handler上次运行的时间。如果它在边距内,则AlarmManager不执行任何操作,并在x分钟后重新安排自己。如果超过x分钟,则处理程序必须已被操作系统杀死,并且AlarmManager再次启动处理程序。

添加一些代码以便为您提供上下文。

public class PollingAlarmReceiver extends WakefulBroadcastReceiver {

    final long POLLING_FREQUENCY_MARGIN = 5 * 1000; //margin kept in case the System delays any threads

    Context mContext = ServicesApp.getContext();


    /*
    Splash/BootReceiver starts the Alarm and the Handler for polling.
    The Handler starts the polling service and schedules the next run after an delay of the polling interval.
    Before starting the service, the Handler also checks when the service was last run and whether it is time for the next call or not (with a margin of 5 seconds [POLLING_FREQUENCY_MARGIN]).
    The Handler should cover all the cases and run smoothly. In case it fails, the Alarm acts as a failsafe.
    The Alarm runs at an interval of 1 minute checking when the Handler was last called.
    If it is past the time of the next scheduled call (with a margin of 5 seconds [POLLING_FREQUENCY_MARGIN]), the Alarm starts the runnable and makes the Handler queue the next run.
    */

    @Override
    public void onReceive(Context context, Intent intent) {

        if (mContext == null)
            mContext = ServicesApp.getContext();

        if (mContext == null)
            mContext = context.getApplicationContext();

        if (mContext != null) {
            if (getLastPolledTimestamp(mContext) > 0 && (System.currentTimeMillis() > (POLLING_FREQUENCY_MARGIN +  getPollingInterval(mContext) + getLastPolledTimestamp(mContext)))) {
                startPollingHandler();
            }
        }
    }


    Runnable mPoller = new Runnable() {
        @Override
        public void run() {

            if (mContext == null)
                mContext = ServicesApp.getContext();
            if (mContext != null) {
                try {
                    if ((System.currentTimeMillis() >= (getPollingInterval(mContext)) - POLLING_FREQUENCY_MARGIN + getLastPolledTimestamp(mContext))) {
                        if (!isServiceRunning(PollingService.class, mContext)) {
                            mContext.getSharedPreferences(CommonLib.APP_SETTINGS, 0).edit().putLong(LAST_POLLED_TIMESTAMP, System.currentTimeMillis()).commit();
                            Intent service = new Intent(mContext, PollingService.class);
                            startWakefulService(mContext, service);
                        }
                    }
                } finally {
                    ServicesApp.getHandler().postDelayed(mPoller, getPollingInterval(mContext));
                }
            }
        }
    };

    public void startAlarmToCheckForHandler() {
        if (mContext == null)
            mContext = ServicesApp.getContext();
        if (mContext != null) {
            AlarmManager alarmMgr = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
            Intent intent = new Intent(mContext, PollingAlarmReceiver.class);
            PendingIntent alarmIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
            alarmMgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 60 * 1000, alarmIntent);

        }
    }

    public void startPollingHandler() {
        mPoller.run();

    }

    public void cancelAlarm() {
        if (mContext == null)
            mContext = ServicesApp.getContext();
        if (mContext != null) {
            AlarmManager alarmMgr = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
            Intent intent = new Intent(mContext, PollingAlarmReceiver.class);
            PendingIntent alarmIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
            alarmMgr.cancel(alarmIntent);
        }
    }
}

<强> P.S。 :我有这个代码在生产中运行数千个设备,其主要功能依赖于轮询的准确性,它似乎对我很有用。

相关问题