Android Frameworks系列(一) startService启动

本文详细剖析了Android中startService的启动过程,从客户端进程的startService开始,经过ActivityManagerService(AMS)的管理,涉及到Binder机制的跨进程通信。讲解了ActivityManagerNative、ActivityManagerProxy、ApplicationThreadProxy等关键类的作用,以及ServiceRecord和服务创建的过程,最终到目标进程中Service的onCreate方法执行,阐述了整个Service启动的生命周期。

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

  之前写过一篇从AIDL看Android跨进程通信 从Java层次去分析AIDL运行的原理,当时主要是为了学习Binder机制而写的,但是Binder机制比较复杂,打算过段时间单独的写一篇来分析。本篇文章分析startService的启动过程,也会涉及到使用Binder机制来进行跨进程通信,但是不会分析Binder机制的细节。不过还是强烈建议大家学习Binder机制,至少要了解Binder的基本架构,Android经常使用Binder来进行跨进程通信。
  在Android中启动Service和Activity统一由ActivityManagerService(简称AMS)管理,AMS在系统启动时,就已经在ServiceManager中注册了,存活于system_server进程中。先来了解一下ActivityManagerService相关的类:
  
  从类图可以看到这是典型的Binder机制,如果使用过AIDL的话,对IBinder,IInterface应该很熟悉。IActivityManager继承自IInterface,是用来管理Activity和Service的接口。ActivityManagerProxy从名字可以看出其是代理,并且是AMS在普通应用进程中的代理,通过它可以使用AMS的功能。ActivityManagerNative则可以看做AMS和Binder之间的中介,这样AMS就可以通过它向客户端提供服务了。

  一 :从客户进程启动Service
  我们还是使用从AIDL看Android跨进程通信 中的例子,从MainActivity来调用startService,一步一步分析:
  1.1 ContextWrapper.java
  

@Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }

  这里的mBase实际是ContextImpl的实例,所以接着看
  
  1.2 ContextImpl.java
  
  /frameworks/base/core/java/android/app/ContextImpl.java
  

@Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, mUser);
    }
...省略...
private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());
            if (cn != null) {
                if (cn.getPackageName().equals("!")) {
                    throw new SecurityException(
                            "Not allowed to start service " + service
                            + " without permission " + cn.getClassName());
                } else if (cn.getPackageName().equals("!!")) {
                    throw new SecurityException(
                            "Unable to start service " + service
                            + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

  在startServiceCommon()方法中首先验证intent是否合法,然后重点看第11~13行。想要继续跟下去就得先搞清楚ActivityManagerNative.getDefault() 是个什么鬼。

  1.3 ActivityManagerNative.java
  /frameworks/base/core/java/android/app/ActivityManagerNative.java

/**
     * Cast a Binder object into an activity manager interface, generating
     * a proxy if needed.
     */
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }

    /**
     * Retrieve the system's default/global activity manager.
     */
    static public IActivityManager getDefault() {
        return gDefault.get();
    }

...省略...

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

  第22行,将会回调Singleton的create()方法,对应28~38行代码,第29行通过ServiceManager.getService("activity") 可以获取一个IBinder,AMS在系统启动的时候已经在ServiceManager中注册过,这里我们只用知道通过这个IBinder对象(具体是BinderProxy对象)我们可以发起远程调用。通过第15行生成了一个ActivityManagerProxy并且将其作为单例保存起来,下次调用可以直接使用了。
  ![这里写图片描述]
  
  
  上图也可以印证我们的分析。ActivityManagerNative.getDefault() 返回的是ActivityManagerProxy实例,那么1.2中将会调用ActivityManagerProxy.startService()方法。

  1.4 ActivityManagerProxy.java
  该类是在ActivityMangerNative中声明。
  

 public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        service.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeString(callingPackage);
        data.writeInt(userId);
        mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
        reply.readException();
        ComponentName res = ComponentName.readFromParcel(reply);
        data.recycle();
        reply.recycle();
        return res;
    }

  第12行mRemote是在ActivityManagerProxy的构造方法中赋值的,还记得刚刚new ActivityManagerProxy时传入了一个IBinder(BinderProxy)吗?通过它的transact()方法像远端AMS发起请求。

  二 system_server进程中AMS响应

  根据Binder机制,ActivityManagerNative的onTransact()方法将会回调,因为这个方法比较长,我只截取了响应startService动作的代码。
  
  2.1 ActivityManagerNative.java

 @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        ...省略...
        case START_SERVICE_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            Intent service = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            String callingPackage = data.readString();
            int userId = data.readInt();
            ComponentName cn = startService(app, service, resolvedType, callingPackage, userId);
            reply.writeNoException();
            ComponentName.writeToParcel(cn, reply);
            return true;
        }
        ...省略...
       }
     }

  第9行,这里将要涉及到另外一个跨进程通信,主要是干什么的呢,我们知道MainActivity所在进程A通过IActivityManager接口可以调用AMS的服务,那么如果AMS想要访问进程A的服务该怎么办呢,通过这里的IApplicationThread接口可以做到。下面是相关类图:
  
  
  
  和之前的IActivityManager类似,使用IApplicationThread也是基于Binder架构的,所以它们的结构都很像,所以这里的ApplicationThreadProxy就是A进程在AMS的一个Binder代理,通过它AMS就可以调用A进程的服务。
  第14行startService()是IActivityManager接口的方法,因为ActivityManagerNative是一个抽象类,并且AMS继承了ActivityManagerNative,所以这里startService()的真正实现在ActivityManagerService中。

  2.2 ActivityManagerService.java
  /frameworks/base/services/core/java/com/android/server/am
  

 @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        if</
