前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android service 进程_简述service的生命周期

android service 进程_简述service的生命周期

作者头像
Java架构师必看
发布2022-07-25 14:08:29
5230
发布2022-07-25 14:08:29
举报
文章被收录于专栏:Java架构师必看

大家好,我是架构君,一个会写代码吟诗的架构师。今天说一说android service 进程_简述service的生命周期,希望能够帮助大家进步!!!

系列博文:

Android 中service 详解

Android service 启动篇之 startService

Android service 启动篇之 bindService

Android service 启动篇之 startForegroundService

基于版本:Android O

0. 前言

通过source code 分析了AMS 中service 的启动过程,bindService 相对复杂一点,主要是多了一些service 和app的绑定关系处理。本文继续结合source code 来剖析。

1. 入口函数

1.1 bindService()

代码语言:javascript
复制
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }

    /** @hide */
    @Override
    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
            UserHandle user) {
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), user);
    }

只听到从架构师办公室传来架构君的声音:

云中天下脊,尤见此山尊。有谁来对上联或下联?

参数:

  • service 的intent
  • bind 时候的ServiceConnection
  • bind service 用到的flag,例如BIND_AUTO_CREATE
  • 当前的user

1.2 bindServiceCommon()

代码语言:javascript
复制
此代码由Java架构师必看网-架构君整理
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess(this);
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
  • ServiceConnection 不能为null,必须要创建
  • android L 之后的版本不能隐式启动service
  • 调用AMS 的bindService(),calling 的thread,calling 的package 都会传入

注意的是bindService 第 5 个参数sd,传入到AMS 不在是client 的ServiceConnection,而是ServiceDispatcher,详细可以看LoadedApk.ServiceDispatcher,每个应用都有这样的dispatcher 会注册到AMS 中,用于AMS 的回调。

代码语言:javascript
复制
    static final class ServiceDispatcher {
        private final ServiceDispatcher.InnerConnection mIServiceConnection;
        private final ServiceConnection mConnection;
        private final Context mContext;
        private final Handler mActivityThread;
        private final ServiceConnectionLeaked mLocation;
        private final int mFlags;

        private RuntimeException mUnbindLocation;

        private boolean mForgotten;

        private static class ConnectionInfo {
            IBinder binder;
            IBinder.DeathRecipient deathMonitor;
        }

        private static class InnerConnection extends IServiceConnection.Stub {
            final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            public void connected(ComponentName name, IBinder service, boolean dead)
                    throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service, dead);
                }
            }
        }

        ...
        ...

        public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
                doConnected(name, service, dead);
            }
        }

        ...
        ...

        public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            ...
            ...

            // If there was an old service, it is now disconnected.
            if (old != null) {
                mConnection.onServiceDisconnected(name);
            }
            if (dead) {
                mConnection.onBindingDied(name);
            }
            // If there is a new service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
        }

        ...
        ...
    }

最终会调用到应用中定义的ServiceConnection 的onServiceDisconnected() 和 onServiceConnected(), 分析AMS 的时候会继续说明这里用处。

2. bindServiceLocked

通过上面的bindService,最终会调用到ActiveServices 中的bindServiceLocked()

2.1 retrieveServiceLocked()

这个在 Android service 启动篇之 startService3.2 节中已经大概解释了下 ,如果app 已经service 在内部进行运行,那么需要满足下面几个条件:

  • service 需要置上flag ServiceInfo.FLAG_EXTERNAL_SERVICE
  • service 需要置上flag ServiceInfo.FLAG_ISOLATED_PROCESS
  • service 的exported 必须为true
  • app 在bind service 的时候置上flag Context.BIND_EXTERNAL_SERVICE
代码语言:javascript
复制
此代码由Java架构师必看网-架构君整理
        ComponentName name = new ComponentName(
                sInfo.applicationInfo.packageName, sInfo.name);
        if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
            if (isBindExternal) {
                if (!sInfo.exported) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                            " is not exported");
                }
                if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                            " is not an isolatedProcess");
                }
                // Run the service under the calling package's application.
                ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
                        callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
                if (aInfo == null) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
                            "could not resolve client package " + callingPackage);
                }
                sInfo = new ServiceInfo(sInfo);
                sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
                sInfo.applicationInfo.packageName = aInfo.packageName;
                sInfo.applicationInfo.uid = aInfo.uid;
                name = new ComponentName(aInfo.packageName, name.getClassName());
                service.setComponent(name);
            } else {
                throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
                        name);
            }

