Service绑定流程

2018-12-18  本文已影响7人  gczxbb

Android系统的Service有两种启动方式,第一种是startService启动,第二种是bindService绑定。服务绑定状态时,多用于和其他组件通信。

Intent intentNormal = new Intent(ServiceActivity.this, NormalService.class);
startService(intentNormal);

Intent intentNormalBind = new Intent(ServiceActivity.this, NormalService.class);
bindService(intentNormalBind, conn, Service.BIND_AUTO_CREATE);

本文分析在Activity组件中调用bindService方法,实现Service绑定的流程,解释一下Service的onBind方法,App自定义实现类ServiceConnection的#onServiceConnected方法调用时机。
参考源码

我们在Activity组件中调用bindService方法,看一下打印日志。

AppServiceLifeCycle: LifeCycleService LifeCycleService 构造方法
LifeCycleService onCreate 方法
LifeCycleService onBind 方法

触发三个方法,构造方法,创建方法和绑定方法。Activity组件继承Context的装饰类ContextWrapper,在它的bindService方法中,调用的是ContextImpl实现类的bindService方法。

@Override
public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
    warnIfCallingFromSystemProcess();
    return bindServiceCommon(service, conn, flags, Process.myUserHandle());
}

进入bindServiceCommon方法代码。

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
        UserHandle user) {
    IServiceConnection sd;
        //抛出conn空的异常
    if (mPackageInfo != null) {
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                mMainThread.getHandler(), flags);
    } else {
    }
    validateServiceIntent(service);
    try {
        IBinder token = getActivityToken();
        //Ams请求
        int res = ActivityManagerNative.getDefault().bindService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, getOpPackageName(), user.getIdentifier());
        if (res < 0) {
            //异常
        }
        return res != 0;
    } catch (RemoteException e) {
    }
}

在该方法中,访问Ams服务,调用服务的bindService方法,传递参数包括ApplicationThread回调App进程,IBinder token代表该Activity在Ams中唯一标志,还有一个IServiceConnection对象。
系统没有将我们自定义ServiceConnection传给Ams服务,根据它由LoadedApk获取IServiceConnection。从系统源码中可以看出,IServiceConnection是一个aidl文件,InnerConnection是Binder类型,继承IServiceConnection.stub类,进程通信的服务端,和ApplicationThread是原理一样,Ams服务回调App进程。看一下LoadedApk的getServiceDispatcher方法。

public final IServiceConnection getServiceDispatcher(ServiceConnection c,
                                                     Context context, Handler handler, int flags) {
    synchronized (mServices) {
        LoadedApk.ServiceDispatcher sd = null;
        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
        if (map != null) {
            sd = map.get(c);
        }
        if (sd == null) {
            sd = new ServiceDispatcher(c, context, handler, flags);
            if (map == null) {
                map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                mServices.put(context, map);
            }
            map.put(c, sd);
        } else {
            sd.validate(context, handler);
        }
        return sd.getIServiceConnection();
    }
}

系统将ServiceConnection和ServiceDispatcher的关系保存在一个ArrayMap,根据Context和ServiceConnection定位ServiceDispatcher,未查到则创建一个新ServiceDispatcher。它的内部封装InnerConnection(Binder),ServiceConnection(服务回调),Context和ActivityThread。


Service绑定Ams服务流程

下面看一下ActivityManagerService的bindService方法处理过程。

public int bindService(IApplicationThread caller, IBinder token, Intent service,
                       String resolvedType, IServiceConnection connection, int flags, String callingPackage,
                       int userId) throws TransactionTooLargeException {
    ...
    synchronized(this) {
        return mServices.bindServiceLocked(caller, token, service,
                resolvedType, connection, flags, callingPackage, userId);
    }
}

