Service源码浅析四

Service源码浅析一
Service源码浅析二
Service源码浅析三
Service源码浅析四

今天又发现两个问题,平时开发中的细节太多了,可能很多只是知道结论,不知道原理,争取以后一一记录下来,探究原理。

1. 主动unbindService会触发onServiceDisconnected回调吗?

先说答案,不会回调onServiceDisconnected。
AMS#unbindService -> ActiveServices#unbindServiceLocked -> removeConnectionLocked -> ActivityThread#scheduleUnbindService -> handleUnbindService -> Service#onUnbind -> AMS#unbindFinished -> ActiveServices#unbindFinishedLocked
回到ActiveServices#removeConnectionLocked -> removeConnectionLocked -> bringDownServiceIfNeededLocked -> bringDownServiceLocked -> ActivityThread#scheduleStopService -> Service#onDestroy
这是咱们整理的unbindService的流程,现在咱们就要在这堆流程中找出第一个问题的答案。

-->ActivityManagerService
    public boolean unbindService(IServiceConnection connection) {
        try {
            if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "unbindService");
            }
            synchronized (this) {
                return mServices.unbindServiceLocked(connection);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

触发ActivityManagerService#unbindService时传入了IServiceConnection,一会咱们看看这个IServiceConnection会起什么作用。
ActiveServices#unbindServiceLocked中会根据IServiceConnection取出对应的ConnectionRecord

/**
     * All currently bound service connections.  Keys are the IBinder of
     * the client's IServiceConnection.
     */
    final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>();

ConnectionRecord是在绑定服务流程bindServiceLocked时创建的,它持有IServiceConnection的引用

/**
 * Description of a single binding to a service.
 */
final class ConnectionRecord implements OomAdjusterModernImpl.Connection{
    final AppBindRecord binding;    // The application/service binding.
    final ActivityServiceConnectionsHolder<ConnectionRecord> activity;  // If non-null, the owning activity.
    final IServiceConnection conn;  // The client connection.
...
}

接下来进入ActiveServices#removeConnectionLocked

int removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
            ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) {
        IBinder binder = c.conn.asBinder();
        AppBindRecord b = c.binding;
        ServiceRecord s = b.service;
        @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy =
                SERVICE_BIND_OOMADJ_POLICY_LEGACY;
        ArrayList<ConnectionRecord> clist = s.getConnections().get(binder);
        if (clist != null) {
            clist.remove(c);
            if (clist.size() == 0) {
                s.removeConnection(binder);
            }
        }
...

在这里根据ConnectionRecord获取了ServiceRecord,ServiceRecord中有对应的

private final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
            = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();

在这个方法中触发了

s.app.getThread().scheduleUnbindService(s, b.intent.intent.getIntent());

传入了ServiceRecord,我们现在改为追踪ServiceRecord。

private void handleUnbindService(BindServiceData data) {
        CreateServiceData createData = mServicesData.get(data.token);
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess(isProtectedComponent(createData.info),
                        s.getAttributionSource());
                boolean doRebind = s.onUnbind(data.intent);
                try {
                    if (doRebind) {
                        ActivityManager.getService().unbindFinished(
                                data.token, data.intent);
                    } else {
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_UNBIND, 0, 0, data.intent);
                    }
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(s, e)) {
                    throw new RuntimeException(
                            "Unable to unbind to service " + s
                            + " with " + data.intent + ": " + e.toString(), e);
                }
            }
        }
    }

进入Service进程的ActivityThread#handleUnbindService,这里又触发了Service#onUnbind

    /**
     * Called when all clients have disconnected from a particular interface
     * published by the service.  The default implementation does nothing and
     * returns false.
     * 
     * @param intent The Intent that was used to bind to this service,
     * as given to {@link android.content.Context#bindService
     * Context.bindService}.  Note that any extras that were included with
     * the Intent at that point will <em>not</em> be seen here.
     * 
     * @return Return true if you would like to have the service's
     * {@link #onRebind} method later called when new clients bind to it.
     */
    public boolean onUnbind(Intent intent) {
        return false;
    }