<think>我们正在讨论如何将后台Service启动到前台,并正确配置通知。根据引用内容,从Android 8.0开始,后台应用启动前台服务必须显示通知。用户可能遇到了通知未正确配置的问题(如缺少通知渠道)或后台启动限制的问题。 核心步骤: 1. 在AndroidManifest.xml中声明服务(Service)和必要的权限(如前台服务权限)。 2. 使用startForegroundService()方法启动服务(在后台启动服务时)。 3. 在服务的onCreate()或onStartCommand()中,创建通知并调用startForeground(),将服务转为前台服务。 注意点: - Android 8.0及以上:必须使用startForegroundService()启动服务,然后在服务中5秒内调用startForeground(),否则会ANR并崩溃。 - 通知必须指定通知渠道(Android 8.0+)。 - 从Android 10开始,后台启动Activity受到限制,但启动前台服务(带通知)是被允许的,不过需要正确配置。 另外,引用[2]提到,在定制系统(如Android 10)中,可能去掉了后台启动Service的限制。但标准开发中,我们应遵循官方规范。 下面详细说明步骤:</think>### Android Service 后台启动到前台的方法及通知配置 在 Android 系统中,将后台 Service 启动为前台服务(带通知)是提高应用存活率的关键技术,尤其在 Android 8.0+ 版本中需严格遵循规范。以下是完整实现方案: --- #### 1. **核心实现步骤(Android 8.0+)** ```java // 步骤1: 在后台组件中启动前台服务 public void startForegroundServiceFromBackground(Context context) { Intent serviceIntent = new Intent(context, MyForegroundService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0+ 必须使用此方法 context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); } } // 步骤2: 在服务中创建通知并转为前台 public class MyForegroundService extends Service { private static final int NOTIFICATION_ID = 1001; private static final String CHANNEL_ID = "foreground_channel"; @Override public void onCreate() { super.onCreate(); createNotificationChannel(); // 创建通知渠道 startForeground(NOTIFICATION_ID, buildNotification()); } private Notification buildNotification() { // 构建通知(必须包含PendingIntent) return new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("前台服务运行中") .setContentText("点击查看详情") .setSmallIcon(R.drawable.ic_service) .setPriority(NotificationCompat.PRIORITY_LOW) .setContentIntent(createPendingIntent()) .build(); } // 创建通知渠道(Android 8.0+) private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "后台服务通知", NotificationManager.IMPORTANCE_LOW ); getSystemService(NotificationManager.class).createNotificationChannel(channel); } } } ``` --- #### 2. **关键注意事项** 1. **5秒超时限制** 从 `startForegroundService()` 调用到执行 `startForeground()` 必须在 **5秒内完成**,否则会触发 `ANR` 和崩溃: ``` android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground() ``` 2. **通知渠道要求** - Android 8.0+ 必须创建通知渠道 - 渠道ID需与通知构建器致 - 建议在 `Application.onCreate()` 中预创建渠道 3. **权限声明** 在 `AndroidManifest.xml` 中添加: ```xml <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <service android:name=".MyForegroundService" android:exported="false"/> ``` --- #### 3. **跨进程启动方案(引用[3])** 当后台服务需启动其他应用的前台服务时: ```java // 在后台服务中启动跨进程前台服务 public void startCrossProcessForegroundService() { Intent intent = new Intent(); intent.setComponent(new ComponentName( "com.example.otherapp", // 目标应用包名 "com.example.otherapp.RemoteForegroundService" // 目标服务类 )); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(intent); } else { startService(intent); } } ``` > **注意**:目标应用需声明 `FOREGROUND_SERVICE` 权限并导出服务 --- #### 4. **不同版本兼容方案** | Android 版本 | 启动方法 | 特殊要求 | |--------------|------------------------------|------------------------------| | ≤ 7.1 | `startService()` | 无通知要求 | | 8.0-9.0 | `startForegroundService()` | 必须5秒内调用`startForeground()` | | ≥ 10.0 | `startForegroundService()` | 后台启动受限[^2] | | ≥ 12 | 需添加 `FOREGROUND_SERVICE` 权限 | 通知必须包含 `PendingIntent` | --- #### 5. **常见问题解决** 1. **后台启动崩溃** ```log IllegalStateException: Not allowed to start service Intent ``` **解决方案**: - 对于系统应用:修改 `frameworks/base` 移除限制(引用[2]) - 对于普通应用:确保通过用户交互触发(如通知点击) 2. **通知不显示** - 检查渠道是否创建 - 验证通知ID是否冲突 - 确保未调用 `setSilent(true)` 3. **Android 10+ 后台启动 Activity** 需满足以下条件之: - 获取 `SYSTEM_ALERT_WINDOW` 权限 - 使用全屏通知(`setFullScreenIntent()`) - 通过用户交互(如点击通知)启动[^4] --- #### 6. **优化建议** 1. **隐藏通知技巧**(仅限低版本) ```java // Android 7.1- 可启动临时服务移除通知 public void hideNotification() { startService(new Intent(this, DummyService.class)); stopSelf(); // 立即停止服务 } ``` > **注意**:Android 8.0+ 禁止此操作,必须持续显示通知 2. **前台服务类型声明**(Android 10+) 在 `AndroidManifest.xml` 中指定服务类型: ```xml <service android:name=".MyForegroundService" android:foregroundServiceType="location|camera" /> ``` 3. **电量优化豁免** 添加 `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` 权限申请: ```java Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); ``` > 前台服务通过降低 `oom_adj` 值(通常为1)显著提高应用存活率[^5],但需平衡用户体验与资源占用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值