• Android 后台服务启动Actvity


    一、问题背景

    相机自动化测试需求,测试apk通过bindService绑定相机apk里面的一个服务,通过AIDL接口的方式向相机apk发送命令,服务接收到命令之后会拉起相机的Activity。原本没有人为干预的情况下是可以拉起这个Activity的,但是拉起Activity之前,我们如果按下Home键,让测试apk退出的话,后台服务就无法拉起Activity了。经过调查发现Android10 之后做了这个限制

    https://developer.android.google.cn/guide/components/activities/background-starts

     二、项目需求框架

    ITestApp通过bindService连接到ITestAppService,是跨进程的。

     ITestAppService是我们的相机应用里面的一个服务,主要是响应外部应用的命令;接收到外部应用的命令之后调用相机内部代码。

    其中第一条命令一般就是启动 CameraActivity,就是后台服务启动Activity。

    三、分析无法启动原因

    当按了Home键之后,后台服务就无法启动相机Activity了,以下是ActivityTaskManager的日志,启动被终止了。

    1. 11-16 13:54:01.143 1160 8007 W ActivityTaskManager: Background activity start
    2. [callingPackage: com.android.camera2; callingUid: 10150; appSwitchState: 1;
    3. isCallingUidForeground: false; callingUidHasAnyVisibleWindow: false;
    4. callingUidProcState: FOREGROUND_SERVICE; isCallingUidPersistentSystemProcess: false;
    5. realCallingUid: 10150; isRealCallingUidForeground: false; realCallingUidHasAnyVisibleWindow: false;
    6. realCallingUidProcState: FOREGROUND_SERVICE; isRealCallingUidPersistentSystemProcess: false;
    7. originatingPendingIntent: null; allowBackgroundActivityStart: false;
    8. intent: Intent { act=android.intent.action.MAIN flg=0x10000000 cmp=com.android.camera2/com.android.camera.CameraActivity (has extras) };
    9. callerApp: ProcessRecord{a469c90 22598:com.android.camera2/u0a150}; inVisibleTask: false]

    反正就是一堆启动的条件都没有满足,所以终止了。

    拦截的逻辑就在系统ActivityStarter.java的这个方法里面,反正就是一个条件都不满足了。

    四、相机应用解决办法、

    AMS WMS的相关代码太复杂,没有过多时间仔细研究。简单看了一下shouldAbortBackgroundActivityStart里面返回false的逻辑,有些还是很好理解的。

    像这一段表明只要在相机应用的AndroidManifest.xml文件里面加上android:sharedUserId=“android.uid.system“ 这一行就可以了,实际验证之后发现确实可行。

     

     像这一段 说明只要应用有SYSTEM_ALERT_WINDOW 权限就可以了,实测也是可以的

     五、测试应用解决办法

    如果不想在被测应用加权限/设置android:sharedUserId,也可以在测试端想办法,我们的做法是在测试应用里面加一个浮窗,那么应用退出的时候浮窗还在,还能继续启动被测应用的activity。

    浮窗代码也是copy的,就是通过一个服务启动的

    1. public class FloatingService extends Service {
    2. private static final String TAG = "CAMTEST_FloatingService";
    3. public FloatingService() {
    4. }
    5. @Override
    6. public void onCreate() {
    7. super.onCreate();
    8. Log.d(TAG, "onCreate");
    9. }
    10. @Override
    11. public void onDestroy() {
    12. super.onDestroy();
    13. Log.d(TAG, "onDestroy");
    14. }
    15. @Nullable
    16. @Override
    17. public IBinder onBind(Intent intent) {
    18. return null;
    19. }
    20. @Override
    21. public int onStartCommand(Intent intent, int flags, int startId) {
    22. showFloatingWindow();
    23. return super.onStartCommand(intent, flags, startId);
    24. }
    25. WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
    26. private void showFloatingWindow() {
    27. if (Settings.canDrawOverlays(this)) {
    28. // 获取WindowManager服务
    29. WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    30. // 新建悬浮窗控件
    31. TextView textView = new TextView(getApplicationContext());
    32. textView.setText("接口测试需要, 勿惊...");
    33. textView.setTextColor(0xffff0000);
    34. textView.setGravity(Gravity.CENTER);
    35. textView.setBackgroundColor(Color.parseColor("#FF6200EE"));
    36. textView.setOnTouchListener(new FloatingOnTouchListener());
    37. // 设置LayoutParam
    38. int screenWidth = windowManager.getDefaultDisplay().getWidth();
    39. int screenHeight = windowManager.getDefaultDisplay().getHeight();
    40. layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    41. layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    42. layoutParams.format = PixelFormat.RGBA_8888;
    43. layoutParams.width = 500;
    44. layoutParams.height = 100;
    45. layoutParams.x = screenWidth - layoutParams.width;
    46. layoutParams.y = screenHeight - layoutParams.height;
    47. // 将悬浮窗控件添加到WindowManager
    48. windowManager.addView(textView, layoutParams);
    49. }
    50. }
    51. private class FloatingOnTouchListener implements View.OnTouchListener {
    52. private int x;
    53. private int y;
    54. @Override
    55. public boolean onTouch(View view, MotionEvent event) {
    56. switch (event.getAction()) {
    57. case MotionEvent.ACTION_DOWN:
    58. x = (int) event.getRawX();
    59. y = (int) event.getRawY();
    60. break;
    61. case MotionEvent.ACTION_MOVE:
    62. int nowX = (int) event.getRawX();
    63. int nowY = (int) event.getRawY();
    64. int movedX = nowX - x;
    65. int movedY = nowY - y;
    66. x = nowX;
    67. y = nowY;
    68. layoutParams.x = layoutParams.x + movedX;
    69. layoutParams.y = layoutParams.y + movedY;
    70. // 更新悬浮窗控件布局
    71. WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    72. windowManager.updateViewLayout(view, layoutParams);
    73. break;
    74. default:
    75. break;
    76. }
    77. return false;
    78. }
    79. }
    80. }

    启动地方代码

    1. public void startFloatingService(View view) {
    2. if (!Settings.canDrawOverlays(this)) {
    3. Toast.makeText(this, "当前无权限,请授权", Toast.LENGTH_SHORT);
    4. startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), 0);
    5. } else {
    6. Log.d(TAG, "startFloatingService startService");
    7. startService(new Intent(MainActivity.this, FloatingService.class));
    8. }
    9. }

  • 相关阅读:
    大带宽服务器的作用有哪些?
    科技的成就(三十四)
    【PTA-训练day3】L2-014 列车调度 + L1-009 N个数求和
    为什么曲面函数的偏导数可以表示其曲面的法向量?
    Stable Diffusion被爆包含性别、种族歧视!比AI更可怕的是人类的偏见......
    如何使用Mondo Rescue备份及恢复Linux系统(制作ISO镜像,成功恢复)
    JVMRandom不可设置种子|问题追溯|源码追溯
    网络协议--链路层
    如何优雅的定义统一响应对象
    百万年薪架构师谈:掌握这【6+2】学习路线 进BAT拿月薪40k真不难
  • 原文地址:https://blog.csdn.net/cfc1243570631/article/details/127921188