如果onUnbind返回false,咱们执行AMS#serviceDoneExecuting

    @Override
    public void serviceDoneExecuting(IBinder token, int type, int startId, int res, Intent intent) {
        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
                throw new IllegalArgumentException("Invalid service token");
            }
            mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false,
                    intent);
        }
    }

传入的token就是触发ActivityThread#handleUnbindService时传入的ServiceRecord。
走到这里有点不太对劲啊,后续好像没有执行Service#onDestroy的逻辑,也就是说如果Service#onUnbind返回false,后续不会走onDestroy吗?
咱们重新回到ActiveServices#removeConnectionLocked在方法最后有这样一段逻辑

int removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
            ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) {
...
            if (c.hasFlag(Context.BIND_AUTO_CREATE)) {
                boolean hasAutoCreate = s.hasAutoCreateConnections();
                if (!hasAutoCreate) {
                    if (s.tracker != null) {
                        synchronized (mAm.mProcessStats.mLock) {
                            s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(),
                                    SystemClock.uptimeMillis());
                        }
                    }
                }
                bringDownServiceIfNeededLocked(s, true, hasAutoCreate, enqueueOomAdj,
                        "removeConnection");
            }
...
}

如果启动Service时指定了BIND_AUTO_CREATE,则会执行bringDownServiceIfNeededLocked,如果没有指定这个Flag,说明Service不是自动创建的,不需要销毁,这样逻辑就圆起来了啊,完美。
咱们继续顺着bringDownServiceIfNeededLocked往下走,跟紧ServiceRecord。
接着调用bringDownServiceLocked,这是一个超级方法。

        // Report to all of the connections that the service is no longer
        // available.
        ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
        for (int conni = connections.size() - 1; conni >= 0; conni--) {
            ArrayList<ConnectionRecord> c = connections.valueAt(conni);
            for (int i=0; i<c.size(); i++) {
                ConnectionRecord cr = c.get(i);
                // There is still a connection to the service that is
                // being brought down.  Mark it as dead.
                cr.serviceDead = true;
                cr.stopAssociation();
                final ComponentName clientSideComponentName =
                        cr.aliasComponent != null ? cr.aliasComponent : r.name;
                try {
                    cr.conn.connected(clientSideComponentName, null, true);
                } catch (Exception e) {
                    Slog.w(TAG, "Failure disconnecting service " + r.shortInstanceName
                          + " to connection " + c.get(i).conn.asBinder()
                          + " (in " + c.get(i).binding.client.processName + ")", e);
                }
            }
        }

cr.conn.connected(clientSideComponentName, null, true);会调到客户端的LoadedApk

        private static class InnerConnection extends IServiceConnection.Stub {
            @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
            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);
                }
            }
        }

入参dead=true,接着触发ServiceDispatcher#doConnected

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

            synchronized (this) {
                if (mForgotten) {
                    // We unbound before receiving the connection; ignore
                    // any connection received.
                    return;
                }
                old = mActiveConnections.get(name);
                if (old != null && old.binder == service) {
                    // Huh, already have this one.  Oh well!
                    return;
                }

                if (service != null) {
                    // A new service is being connected... set it all up.
                    info = new ConnectionInfo();
                    info.binder = service;
                    info.deathMonitor = new DeathMonitor(name, service);
                    try {
                        service.linkToDeath(info.deathMonitor, 0);
                        mActiveConnections.put(name, info);
                    } catch (RemoteException e) {
                        // This service was dead before we got it...  just
                        // don't do anything with it.
                        mActiveConnections.remove(name);
                        return;
                    }

                } else {
                    // The named service is being disconnected... clean up.
                    mActiveConnections.remove(name);
                }

                if (old != null) {
                    old.binder.unlinkToDeath(old.deathMonitor, 0);
                }
            }

            // If there was an old service, it is now disconnected.
            if (old != null) {
                mConnection.onServiceDisconnected(name);
            }
            if (dead) {
                mConnection.onBindingDied(name);
            } else {
                // If there is a new viable service, it is now connected.
                if (service != null) {
                    mConnection.onServiceConnected(name, service);
                } else {
                    // The binding machinery worked, but the remote returned null from onBind().
                    mConnection.onNullBinding(name);
                }
            }
        }

