android Service中Thread.sleep不精确
平台
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();}
}
测试过程
正常
- 打开Activity
- 点击按键启动服务.
- 等待线程执行并打印结果.
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
延迟
- 打开Activity
- 点击按键启动服务.
- 点击HOME 键退出到桌面.
- 等待线程执行并打印结果.
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不精确相关推荐
- Android Service与Thread的区别
Android Service,后台,Android的后台就是指,它的运行是完全不依赖UI的.即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行.比如说一些应用程序 ...
- android service中显示一个dialog
转自:http://blog.csdn.net/huxueyan521/article/details/8954844 dialog是依附于activity存在的.但是app中经常需要使用以下的情况, ...
- Android service 中的stub类
stub是为了方便client,service交互而生成出来的代码. AIDL(android 接口描述语言)是一个IDL语言,它可以生成一段代码,可以使在一个android设备上运行的两个进程使用内 ...
- Android Framework中的线程Thread及它的threadLoop方法
当初跟踪Camera的代码中的时候一直追到了HAL层,而在Framework中的代码看见了许许多多的Thread.它们普遍的特点就是有一个threadLoop方法.按照字面的意思应该是这个线程能够循环 ...
- Android Service完全解析,关于服务你所需知道的一切(上)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的A ...
- 知识点干货--聊一聊Android中Service与Thread的区别
古语说得好:"一寸光阴一寸金,寸金难买寸光阴."一寸光阴和一寸长的黄金一样昂贵,而一寸长的黄金却难以买到一寸光阴.比喻时间十分宝贵.此语句出自唐朝王贞白的<白鹿洞二首> ...
- Android 轮询最佳实践 Service + AlarmManager+Thread
为什么80%的码农都做不了架构师?>>> android中涉及到将服务器中数据变化信息通知用户一般有两种办法,推送和轮询. 消息推送是服务端主动发消息给客户端,因为第一时间知道 ...
- 既然android service是运行在主线程中的,那service还有什么用?
既然android service是运行在主线程中的,那service还有什么用? 对于Android,每一个进程都有一个主线程,四大组件的处理任务都是在这个线程中进行的.每个线程都有一个Messag ...
- Android 在Service中使用bindService
前面已经对Service的startServer方式启动一个服务了解过了,现在来看一下Service的另一种启动方式→bindServer bindServer使用场景 1.在同个app之间调用(即是 ...
最新文章
- 科大讯飞语音助手Lite智能鼠标电脑版安装不成功为什么?怎么办?
- Sql server profiler抓出的语句不可信
- CYP音乐emlog主题免费版 炫酷黑色主题
- Python编码规范和Python风格规范
- Python基础 —— sys 模块
- 微型计算机工作原理详细,微型计算机基本工作原理.ppt
- Qt 实现 QQ 截图工具(开源OEasyScreenshot)
- 数字孪生智慧高铁研究案例
- Oracle8.1.7 报错01033,win10系统下oracle数据库报错ORA-01033如何解决
- cpp-http 库的使用
- SAP 未审批的采购订单(PO)提交到OA去审批,最后OA审批结果回写到SAP。
- 目前见到的最傻瓜全面的STRUTS入门教程^_^
- vipkid怎么样?来自家长的真实评价
- 分布式 - ElasticSearch解决大数据量检索难题
- 【从零开始vnpy量化投资】一. vnpy初探 - 注册、安装、运行策略
- gpu-z中的几个上限
- MyBatis-Plus分页查询where后面的参数拼接错误报### The error occurred while setting parameters
- Java简单语句项目练习——英雄联盟商城
- replication_applier_configuration、replication_connection_configuration和replication_connection_status
- php两张图片动态合成thinkphp实现二维码及文字水印合并拼接到背景图上
热门文章
- 微软悬赏25万美元捉拿蠕虫病毒作者
- 咕咚为何值1.5亿美元?
- Google 测试总监聊如何经营成功的测试职业生涯
- Centos7 -- glibc 升级失败、意外删除、故意删除后的处理方法
- sql_mode中的STRICT_TRANS_TABLES和STRICT_ALL_TABLES区别
- 数据库MySQL面经——一篇掌握MySQL(MySQL详细知识点)
- 常见网络安全设备默认口令
- postgres ,Lateral查询
- 如何实现用将富文本编辑器内容保存为txt文件并展示
- 群晖的RAID类型:basic、shr、raid1、jbod、raid0、raid5