委托Ams内部ActiveServices类。下面的都是该类的方法。

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags,
        String callingPackage, int userId) throws TransactionTooLargeException {

    final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
    //ProcessRecord是空抛异常

    ActivityRecord activity = null;
    //根据token查找Ams中的ActivityRecord记录
    if (token != null) {
        activity = ActivityRecord.isInStackLocked(token);
        //记录是空返回0
    }
    ServiceRecord s = res.record;

    try {
        ...
        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            s.lastActivity = SystemClock.uptimeMillis();
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
                return 0;
            }
        }

        if (s.app != null && b.intent.received) {
            try {
                c.conn.connected(s.name, b.intent.binder);
            } catch (Exception e) {
            }
            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);
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
    return 1;
}

调用bringUpServiceLocked方法。

private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting) throws TransactionTooLargeException {
    //如果app和thread存在,直接回调app进程onStartCommand方法,
    if (r.app != null && r.app.thread != null) {
        sendServiceArgsLocked(r, execInFg, false);
        return null;
    }
    //不存在,继续走流程
    realStartServiceLocked方法。
}

然后,调用realStartServiceLocked方法,通过scheduleCreateService方法回调App进程,创建Service,生命周期方法onCreate。
最后,bindServiceLocked方法会调用到requestServiceBindingLocked方法,回调App进程的scheduleBindService方法。回传参数IBinder(Ams的ServiceRecord记录),Intent。在App进程,发送BIND_SERVICE消息到ActivityThread的H中处理。

private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            data.intent.prepareToEnterProcess();
            try {
                if (!data.rebind) {
                    IBinder binder = s.onBind(data.intent);
                    //服务返回Binder
                    ActivityManagerNative.getDefault().publishService(
                            data.token, data.intent, binder);
                } else {
                    s.onRebind(data.intent);
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
                ensureJitEnabled();
            } catch (RemoteException ex) {
            }
        } catch (Exception e) {
        }
    }
}

根据token,查找绑定的Service类,调用Service的onBind方法。返回Binder对象,这时,服务已经被绑定。
publishService方法,回访Ams服务,将Binder发布到Ams,由Ams负责通知客户端。三个参数,都是scheduleBindService方法传过来的。
Ams的publishService方法,调用ActiveServices#publishServiceLocked方法。

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    ...
    for (int i=0; i<clist.size(); i++) {
        ConnectionRecord c = clist.get(i);
        if (!filter.equals(c.binding.intent.intent)) {
            continue;
        }
        try {
            c.conn.connected(r.name, service);
        } catch (Exception e) {
    }
}

ConnectionRecord内部conn是InnerConnection,Ams服务利用它进程通信,通知App进程。调用InnerConnection的connected方法,并将Binder返回绑定触发者,即Activity组件中自定义的ServiceConnection类。ServiceDispatcher的connected方法。

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

ServiceDispatcher构造方法传入Handler,它就是从ActivityThread获取的H类。看一下RunConnection任务。执行doConnected方法。

public void doConnected(ComponentName name, IBinder service) {
    ServiceDispatcher.ConnectionInfo old;
    ServiceDispatcher.ConnectionInfo info;
    ...
    // If there is a new service, it is now connected.
    if (service != null) {
        mConnection.onServiceConnected(name, service);
    }
}

调用ServiceDispatcher内部ServiceConnection的方法。到这里,我们在开发中遇到的Service绑定最常见的两个方法都执行过了,注意,只有onBind方法返回Binder对象不空时,才会调用onServiceConnected方法。


总结

Service绑定流程与其他组件启动类似,向Ams注册要绑定的服务类,通过ApplicationThread类实现App回调通知。在Ams服务中,如果发现服务未创建时,首先通知App生成服务实例,生命周期方法onCreate。
App进程Service绑定成功,将服务Binder通知到Ams服务,最后,Ams服务再利用IServiceConnection进程间业务接口将服务已经连接的事情通知在Activity中自定义的服务连接对象ServiceConnection,并告知它服务Binder。


任重而道远

上一篇下一篇

猜你喜欢

热点阅读