最全实战_Android后台启动Activity实践之路,春招的面试时间

尾声

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

Android P后台启动权限

在小米 Max3 上测试发现了两种方式可以实现从后台启动 Activity 界面,其系统是基于 Android 9 的 MIUI 系统。

方式一:moveTaskToFront

这种方式不算是直接从后台启动 Activity,而是换了一个思路,在后台启动目标 Activity 之前先将应用切换到前台,然后再启动目标 Activity,如果有必要的话,还可以通过 Activity.moveTaskToBack 方法将之前切换到前台的 Activity 重新移入后台,经过测试,在 Android 10 上这个方法已经失效了…但是 10 以下的版本还是可以抢救一下的(需要声明 android.permission.REORDER_TASKS 权限)。

启动目标 Activity 之前先判断一下应用是否在后台,判断方法可以借助 ActivityManager.getRunningAppProcesses 方法或者 Application.ActivityLifecycleCallbacks 来监听前后台,这两种方法网上都有文章讲解,就不赘述了。直接贴出后台切换到前台的代码:

fun moveToFront(context: Context) {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
activityManager?.getRunningTasks(100)?.forEach { taskInfo ->
if (taskInfo.topActivity?.packageName == context.packageName) {
Log.d(“LLL”, “Try to move to front”)
activityManager.moveTaskToFront(taskInfo.id, 0)
return
}
}
}

fun startActivity(activity: Activity, intent: Intent) {
if (!isRunningForeground(activity)) {
Log.d(“LLL”, “Now is in background”)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
// TODO 防止 moveToFront 失败,可以多尝试调用几次
moveToFront(activity)
activity.startActivity(intent)
activity.moveTaskToBack(true)
} else {
NotificationUtils.sendNotificationFullScreen(activity, “”, “”)
}
} else {
Log.d(“LLL”, “Now is in foreground”)
activity.startActivity(intent)
}
}

方式二:Hook

由于 MIUI 系统不开源,因此尝试再研究研究 AOSP 源码,死马当活马医看能不能找到什么蛛丝马迹。首先从 Activity.startActivity 方法开始追,如果阅读过 Activity 启动源码流程的话可以知道 Activity.startActivity 或调用到 Instrumentation.execStartActivity 中,然后通过 Binder 调用到 AMS 相关的方法,权限认证就在 AMS 中完成,如果权限不满足自然启动就失败了(Android 10)。

// APP 进程
public ActivityResult execStartActivity(Context who, IBinder contextThread, …) {
// …
// 这里会通过 Binder 调用到 AMS 相关的代码
int result = ActivityManager.getService().startActivity(whoThread, who.getBasePackageName(),
intent, intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
// …
}

// system_server进程
// AMS
public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
// …
}

这里如果对 system_server, zygote 进程这些感兴趣的话可以参考 Android系统启动源码解读-init-zygote-system_server。看一下这几个参数:

  • caller: AMS 在完成相关任务后会通过它来 Binder 调用到客户端 APP 进程来实例化 Activity 对象并回调其生命周期方法,caller 的 Binder 服务端位于 APP 进程。
  • callingPackage: 这个参数标识调用者包名。

这里可以尝试 Hook 一些系统的东西,具体怎么 Hook 的代码先不给出了,经过测试在 Android 9 的小米设备上可以成功,有兴趣可以自行研究谈论哈,暂时不公开了,有需要的同学可以留言告诉我。或者反编译小米 ROM 源码,可以从里面发现一些东西。

Android Q后台启动权限

在上面介绍过 Android Q 版本开始原生系统也加入了后台启动的限制,通过通知设置 fullScreenIntent 可以在原生 Android 10 系统上从后台启动 Activity。查看 AOSP 源码,可以在 AMS 找到这部分后台权限限制的代码,上面讲到 startActivity 的流程,在 APP 进程发起请求后,会通过 Binder 跨进程调用到 system_server 进程中的 AMS,然后调用到 ActivityStarter.startActivity 方法,关于后台启动的限制就这这里:

// 好家伙,整整二十多个参数,嘿嘿,嘿嘿
private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
SafeActivityOptions options,
boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup,
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
// …
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
inTask != null, callerApp, resultRecord, resultStack);
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
callingPackage);

boolean restrictedBgActivity = false;
if (!abort) {
restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
originatingPendingIntent, allowBackgroundActivityStart, intent);
}
// …
}

这里的 shouldAbortBackgroundActivityStart 调用是在 Android Q 中新增的,看方法名就能菜刀这是针对后台启动的:

