平台

RK3288 + Android 7.1

问题

在测试Thread.sleep过程中发现, 当App进入后台后, 服务中的Thread.sleep会有不同程度的精确度丢失.
测试sleep 2ms, 当置于后台时, 实际延迟达到 [10 - 40] ms

相关测试代码:

|-- AndroidManifest.xml

    <applicationandroid:allowBackup="true"android:icon="@mipmap/ic_factory_test"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"android:directBootAware="true"><activity android:name="MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /><category android:name="android.intent.category.DEFAULT"/></intent-filter></activity><service android:name="NormalService"/></application>

|-- layout/main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><Button android:id="@+id/btStart"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/start"/>
</LinearLayout>

|-- MainActivity.java

public class MainActivity extends Activity implements View.OnClickListener {boolean serviceStarted = false;Button btStart;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.service_test);btStart = (Button)findViewById(R.id.btStart);btStart.setOnClickListener(this);}@Overridepublic void onClick(View view) {if(view.getId() == R.id.btStart){if(serviceStarted){btStart.setText(R.string.start);stopService(new Intent(this, NormalService.class));serviceStarted = false;}else{btStart.setText(R.string.stop);startService(new Intent(this, NormalService.class));serviceStarted = true;}}}
}

|-- NormalService.java

public class NormalService extends Service {final String TAG = "NormalService";@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {testSleep();return super.onStartCommand(intent, flags, startId);}void testSleep(){new Thread(TAG){@Overridepublic void run() {try{sleep(5000);for(int i = 0; i < 30; i ++){sleep(2);Global.d(TAG, "testSleep");}}catch(Exception e){}}}.start();}
}

测试过程

正常

  1. 打开Activity
  2. 点击按键启动服务.
  3. 等待线程执行并打印结果.
2019-05-09 13:55:42.295  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.298  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.301  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.305  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.308  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.312  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.315  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.318  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.322  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.325  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.328  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.332  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.336  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.339  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.342  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.346  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.349  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.352  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.356  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.359  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.362  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.366  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.369  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.371  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.374  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.377  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.381  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.383  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.386  D/NormalService: ALog > testSleep
2019-05-09 13:55:42.389  D/NormalService: ALog > testSleep

延迟

  1. 打开Activity
  2. 点击按键启动服务.
  3. 点击HOME 键退出到桌面.
  4. 等待线程执行并打印结果.
2019-05-09 13:53:49.313  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.354  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.396  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.440  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.459  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.501  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.544  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.560  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.572  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.583  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.596  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.606  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.618  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.629  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.641  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.653  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.664  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.676  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.723  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.746  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.758  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.769  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.781  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.793  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.805  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.816  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.828  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.839  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.851  D/NormalService: ALog > testSleep
2019-05-09 13:53:49.862  D/NormalService: ALog > testSleep

解决方法

以上问题是后台进程的CPU调度优先级降低导致.

关于CPU调度请参考:
随笔之Android平台上的进程调度探讨-https://blog.csdn.net/innost/article/details/6940136
[RK3288][Android6.0] CPU频率调度策略小结-https://blog.csdn.net/kris_fei/article/details/78016996

解决方法可参考:
Android Foreground Service (前台服务)-http://www.cnblogs.com/renhui/p/8575299.html

关键代码:

// 参数一:唯一的通知标识;参数二:通知消息。
startForeground(110, notification);// 开始前台服务

附源码相关
|-- frameworks/base/core/java/android/app/Service.java

    public final void startForeground(int id, Notification notification) {try {mActivityManager.setServiceForeground(new ComponentName(this, mClassName), mToken, id,notification, 0);} catch (RemoteException ex) {}}

|-- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    @Overridepublic void setServiceForeground(ComponentName className, IBinder token,int id, Notification notification, int flags) {synchronized(this) {mServices.setServiceForegroundLocked(className, token, id, notification, flags);}}

|-- frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

    public void setServiceForegroundLocked(ComponentName className, IBinder token,int id, Notification notification, int flags) {final int userId = UserHandle.getCallingUserId();final long origId = Binder.clearCallingIdentity();try {ServiceRecord r = findServiceLocked(className, token, userId);if (r != null) {if (id != 0) {if (notification == null) {throw new IllegalArgumentException("null notification");}if (r.foregroundId != id) {cancelForegroudNotificationLocked(r);r.foregroundId = id;}notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;r.foregroundNoti = notification;r.isForeground = true;r.postNotification();if (r.app != null) {updateServiceForegroundLocked(r.app, true);}getServiceMap(r.userId).ensureNotStartingBackground(r);mAm.notifyPackageUse(r.serviceInfo.packageName,PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);} else {if (r.isForeground) {r.isForeground = false;if (r.app != null) {mAm.updateLruProcessLocked(r.app, false, null);updateServiceForegroundLocked(r.app, true);}}if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {cancelForegroudNotificationLocked(r);r.foregroundId = 0;r.foregroundNoti = null;} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {r.stripForegroundServiceFlagFromNotification();if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {r.foregroundId = 0;r.foregroundNoti = null;}}}}} finally {Binder.restoreCallingIdentity(origId);}}private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {boolean anyForeground = false;for (int i=proc.services.size()-1; i>=0; i--) {ServiceRecord sr = proc.services.valueAt(i);if (sr.isForeground) {anyForeground = true;break;}}mAm.updateProcessForegroundLocked(proc, anyForeground, oomAdj);}

