关于android O之后前台服务crash的问题

探讨Android O及后续版本中前台服务启动机制变化,详解startForegroundService与startForeground使用要点,分享避免服务异常终止的策略,特别关注小米等设备上的稳定性提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于android O之后前台服务crash的问题

android O 之后系统提供startForegroundService(Intent service)来开启前台服务

官方原文:Similar to startService(android.content.Intent), but with an implicit promise that the Service will call startForeground(int, android.app.Notification) once it begins running. The service is given an amount of time comparable to the ANR interval to do this, otherwise the system will automatically stop the service and declare the app ANR.
Unlike the ordinary startService(android.content.Intent), this method can be used at any time, regardless of whether the app hosting the service is in a foreground state.

翻译:跟startService(Intent)相似,但不同的是,service通过这个方法启动之后需要在一定时间内马上调用startForegrond()方法,而这个时间跟系统出现ANR的时间一致,否则,系统将会自动停止service,并出现ANR。跟传统的startService()方法不同,这个方法可以在任何时候使用,不管启动service的应用是否在前台。

实际上,官方描述的只是出现ANR的情况,在指定时间内(5s)未调用startForegrond()就会出现ANR,但是如果在调用startForegrond()之前服务已经被杀死了,那么会出现crash:android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()

换句话说,现在无论应用处于前后台都可以通过startForegroundService()方法来启动服务,只是需要在服务一启动的时候,就调用startForeground()方法,也就是说,我们需要在服务的onCreate()中调用startForeground(),如下:

public void onCreate() {
	... //创建Notification
	startForeground(id, notification);
}

不过以防万一,最好在onStartCommand()中也调用startForeground()

public void public int onStartCommand(Intent intent, int flags, int startId) { {
	... //前面创建的Notification
	startForeground(id, notification);
}

针对出现crash的情况,目前谷歌官方觉得这是在预期之内的,并不算bug,但是对于没写厂商的手机,出现crash的情况还是很高的,比如小米。目前借鉴网上大佬的方法是:

  • 当应用处于前台的时候,调用startservices()方法来启动服务
  • 当应用处于后台的时候,可以创建一个一像素的activity讲应用换到前台,任何再调用startservices()来启动服务

具体代码如下:

//方法一:适用于应用没有启动activity的情况,比如说,被第三方应用拉起,只执行application差不多
//判断应用是否处于前台
public static boolean isAppForeground(Context context) {
        final ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        if (manager != null) {
            final List<ActivityManager.RunningAppProcessInfo> taskInfoList = manager.getRunningAppProcesses();
            for (int i = 0; i < taskInfoList.size(); i++) {
            	//"com.runningman.test"为包名
                if ("com.runningman.test".equals(taskInfoList.get(i).processName)) {
                    if (taskInfoList.get(i).importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                        return false;
                    } else {
                        return true;
                    }
                }
            }
        }
        return false;
    }

//方法二:适用于应用有启动activity的情况
//也可以通过application的registerActivityLifecycleCallbacks方法来判断
boolean isAppForeground = false;
application.registerActivityLifecycleCallbacksnew SimpleActivityLifecycleCallbacks() {

           @Override
            public void onActivityStarted(Activity activity) {
                super.onActivityStarted(activity);
                isAppForeground = false;
            }
           
            @Override
            public void onActivityStopped(Activity activity) {
                super.onActivityStopped(activity);
                isAppForeground = true;
            }
        }

//最后通过以下方式启动service
public static void start(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (isAppForeground(context)) {
                Intent intent = new Intent(context, TestService.class);
                context.startService(intent);
            } else {
                ServiceActivity.start(context);   //一像素Activity
            }
        } else {
            Intent intent = new Intent(context, TestService.class);
            context.startService(intent);
        }
    }

//ServiceActivity.class
public class ServiceActivity extends AppcompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        //设置1像素
        Window window = getWindow();
        window.setGravity(Gravity.START | Gravity.TOP);
        WindowManager.LayoutParams params = window.getAttributes();
        params.x = 0;
        params.y = 0;
        params.height = 1;
        params.width = 1;
        window.setAttributes(params);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, TestService.class);
        startService(intent);
        finish();
    }
	public static void start(Context context) {
	        Intent intent = new Intent(context, ServiceActivity.class);
	        context.startActivity(intent);
	}
}

使一像素Activity透明化

<style name="service_Activity" parent="Theme.AppCompat.Light.NoActionBar">//无标题
        <item name="android:windowIsTranslucent">true</item>//透明
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值