boolean shouldAbortBackgroundActivityStart(…) {
final int callingAppId = UserHandle.getAppId(callingUid);
if (callingUid == Process.ROOT_UID || callingAppId == Process.SYSTEM_UID
|| callingAppId == Process.NFC_UID) {
return false;
}
if (callingUidHasAnyVisibleWindow || isCallingUidPersistentSystemProcess) {
return false;
}
// don’t abort if the callingUid has START_ACTIVITIES_FROM_BACKGROUND permission
if (mService.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
return false;
}
// don’t abort if the caller has the same uid as the recents component
if (mSupervisor.mRecentTasks.isCallerRecents(callingUid)) {
return false;
}
// don’t abort if the callingUid is the device owner
if (mService.isDeviceOwner(callingUid)) {
return false;
}
// don’t abort if the callingUid has SYSTEM_ALERT_WINDOW permission
if (mService.hasSystemAlertWindowPermission(callingUid, callingPid, callingPackage)) {
Slog.w(TAG, "Background activity start for " + callingPackage

  • " allowed because SYSTEM_ALERT_WINDOW permission is granted.");
    return false;
    }
    // …
    }

从这个方法可以看到后台启动的限制和官方文档 从后台启动 Activity 的限制 中的说明是可以对应上的,这里面都是针对 uid 去做权限判断的,且是在系统进程 system_server 中完成,单纯更改包名已经没用了。。。

在一些没有针对后台启动单独做限制的 ROM 上通过 全屏通知 可以成功弹出后台 Activity 页面,比如说小米 A3,另外还有一台 vivo 和一台三星手机,具体机型忘记了;在做了限制的设备上则弹不出来,比如说红米 Note 8 Pro。

对于红米 Note 8 Pro 这块硬骨头,不停尝试了好多方法,但其实都是碰运气的,因为拿不到 MIUI 的源码,后来想转变思路,是否可以尝试从这台手机上 pull 出相关的 framework.jar 包然后反编译呢?说不定就有收获!不过需要 Root 手机,这个好办,小米自己是有提供可以 Root 的开发版系统的,于是就去 MIUI 官网找了一下,发现这台红米 Note 8 Pro 机型没有提供开发版系统(笑哭),想起来好像之前是说过低端机小米不再提供开发版了。。。好吧,手里头没有其它可以尝试的手机了。

再转念一想,是否可以直接下载稳定版的 ROM 包,解压后有没有工具能够得到一些源码相关的痕迹呢?于是下载了一个 ROM.zip 后,解压看到里面只有一些系统映像 img 文件和 .dat.br 文件,这一块我还不太懂,猜想就算能得到我想要的东西,整套流程花费的时间成本估计也超出预期了,所以暂时只能先放下这个想法了。后续有足够的时间再深入研究研究吧。

总结

原生Android ROM

Android 原生 ROM 都能正常地从后台启动 Activity 界面,无论是 Android 9(直接启动) 还是 10 版本(借助全屏通知)。

定制化ROM

检测后台弹出界面权限:

  • 通过反射 AppOpsManager 相关方法检测对应 opCode 的权限;
  • opCode = 10021(小米机型);
  • 其它机型可以尝试遍历得到 opCode;

Android P版本的小米:

  • 通过Hook相关参数来后台启动Activity,代码由于某些原因不能给出了,有需要的同学可以留言告诉我哈;
  • 只测试过小米机型,其它机型不一定可用;
  • 理论上 P 版本以下的小米应该也支持;

Android P版本的机型:

  • 通过 moveTaskToFront 方法将应用切换到前台;
  • 这种方法毕竟是官方 API,因此兼容性可能更好一些;
  • 如果切换失败的话可以多尝试几次调用 moveTaskToFront 方法;
  • 理论上 P 版本以下的机型应该也支持;
    Android Q版本的机型:
  • 通过系统全屏通知的方式调起后台 Activity;
  • 在一些另作了限制的 ROM 上可能调起失败;
    至于反编译 MIUI 代码的方式只是一个猜想,时间原因未能付诸行动。看样子产品哥哥的需求暂时不能完全实现了,不知道有没有做过相关研究(或者知道内情)的小伙伴能不能提供一些参考思路,虽然是一个比较流氓的功能,但是代码是无罪的嘿嘿,朝着一个需求目标,为此思考解决方法,并从各个方向去调研,我觉得本身是一件有意思也有提升的事情!欢迎有过相关研究的同学在评论区提出建议,做好需求奥里给。

最后

这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容

除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~

喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值