|-- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,boolean oomAdj) {if (isForeground != proc.foregroundServices) {proc.foregroundServices = isForeground;ArrayList<ProcessRecord> curProcs = mForegroundPackages.get(proc.info.packageName,proc.info.uid);if (isForeground) {if (curProcs == null) {curProcs = new ArrayList<ProcessRecord>();mForegroundPackages.put(proc.info.packageName, proc.info.uid, curProcs);}if (!curProcs.contains(proc)) {curProcs.add(proc);mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_FOREGROUND_START,proc.info.packageName, proc.info.uid);}} else {if (curProcs != null) {if (curProcs.remove(proc)) {mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_FOREGROUND_FINISH,proc.info.packageName, proc.info.uid);if (curProcs.size() <= 0) {mForegroundPackages.remove(proc.info.packageName, proc.info.uid);}}}}if (oomAdj) {updateOomAdjLocked();}}}

android Service中Thread.sleep不精确相关推荐

  1. Android Service与Thread的区别

    Android Service,后台,Android的后台就是指,它的运行是完全不依赖UI的.即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行.比如说一些应用程序 ...

  2. android service中显示一个dialog

    转自:http://blog.csdn.net/huxueyan521/article/details/8954844 dialog是依附于activity存在的.但是app中经常需要使用以下的情况, ...

  3. Android service 中的stub类

    stub是为了方便client,service交互而生成出来的代码. AIDL(android 接口描述语言)是一个IDL语言,它可以生成一段代码,可以使在一个android设备上运行的两个进程使用内 ...

  4. Android Framework中的线程Thread及它的threadLoop方法

    当初跟踪Camera的代码中的时候一直追到了HAL层,而在Framework中的代码看见了许许多多的Thread.它们普遍的特点就是有一个threadLoop方法.按照字面的意思应该是这个线程能够循环 ...

  5. Android Service完全解析,关于服务你所需知道的一切(上)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...

  6. 知识点干货--聊一聊Android中Service与Thread的区别

    古语说得好:"一寸光阴一寸金,寸金难买寸光阴."一寸光阴和一寸长的黄金一样昂贵,而一寸长的黄金却难以买到一寸光阴.比喻时间十分宝贵.此语句出自唐朝王贞白的<白鹿洞二首> ...

  7. Android 轮询最佳实践 Service + AlarmManager+Thread

    为什么80%的码农都做不了架构师?>>>    android中涉及到将服务器中数据变化信息通知用户一般有两种办法,推送和轮询. 消息推送是服务端主动发消息给客户端,因为第一时间知道 ...

  8. 既然android service是运行在主线程中的,那service还有什么用?

    既然android service是运行在主线程中的,那service还有什么用? 对于Android,每一个进程都有一个主线程,四大组件的处理任务都是在这个线程中进行的.每个线程都有一个Messag ...

  9. Android 在Service中使用bindService

    前面已经对Service的startServer方式启动一个服务了解过了,现在来看一下Service的另一种启动方式→bindServer bindServer使用场景 1.在同个app之间调用(即是 ...

最新文章

  1. 科大讯飞语音助手Lite智能鼠标电脑版安装不成功为什么?怎么办?
  2. Sql server profiler抓出的语句不可信
  3. CYP音乐emlog主题免费版 炫酷黑色主题
  4. Python编码规范和Python风格规范
  5. Python基础 —— sys 模块
  6. 微型计算机工作原理详细,微型计算机基本工作原理.ppt
  7. Qt 实现 QQ 截图工具(开源OEasyScreenshot)
  8. 数字孪生智慧高铁研究案例
  9. Oracle8.1.7 报错01033,win10系统下oracle数据库报错ORA-01033如何解决
  10. cpp-http 库的使用
  11. SAP 未审批的采购订单(PO)提交到OA去审批,最后OA审批结果回写到SAP。
  12. 目前见到的最傻瓜全面的STRUTS入门教程^_^
  13. vipkid怎么样?来自家长的真实评价
  14. 分布式 - ElasticSearch解决大数据量检索难题
  15. 【从零开始vnpy量化投资】一. vnpy初探 - 注册、安装、运行策略
  16. gpu-z中的几个上限
  17. MyBatis-Plus分页查询where后面的参数拼接错误报### The error occurred while setting parameters
  18. Java简单语句项目练习——英雄联盟商城
  19. replication_applier_configuration、replication_connection_configuration和replication_connection_status
  20. php两张图片动态合成thinkphp实现二维码及文字水印合并拼接到背景图上

热门文章

  1. 微软悬赏25万美元捉拿蠕虫病毒作者
  2. 咕咚为何值1.5亿美元?
  3. Google 测试总监聊如何经营成功的测试职业生涯
  4. Centos7 -- glibc 升级失败、意外删除、故意删除后的处理方法
  5. sql_mode中的STRICT_TRANS_TABLES和STRICT_ALL_TABLES区别
  6. 数据库MySQL面经——一篇掌握MySQL(MySQL详细知识点)
  7. 常见网络安全设备默认口令
  8. postgres ,Lateral查询
  9. 如何实现用将富文本编辑器内容保存为txt文件并展示
  10. 群晖的RAID类型:basic、shr、raid1、jbod、raid0、raid5