在Android Application类onCreate中识别应用启动来源并获取Intent
核心结论
默认无法直接在Application.onCreate()回调中获取启动Intent或者识别启动场景。Application.onCreate是应用进程创建后最先执行的系统回调,此时触发进程启动的组件(Activity、Service、BroadcastReceiver)还未执行对应生命周期,系统不会将启动Intent直接传递给Application实例。
可行实现方案
1. 全入口标记方案(兼容性最好,无适配问题)
你需要在所有可能触发应用进程创建的入口处写入启动类型标记,存储在Application的全局变量中,即可在后续任意位置直接读取:
- 静态注册的开机广播接收器:在
onReceive()回调中设置启动类型为START_TYPE_BOOT - 推送服务的接收器/服务:收到推送拉起进程时设置启动类型为
START_TYPE_PUSH - 桌面启动入口Activity:仅需要在声明了
<category android:name="android.intent.category.LAUNCHER"的入口Activity的onCreate()中,判断savedInstanceState == null(首次创建场景)时,给Application设置启动类型为START_TYPE_LAUNCHER,同时可以保存当前的启动Intent。
注:Launcher启动应用的本质就是拉起你的LAUNCHER分类Activity,所以完全不接触Activity无法实现这个判断,但以上实现仅需要在入口Activity加一行代码,不需要改造其他业务Activity。
2. 反射兜底方案(无需修改组件代码,有兼容性风险)
如果不想修改各入口组件的代码,可以通过反射系统ActivityThread类的内部字段获取进程启动的初始Intent,在Application.onCreate中直接判断:
// 仅Android 8.0及以上版本兼容性较好,定制ROM可能不支持 try { Class<?> activityThreadClz = Class.forName("android.app.ActivityThread"); Object currentActivityThread = activityThreadClz.getMethod("currentActivityThread").invoke(null); Field boundAppField = activityThreadClz.getDeclaredField("mBoundApplication"); boundAppField.setAccessible(true); Object appBindData = boundAppField.get(currentActivityThread); Field intentField = appBindData.getClass().getDeclaredField("intent"); intentField.setAccessible(true); Intent initIntent = (Intent) intentField.get(appBindData); // 判断是否为Launcher启动 if (initIntent != null && Intent.ACTION_MAIN.equals(initIntent.getAction()) && initIntent.getCategories() != null && initIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { // 匹配到Launcher启动特征 } } catch (Exception e) { // 反射失败走兜底逻辑 }
该方案的局限:
- 仅对冷启动场景有效,如果应用进程已经存活在后台,后续启动不会触发Application.onCreate,也不会更新这个初始Intent字段
- 不同系统版本、定制ROM的内部字段可能变化,反射失败概率较高
内容的提问来源于stack exchange,提问作者Moti Bartov
相关产品推荐
相关产品推荐