最终获取的ComponentName 的package 为app 的package,name 是service 的name。

2.2 变量AppBindRecord b

代码语言:javascript
复制
 AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);

来看下函数retrieveAppBindingLocked():

代码语言:javascript
复制
    public AppBindRecord retrieveAppBindingLocked(Intent intent,
            ProcessRecord app) {
        Intent.FilterComparison filter = new Intent.FilterComparison(intent);
        IntentBindRecord i = bindings.get(filter);
        if (i == null) {
            i = new IntentBindRecord(this, filter);
            bindings.put(filter, i);
        }
        AppBindRecord a = i.apps.get(app);
        if (a != null) {
            return a;
        }
        a = new AppBindRecord(this, i, app);
        i.apps.put(app, a);
        return a;
    }

注意,后面在bringUpServiceLocked() 的时候会根据bindings 来确定是否可以bind service。

变量类型为AppBindRecord,如下:

代码语言:javascript
复制
final class AppBindRecord {
    final ServiceRecord service;    // The running service.
    final IntentBindRecord intent;  // The intent we are bound to.
    final ProcessRecord client;     // Who has started/bound the service.

    final ArraySet<ConnectionRecord> connections = new ArraySet<>();
                                    // All ConnectionRecord for this client.

retrieveServiceLocked查找到ServiceRecord之后,生成Service和Client(callerApp)之间的绑定关系AppBindRecord,AppBindRecord的字段包括service,client,intent,确定了他们之间的绑定关系。

2.3 变量 ConnectionRecord c

代码语言:javascript
复制
    ConnectionRecord c = new ConnectionRecord(b, activity,
            connection, flags, clientLabel, clientIntent);

其中的connection 是从app 中传进来的,在上面 1.2 节中已经解析过,相当于一个ServiceDispatcher。记住了每一个需要bind 到该service 的所有信息。在bind 信息有更新的时候也会通过这里的conn 进行connected() 调用。

代码语言:javascript
复制
final class ConnectionRecord {
    final AppBindRecord binding;    // The application/service binding.
    final ActivityRecord activity;  // If non-null, the owning activity.
    final IServiceConnection conn;  // The client connection.
    final int flags;                // Binding options.
    final int clientLabel;          // String resource labeling this client.
    final PendingIntent clientIntent; // How to launch the client.
    String stringName;              // Caching of toString.
    boolean serviceDead;            // Well is it?

2.4 变量 connections

代码语言:javascript
复制
    IBinder binder = connection.asBinder();
    ArrayList<ConnectionRecord> clist = s.connections.get(binder);
    if (clist == null) {
        clist = new ArrayList<ConnectionRecord>();
        s.connections.put(binder, clist);
    }
    clist.add(c);
    b.connections.add(c);
    if (activity != null) {
        if (activity.connections == null) {
            activity.connections = new HashSet<ConnectionRecord>();
        }
        activity.connections.add(c);
    }
    b.client.connections.add(c);
    if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
        b.client.hasAboveClient = true;
    }
    if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
        s.whitelistManager = true;
    }
    if (s.app != null) {
        updateServiceClientActivitiesLocked(s.app, c, true);
    }
    clist = mServiceConnections.get(binder);
    if (clist == null) {
        clist = new ArrayList<ConnectionRecord>();
        mServiceConnections.put(binder, clist);
    }
    clist.add(c);

AppBindRecord的connections字段则保存了这个client的所有ServiceConnection连接ConnectionRecord,ConnectionRecord和IServiceConnection对象是对应的。ServiceRecord也有个connections列表,但ServiceRecord的connections列表存储的是这个Service相关的所有ConnectionRecord,Service和Client之间是多对多的关系,所以其各自维护了一个connections。

2.5 bringUpServiceLocked()

代码语言:javascript
复制
    if ((flags&Context.BIND_AUTO_CREATE) != 0) {
        s.lastActivity = SystemClock.uptimeMillis();
        if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                permissionsReviewRequired) != null) {
            return 0;
        }
    }

如果bindService 的时候置上flag Context.BIND_AUTO_CREATE,那么会直接进入bringUpServiceLocked() 进行唤醒。

2.5.1 进入create 流程

Android service 启动篇之 startService3.6.1 节中我们对该函数进行的详细的分析,第一次进入唤醒的时候需要通过函数realStartServiceLocked() 进行启动,并且通过函数bumpServiceExecutingLocked(r, execInFg, "create"); 埋下create 流程ANR的炸弹,最后通过scheduleCreateService进入create 流程。

