平台
RK3288 + Android 7.1
问题
在测试Thread.sleep过程中发现, 当App进入后台后, 服务中的Thread.sleep会有不同程度的精确度丢失.
测试sleep 2ms, 当置于后台时, 实际延迟达到 [10 - 40] ms
相关测试代码:
|-- AndroidManifest.xml
<application android: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; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.service_test); btStart = (Button)findViewById(R.id.btStart); btStart.setOnClickListener(this); } @Override public 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"; @Override public IBinder onBind(Intent intent) { return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { testSleep(); return super.onStartCommand(intent, flags, startId); } void testSleep(){ new Thread(TAG){ @Override public 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
@Override public 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(); } } }