如何在Android服务上实现精确(准确到秒)的时间

时间:2015-10-16 00:37:05

标签: android multithreading service

我正在创建一个按时间间隔执行操作的应用。我可以在更新之间等待的绝对最长时间是30秒,在0和0之间的任何东西都可以接受,但我更喜欢15秒以获得良好的测量。然而,它并不像听起来那么容易。我尝试了4种方法,由于种种原因,所有方法都是不可接受的。

请记住Service中出现这些问题,当此代码在Activity中运行时,它运行正常。我还注意到,当手机插入我的电脑进行调试时,解决方案3运行完全正常,直到我将其拔出。我检查了我的电池设置,没有省电模式或其他类似的东西,这可能是原因。

1. IntentService做事,然后在15秒内重新安排。不幸的是,当报警准确无误时,AlarmManager.setExact()完全不可靠,正如我在这个风滚草中描述的那样:Android Alarm not triggering at exact time

2.具有Thread的地面服务。在那个帖子中我做了我的东西然后sleep() 15秒。事实证明这种方法比以前更糟糕了,该线程在适当的时间之后被唤醒了5分钟。

3.然后我尝试使用Timer(建议使用geokavel)并使用scheduleAtFixedRateschedule安排工作,但工作大约在15-45秒内完成,这使得间隔为1分钟而不是15秒。

4.我想到达到这个目的的最后一种方法是不从上方睡在前台服务中的Thread。相反,我比较时间:

public void run(){
    nextTime = System.currentTimeMillis() + sleep;
    while (true){
        if (System.currentTimeMillis() >= nextTime) {
            nextTime = System.currentTimeMillis() + sleep;
            //do stuff
        }
    }
}

除了一个主要的缺点外,这种方法就像魅力一样 - 它一直使用20-25%的CPU。

所以我的问题是,是否有一种方法可以使上述解决方案正常工作(没有不可接受的缺点)?如果没有,我错过了更好的方式吗?如果需要,请随时询问更多细节。

编辑:请求的run()代码:

public void run(){
    try {
        if (Thread.currentThread().isInterrupted()){
            throw new InterruptedException();
        }
        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info == null) {
            throw new UnknownHostException();
        }
        if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
            Log.i(TAG, "Won't use mobile connection");
            throw new UnknownHostException();
        } else {
            internetRestored();
            st.updateData();
        }
    } catch (MalformedURLException | UnknownHostException e) {
        internetFailed();
        Log.e(TAG, "No internet connection, cant log");
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        Log.i(TAG, "Thread iterrupted");
    }
}

4 个答案:

答案 0 :(得分:0)

您定位/编译哪些API?

编辑:查看处理程序:

https://developer.android.com/reference/android/os/Handler.html

  

Handler有两个主要用途:(1)安排消息和   runnables将在未来的某个点上执行; (2)至   将要在不同于您自己的线程上执行的操作排入队列。   ...   发布或发送到处理程序时,您可以在消息队列准备好后立即允许处理该项目,或者在处理之前指定延迟或者处理它的绝对时间。后两者允许您实现超时,滴答和其他基于时间的行为。

我没有看到关于具体时间的任何警告,所以这可能是一个很好的镜头。

一些有趣的文档:

https://developer.android.com/reference/android/app/AlarmManager.html

  

否则,警报将被设置为应用程序调用setRepeating(int,long,long,PendingIntent)。从API 19开始,所有重复警报都将是不精确的,并且无论其规定的重复间隔如何,都可以与其他警报进行批处理。

来自Alarm Manager的更多信息:

  

注意:从API 19(KITKAT)开始,警报传递是不准确的:操作系统   将移动警报以最小化唤醒和电池使用。那里   是支持需要严格交付的应用程序的新API   担保;请参阅setWindow(int,long,long,PendingIntent)和   setExact(int,long,PendingIntent)。应用程序   targetSdkVersion早于API 19将继续看到   以前的行为,其中所有警报都在何时传递   请求。

https://developer.android.com/reference/java/lang/Thread.html

  

使发送此消息的线程休眠给定的   时间间隔(以毫秒和纳秒为单位)。该   不保证精度 - 线程可能睡眠多于或少于   请求。

计时器:

  

此类不提供有关任务调度的实时性质的保证。

的ScheduledThreadPoolExecutor

  

延迟任务在启用之前执行,但没有任何执行   关于何时启用后,他们会实时保证   开始。计划完成相同执行时间的任务是   以先进先出(FIFO)提交顺序启用。

一些想法:

根据我所见,您最好的选择是降低目标API <&lt; 19并使用Alarm Manager。

如果这不起作用,我的下一个最佳选项是thread.sleep(),同时使用Thread.setPriority()将线程执行优先级设置为尽可能高;

如果不这样做,我会研究减少实时需求的方法 - 事实上,android处理处理器时间的方式很复杂,而且它并不是真正的实时系统。

如果您真的需要非常准确的计时,您可以探索C并尝试在AudioFastPath中执行您的代码,这会使您在处理器时间内尽可能保持规律性。多数民众赞成可能比杀人更有害。

答案 1 :(得分:-1)

钽沓! https://developer.android.com/reference/java/util/Timer.html

Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
    public void run() {
        //code to run
    }
},initial_delay,period);

答案 2 :(得分:-1)

Handler minuteHandler = new Handler();
minuteHandler.postDelayed(runnable, 60000);
final Runnable runnable = new Runnable() {
@Override
public void run() {
  // your runnable code
  minuteHandler.removeCallbacks(runnable);
  minuteHandler.postDelayed(runnable, 60000);
  }
};

答案 3 :(得分:-2)

如果Timers真的不适合你,那么最基本的实现就是这样的。您无需为此设置新服务:

$("#timepicker").kendoTimePicker({
    dates: [
        new Date(2000, 10, 10, 10, 0, 0),
        new Date(2000, 10, 10, 10, 30, 0)
    ] //the drop-down list will consist only two entries - "10:00 AM" and "10:30 AM"
});

除非你在while循环中放入一个变量,或者添加一些条件和/或break语句,否则这将永远存在;