2.5.2 进入bind service 流程

接着会进入bind service 流程:

代码语言:javascript
复制
    private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
            throws TransactionTooLargeException {
        for (int i=r.bindings.size()-1; i>=0; i--) {
            IntentBindRecord ibr = r.bindings.valueAt(i);
            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
                break;
            }
        }
    }

这里的bindings 就是上面 2.2 节中创建的。

注意:

其实在start service 的时候也会进入这个流程,但是bindings 为空,所以,最终不会进入bind service 的流程。

详细的函数requestServiceBindingsLocked(r, execInFg); 分析请看下面2.6节。

2.5.3 进入start service 流程

其实在bringUpServiceLocked() 最后会通过函数:

代码语言:javascript
复制
sendServiceArgsLocked(r, execInFg, true);

进入start service 的流程,详细可以看 Android service 启动篇之 startService3.6.1.2 节,但是对于bind service 来说,在函数的最开始条件就不满足return 了。详细看Android service 启动篇之 startService3.4 节和3.6.1.1 节。

2.6 requestServiceBindingsLocked()

上一节,我们看到如果service 第一次启动的时候,会进入realStartServiceLocked()。最终会进入该函数,执行bind 的完成流程。

但是,如果service 已经启动,这个时候在bringUpServiceLocked() 中:

代码语言:javascript
复制
        if (r.app != null && r.app.thread != null) {
            sendServiceArgsLocked(r, execInFg, false);
            return null;
        }

而 sendServiceArgsLocked() 在上面 2.5.3 中知道bind service 是不会执行的。

那最终会跳过 2.5 节的流程继续往下执行:

代码语言:javascript
复制
        if (s.app != null && b.intent.received) {
            // Service is already running, so we can immediately
            // publish the connection.
            try {
                c.conn.connected(s.name, b.intent.binder, false);
            } catch (Exception e) {
                Slog.w(TAG, "Failure sending service " + s.shortName
                        + " to connection " + c.conn.asBinder()
                        + " (in " + c.binding.client.processName + ")", e);
            }

            // If this is the first app connected back to this binding,
            // and the service had previously asked to be told when
            // rebound, then do so.
            if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                requestServiceBindingLocked(s, b.intent, callerFg, true);
            }
        } else if (!b.intent.requested) {
            requestServiceBindingLocked(s, b.intent, callerFg, false);
        }

第一次bind service 的时候requested 为false,最终会调用到requestServiceBindingLocked():

代码语言:javascript
复制
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                + " rebind=" + rebind);
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
                // Keep the executeNesting count accurate.
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                throw e;
            } catch (RemoteException e) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                // Keep the executeNesting count accurate.
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                return false;
            }
        }
        return true;
    }

通过该函数进行bind 的request 申请,通过函数:

代码语言:javascript
复制
bumpServiceExecutingLocked(r, execInFg, "bind");

埋下onBind操作的ANR 炸弹,要求onBind 处理不能超过20s。

并通过:

代码语言:javascript
复制
r.app.thread.scheduleBindService()

最终调用handleBindService函数:

代码语言:javascript
复制
    private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        ...
        if (s != null) {
            try {
                ...
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                ...
            }
        }
    }

onBind 接口调用完成后会调用AMS.publishService,最终会调用到ActiveServices.publishServiceLocked 进行onBind 的后期处理,最终会调用serviceDoneExecutingLocked 解除ANR炸弹

至此,bindService的流程基本完成,总结下:

  • 注意两个核心变量AppBindRecord 和ConnectionRecord;
  • 如果没有启动service,会顺序进入create、bind、start 流程,并且在每个流程都会埋下ANR炸弹,要求每个流程在20s 内处理完成,在处理结束后ActiveServices 会拆除炸弹;
  • 如果已经启动service,会进入onStartCommand流程,同样伴随着ANR炸弹;
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-232,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0. 前言
  • 1. 入口函数
    • 1.1 bindService()
      • 1.2 bindServiceCommon()
      • 2. bindServiceLocked
          • 2.1 retrieveServiceLocked()
            • 2.2 变量AppBindRecord b
              • 2.3 变量 ConnectionRecord c
                • 2.4 变量 connections
                  • 2.5 bringUpServiceLocked()
                    • 2.5.1 进入create 流程
                    • 2.5.2 进入bind service 流程
                    • 2.5.3 进入start service 流程
                  • 2.6 requestServiceBindingsLocked()
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档