这个方法之前贴过,这里在贴一遍,可以看到如果old不为null,会触发onServiceDisconnected,

private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
            = new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();

old是从mActiveConnections中取出的,

        void doForget() {
            synchronized(this) {
                for (int i=0; i<mActiveConnections.size(); i++) {
                    ServiceDispatcher.ConnectionInfo ci = mActiveConnections.valueAt(i);
                    ci.binder.unlinkToDeath(ci.deathMonitor, 0);
                }
                mActiveConnections.clear();
                mForgotten = true;
            }
        }

mActiveConnections在最开始调用forgetServiceDispatcher时就触发了ServiceDispatcher#doForget清空了mActiveConnections,所以正常解绑流程old是null的,也就是不会触发onServiceDisconnected,异常情况,服务端崩溃导致的调用old不为null则触发onServiceDisconnected,到这里其实这个问题已经破案了,bringDownServiceLocked后面就是destroy Service的逻辑了,不详细分析了。

2. unbindService一个未绑定的服务,会发生异常吗?

答案是会的,不管是服务未绑定过还是绑定过但是解绑了,都会触发异常。

---> ContextImpl
    @Override
    public void unbindService(ServiceConnection conn) {
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
                    getOuterContext(), conn);
            try {
                ActivityManager.getService().unbindService(sd);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } else {
            throw new RuntimeException("Not supported in system context");
        }
    }

unbindService的主流程咱们在之前的文章中跟过,但是主流程好过,流程中夹杂的细节就不太好办了,只能通过问题的方式一点一点的扣,这个问题就隐藏在流程中。
这里触发了mPackageInfo.forgetServiceDispatcher,其中mPackageInfo是LoadedApk的实例。

    public final IServiceConnection forgetServiceDispatcher(Context context,
            ServiceConnection c) {
        synchronized (mServices) {
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
                    = mServices.get(context);
            LoadedApk.ServiceDispatcher sd = null;
            if (map != null) {
                sd = map.get(c);
                if (sd != null) {
                    if (DEBUG) Slog.d(TAG, "Removing dispatcher " + sd + " for conn " + c);
                    map.remove(c);
                    sd.doForget();
                    if (map.size() == 0) {
                        mServices.remove(context);
                    }
                    if ((sd.getFlags()&Context.BIND_DEBUG_UNBIND) != 0) {
                        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
                                = mUnboundServices.get(context);
                        if (holder == null) {
                            holder = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                            mUnboundServices.put(context, holder);
                        }
                        RuntimeException ex = new IllegalArgumentException(
                                "Originally unbound here:");
                        sd.setUnbindLocation(ex);
                        holder.put(c, sd);
                    }
                    return sd.getIServiceConnection();
                }
            }
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
                    = mUnboundServices.get(context);
            if (holder != null) {
                sd = holder.get(c);
                if (sd != null) {
                    RuntimeException ex = sd.getUnbindLocation();
                    throw new IllegalArgumentException(
                            "Unbinding Service " + c
                            + " that was already unbound", ex);
                }
            }
            if (context == null) {
                throw new IllegalStateException("Unbinding Service " + c
                        + " from Context that is no longer in use: " + context);
            } else {
                throw new IllegalArgumentException("Service not registered: " + c);
            }
        }
    }

这段代码的逻辑比较简单,其中mServices和mUnboundServices是两个map

private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
        = new ArrayMap<>();
    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
        = new ArrayMap<>();

如果是正常解绑,就返回ServiceDispatcher#getIServiceConnection这里的InnerConnection之前也提到过,Service解绑/绑定之后会触发它的回调方法。
mServices和mUnboundServices分别在绑定和解绑时进行更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容