每日一问:Android 不杀进程会重走application.onCreate() 吗

Android 进程未被杀死时Application的重建现象解析
本文探讨了Android应用在进程未被杀死的情况下,如何可能导致Application的onCreate()方法重新执行的问题。通过分析启动流程、异常情况和复现步骤,指出在特定条件下,由于内存管理和Activity生命周期的影响,可能导致Application的重建。文中提供了两种解题思路,包括暴力复现和源码分析,最后总结了对此现象的理解和疑问。

主题是Android 应用在进程未被杀死的情况下,可能会重建Application 类

个人网站 - 同步文章地址

前情提要

基本页面:应用有 开屏页Splash,主页Home

正常点击icon 是从Splash -> Home

需求:为了让Home 页的操作更加流畅,需要在启动时提前做一些操作

为了方便描述,操作的事叫做 Thing

实现:考虑到App 里需要异步初始化Thing,”启动“ 这个时机改到Splash 进行

SHOW U CODE

// App
class App{
   
   
    fun onCretate(){
   
   
        Log.d("创建 app")
        Thing.init()
    }
}
// SplashActivity
class SplashAct{
   
   
    fun preDoThing(){
   
   
        Thing.env = Thing.Env()
    }
}
// Hom
那我代码是需要修改,目前的代码是这样的:package com.example.bus; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import com.example.bus.databinding.ActivityMainBinding; import com.google.android.material.bottomnavigation.BottomNavigationView; public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; private static final String SAVED_NAV_ID = "saved_nav_id"; private boolean isPermissionRequestedForMap = false; private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); BottomNavigationView navView = findViewById(R.id.nav_view); AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( R.id.navigation_home, R.id.navigation_map, R.id.navigation_settings) .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupWithNavController(navView, navController); navView.setOnItemSelectedListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.navigation_map) { requestFineLocationPermission(() -> { if (navController.getCurrentDestination().getId() != R.id.navigation_map) { navController.navigate(R.id.navigation_map); } }); return true; } navController.navigate(itemId); return true; }); // 恢复上次选中的底部菜单项 if (savedInstanceState != null) { int savedId = savedInstanceState.getInt(SAVED_NAV_ID, R.id.navigation_home); navView.setSelectedItemId(savedId); } } /** * 请求精确定位权限 */ private void requestFineLocationPermission(Runnable onGranted) { boolean hasFine = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; if (hasFine) { // ✅ 当前确实有权限 → 执行回调 onGranted.run(); return; } // ❌ 没有权限,判断是否需要展示解释说明 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { // 用户之前拒绝过,给出解释 new AlertDialog.Builder(this) .setTitle("需要精确定位权限") .setMessage("为了准确查找您附近的公交站点和车辆位置,本应用需要获取您的精确位置。\n否则将无法使用地图相关功能。\n\n请务必选择【允许】或【仅限这次】。") .setPositiveButton("去允许", (d, w) -> requestFineLocation()) .setNegativeButton("取消", null) .show(); } else { // 第次请求 或 用户勾选了“再提示” requestFineLocation(); } } /** * 发起精确位置权限请求 */ private void requestFineLocation() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { isPermissionRequestedForMap = false; boolean hasFine = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; if (hasFine) { Toast.makeText(this, "已获得精确定位权限", Toast.LENGTH_SHORT).show(); navigateToMapIfNeeded(); } else { // 权限未授予(包括“仅限这次”已失效、被拒绝等) boolean hasCoarse = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; if (hasCoarse) { // 用户选择了“大致位置” new AlertDialog.Builder(this) .setTitle("需要精确位置") .setMessage("检测到您使用的是【大致位置】,这会导致地图功能无法正常使用。\n\n" + "请在设置中将定位权限修改为【精确位置】。") .setPositiveButton("去设置", (d, w) -> openAppSettings()) .setNegativeButton("取消", null) .show(); } else { // 完全没有定位权限 Toast.makeText(this, "定位权限未授予,无法使用地图功能", Toast.LENGTH_LONG).show(); } } } } private void navigateToMapIfNeeded() { NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main); if (navController.getCurrentDestination().getId() != R.id.navigation_map) { navController.navigate(R.id.navigation_map); } } /** * 打开应用设置页面 */ private void openAppSettings() { Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); BottomNavigationView navView = findViewById(R.id.nav_view); outState.putInt(SAVED_NAV_ID, navView.getSelectedItemId()); } }
10-13
这是个非常关键的题,尤其是在使用 **小组件(App Widget)**、**通知栏服务**、**AlarmManager** 或 **JobScheduler** 等组件时:你可能从未打开过主界面(Launcher Activity),但你的 `Application.onCreate()` 却已经执行了。 --- ### ✅ 回答: > **在打开主 Activity 的情况下,`Application.onCreate()` 依然会被自动调用** —— 只要系统启动了你的应用进程来运行任何个组件(如 `Activity`、`Service`、`BroadcastReceiver`、`ContentProvider`),Android 框架就会首先创建 `Application` 实例并调用其 `onCreate()` 方法。 这意味着: 即使用户从未点击 App 图标,只要系统启动了你的配置页 Activity 或其他组件,`Application` 的初始化逻辑就会正常执行。 --- ### 🔍 详细解释 #### 1. Application.onCreate() 的触发时机 - 当系统需要运行你 App 中的任意个组件时(例如:`MyWidgetConfigureActivity`) - 如果当前还没有运行中的进程,则: 1. fork 新进程 2. 创建 `Application` 对象 3. 调用 `Application.onCreate()` 4.启动目标组件(如 Activity) 📌 所以:**`Application.onCreate()` 是整个应用生命周期的起点,它早于任何组件执行。** --- #### 2. 示例场景:从桌面添加 Widget → 启动配置页 ```mermaid sequenceDiagram participant Launcher participant SystemServer participant MyAppProcess Launcher->>SystemServer: 请求启动 com.example.MyWidgetConfigureActivity SystemServer->>MyAppProcess: 创建新进程 MyAppProcess->>MyAppProcess: 初始化 Application MyAppProcess->>MyAppProcess: 调用 Application.onCreate() MyAppProcess->>MyAppProcess: 启动 MyWidgetConfigureActivity.onCreate() MyAppProcess-->>Launcher: 显示配置页面 ``` 在这个流程中: - 用户没有打开主 Activity - 但 `Application.onCreate()` 已被执行 - 所有全局初始化代码(如第三方 SDK、数据库、SharedPreferences 缓存等)都可以在这里安全运行 --- #### 3. 验证代码示例 ##### 自定义 Application 类: ```java public class MyApplication extends Application { private static final String TAG = "MyApplication"; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "Application.onCreate() called - 初始化全局状态"); // 初始化第三方库 // MobileAds.initialize(this); // Crashlytics.getInstance(); // 全局共享数据 sInstance = this; } private static MyApplication sInstance; public static MyApplication getInstance() { return sInstance; } } ``` ##### 在 AndroidManifest.xml 中注册: ```xml <application android:name=".MyApplication" android:allowBackup="true" android:label="@string/app_name" android:supportsRtl="true"> <activity android:name=".MyWidgetConfigureActivity" android:exported="true"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" /> </intent-filter> </activity> <!-- 其他组件 --> </application> ``` ##### 在配置页中验证 Application 是否已初始化: ```java public class MyWidgetConfigureActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_widget_configure); // 此处可以安全访 Application 实例 MyApplication app = MyApplication.getInstance(); Log.d("Config", "Current app instance: " + app); // 使用全局状态或工具类 } } ``` > 💡 输出结果会显示 `Application.onCreate()` 已被调用,尽管用户从未打开主界面。 --- ### 🛠️ 如何确保某些初始化只执行次? 由于 `Application.onCreate()` 在进程启动时只会调用次,所以它是做全局初始化的最佳位置。 常见用途包括: | 初始化内容 | 示例 | |-----------|------| | 第三方 SDK | Firebase、友盟统计、极光推送 | | 数据库 | Room、GreenDAO 初始化 | | 网络框架 | OkHttp、Retrofit 单例 | | 全局上下文持有 | 用于非 Context 参数的方法 | ⚠️ 注意事项: - 要在 `Application` 中做耗时操作(如网络请求、大数据读取),会拖慢组件启动速度。 - 避免内存泄漏:要长期持有 `Activity` 引用。 - 多进程环境下,每个进程都会有自己的 `Application` 实例,需注意区分。 --- ### 🧩 特殊情况:多进程组件 如果你的 `Service` 或 `Provider` 运行在独立进程(如 `android:process=":remote"`),那么该进程也会有自己的 `Application.onCreate()` 调用! 你可以通过判断进程名来区分: ```java public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); String processName = getProcessName(); if (processName.equals(getPackageName())) { // 主进程初始化 initMainProcess(); } else if (processName.equals(getPackageName() + ":remote")) { // 子进程初始化 initRemoteProcess(); } } private String getProcessName() { ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses(); if (runningApps != null) { int myPid = android.os.Process.myPid(); for (ActivityManager.RunningAppProcessInfo processInfo : runningApps) { if (processInfo.pid == myPid) { return processInfo.processName; } } } return null; } } ``` --- ### ✅ 总结 > 即使打开主 Activity,`Application.onCreate()` 也会在系统首次启动你的应用组件(如小组件配置页)时自动执行。 > 因此,你可以放心地将全局初始化逻辑放在 `Application.onCreate()` 中,无需担心“未启动 App 就无法初始化”的题。 这是 Android 组件化架构的核心机制之。 ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值