bindService 分析---之一

2, bindService

当一个用户进程bindService()时,它需要先准备好一个实现了ServiceConnection接口的对象。ServiceConnection的定义如下:

public interface ServiceConnection {
    public void onServiceConnected(ComponentName name, IBinder service);
    public void onServiceDisconnected(ComponentName name);
}

每当用户调用bindService(),Android都将之视作是要建立一个新的“逻辑连接”。而当连接建立起来时,系统会回调ServiceConnection

接口的onServiceConnected()。另一方面,那个onServiceDisconnected()函数却不是在unbindService()时发生的。一般来说,当目标

service所在的进程意外挂掉或者被杀掉时,系统才会回调onServiceDisconnected(),而且,此时并不会销毁之前的逻辑连接,也就是说,

那个“逻辑连接”仍然处于激活状态,一旦service后续再次运行,系统会再次回调onServiceConnected()。bindService调用流程图如下,


进程调用bindService方法时,向AMS请求这一段和启动服务的流程一样,在此就不详细分析了。调用流程图如下,


AMS的bindService方法如下,

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

bindServiceLocked方法如下,

int bindServiceLocked(IApplicationThread caller, IBinder token,
    Intent service, String resolvedType,
    IServiceConnection connection, int flags, int userId) {
    . . . . . .
    final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
    . . . . . .
    ActivityRecord activity = null;
    . . . . . .
        activity = ActivityRecord.isInStackLocked(token);
    . . . . . .
    ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,
                Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
    . . . . . .
    ServiceRecord s = res.record;
    . . . . . . 
        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);  
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent);

        IBinder binder = connection.asBinder();
        ArrayList<ConnectionRecord> clist = s.connections.get(binder);
        . . . . . .
        clist.add(c);
        b.connections.add(c);
        . . . . . .
        b.client.connections.add(c);
        . . . . . .
        clist = mServiceConnections.get(binder);
        . . . . . .
        clist.add(c);

        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            . . . . . .
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
                return 0;
            }
        }
        . . . . . .
        if (s.app != null && b.intent.received) {
            . . . . . .
                c.conn.connected(s.name, b.intent.binder); 
            . . . . . .
            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);  
        }
    . . . . . .
}

尽管这个函数里有不少技术细节,而且涉及到多个映射表,但它的总体意思大概是这样的。每当用户调用bindService()时,

Android都将之看作是要建立一个新的“逻辑连接”,而每个逻辑连接都对应一个ConnectionRecord节点,所以最终的表现肯定

是向ServiceRecord内部的某张映射表里添加一个新的ConnectionRecord节点。当然,在实际运作时,这个节点还会记录

进其他几个映射表里(比如系统总映射表)。

对于一个Service而言,有多少应用和它建立了绑定关系,就会有多少个AppBindRecord节点, 当然,一个应用里可以有多个

地方发起绑定动作,所以AppBindRecord里需要用一个ArraySet<ConnectionRecord>记录下每个绑定动作对应的逻辑连接节点。

在ConnectionRecord被记录进合适的表后,要开始和目标service建立连接了。我们可以看到,bindServiceLocked()会尝试

呼叫起目标service。

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

看到这句if语句,大家应该知道调用bindService()时为什么常常要加上BIND_AUTO_CREATE了吧:

bindService(intent, conn, Context.BIND_AUTO_CREATE);

这里面也分为三种情况:

1,目标服务已经启动了,直接调用c.conn.connected 方法

2,目标服务并没有启动,调用requestServiceBindingLocked

3,目标服务所在的进程还未启动,调用bringUpServiceLocked方法

1.1 connected

connected 方法调用流程图如下,


c.conn.connected到底是怎么调用的呢?此时在AMS服务进程中。

C是ConnectionRecord对象, conn 是 IServiceConnection,一看就是跨进程调用。

这时,以下流程都是运行于发起bindService所在的进程中,在LoadedApk的内部类 ServiceDispatcher里,

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) throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service);
                }
            }
        }

ServiceDispatcher的connected方法如下,

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

RunConnection类定义如下,

private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command) {
                mName = name;
                mService = service;
                mCommand = command;
            }

            public void run() {
                if (mCommand == 0) {
                    doConnected(mName, mService);
                } else if (mCommand == 1) {
                    doDeath(mName, mService);
                }
            }

            final ComponentName mName;
            final IBinder mService;
            final int mCommand;
        }

doConnected方法如下,

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

            synchronized (this) {
               
           •••
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
        }

到这儿终于看到调用onServiceConnected方法了,在该方法中,有一个 service参数,这就是被绑定的binder的引用,

通过它就可以进行跨进程调用。

public void doDeath(ComponentName name, IBinder service) {
            mConnection.onServiceDisconnected(name);
        }

发布了369 篇原创文章 · 获赞 81 · 访问量 43万+
展开阅读全文
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览