Android 后台服务启动Actvity
创始人
2024-01-31 14:02:43

一、问题背景

相机自动化测试需求,测试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的日志,启动被终止了。

11-16 13:54:01.143  1160  8007 W ActivityTaskManager: Background activity start 
[callingPackage: com.android.camera2; callingUid: 10150; appSwitchState: 1; 
isCallingUidForeground: false; callingUidHasAnyVisibleWindow: false; 
callingUidProcState: FOREGROUND_SERVICE; isCallingUidPersistentSystemProcess: false; 
realCallingUid: 10150; isRealCallingUidForeground: false; realCallingUidHasAnyVisibleWindow: false; 
realCallingUidProcState: FOREGROUND_SERVICE; isRealCallingUidPersistentSystemProcess: false; 
originatingPendingIntent: null; allowBackgroundActivityStart: false; 
intent: Intent { act=android.intent.action.MAIN flg=0x10000000 cmp=com.android.camera2/com.android.camera.CameraActivity (has extras) }; 
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的,就是通过一个服务启动的

public class FloatingService extends Service {private static final String TAG = "CAMTEST_FloatingService";public FloatingService() {}@Overridepublic void onCreate() {super.onCreate();Log.d(TAG, "onCreate");}@Overridepublic void onDestroy() {super.onDestroy();Log.d(TAG, "onDestroy");}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {showFloatingWindow();return super.onStartCommand(intent, flags, startId);}WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();private void showFloatingWindow() {if (Settings.canDrawOverlays(this)) {// 获取WindowManager服务WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);// 新建悬浮窗控件TextView textView = new TextView(getApplicationContext());textView.setText("接口测试需要, 勿惊...");textView.setTextColor(0xffff0000);textView.setGravity(Gravity.CENTER);textView.setBackgroundColor(Color.parseColor("#FF6200EE"));textView.setOnTouchListener(new FloatingOnTouchListener());// 设置LayoutParamint screenWidth = windowManager.getDefaultDisplay().getWidth();int screenHeight = windowManager.getDefaultDisplay().getHeight();layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;layoutParams.format = PixelFormat.RGBA_8888;layoutParams.width = 500;layoutParams.height = 100;layoutParams.x = screenWidth - layoutParams.width;layoutParams.y = screenHeight - layoutParams.height;// 将悬浮窗控件添加到WindowManagerwindowManager.addView(textView, layoutParams);}}private class FloatingOnTouchListener implements View.OnTouchListener {private int x;private int y;@Overridepublic boolean onTouch(View view, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:x = (int) event.getRawX();y = (int) event.getRawY();break;case MotionEvent.ACTION_MOVE:int nowX = (int) event.getRawX();int nowY = (int) event.getRawY();int movedX = nowX - x;int movedY = nowY - y;x = nowX;y = nowY;layoutParams.x = layoutParams.x + movedX;layoutParams.y = layoutParams.y + movedY;// 更新悬浮窗控件布局WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);windowManager.updateViewLayout(view, layoutParams);break;default:break;}return false;}}
}

启动地方代码

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

相关内容

热门资讯

脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
怎么发微博故事 视频号怎么改回... 在被快手、头条、秒拍轮番定义之后,短视频还有哪些新的玩法?当今日头条孵化的抖音迅速蹿红,短视频领域的...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
怎么让男人兴奋 男人抵抗不了女... “男生对女人最初的崇拜,就是一声妈”有些东西分开挺好的,合起来就不行比如一个男人,又苏又大又强,简直...