前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >bindService流程详解

bindService流程详解

作者头像
全栈程序员站长
发布2022-08-26 13:13:22
3730
发布2022-08-26 13:13:22
举报

大家好,又见面了,我是你们的朋友全栈君。

bindService的流程,入口同样在ContextImpl中。

ContextImpl.java

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

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) { 
   
    IServiceConnection sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
    validateServiceIntent(service);
    int res = ActivityManager.getService().bindService(
        mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier());
    return res != 0;
}

类似startService,bindService也是经由bindServiceCommon交由AMS处理。 要注意的是bind连接回调ServiceConnection是引用类型,因为bindService可能是跨进程的,需要先将其转换为bind接口IServiceConnection,具体实现为ServiceDispatcher的内部类InnerConnection,充当了Binder的角色。

ActivityManagerService.java

代码语言:javascript
复制
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中同样直接交给了ActiveService.bindServiceLocked处理。

这个方法的处理步骤如下:

  1. 从ServiceMap中查找ServiceRecord,并且在查的时进行了exported属性、权限等校验。
  2. 将Service调用方和被调用方的信息保存到AMS中;
  3. 创建连接信息;
  4. 将连接信息保存到mServiceConnections中;
  5. 如bind的flag包含BIND_AUTO_CREATE,则先尝试启动Service;
  6. 如此intent未被连接过,直接建立连接,回调onServiceConnected;
  7. Service未被bind过,需先调用onBind后,再回调onServiceConnected。

连接的过程分为2种场景:已经bind过 和 未被bind过,这2种场景如何区分呢?

s.app != null && b.intent.received,回到前面创建AppBindRecord的代码:

代码语言:javascript
复制
// 3.创建连接信息
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);

具体看ServiceRecord的retrieveAppBindingLocked方法实现:

代码语言:javascript
复制
public AppBindRecord retrieveAppBindingLocked(Intent intent, ProcessRecord app) { 
   
    Intent.FilterComparison filter = new Intent.FilterComparison(intent);
    // bindings是一个map
    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;
}

从这个方法中可以看出,如果fileter不同,即intent数据不同,将返回一个新的AppBindRecord对象。再去看Intent.FilterComparison equals()的实现,其比较的是Intent的数据。即,同一个app bind同一个Service,如果bind时传递的Intent数据一致,将共享同一个AppBindRecord。也就是说,bindServiceLocked中认为Service已经连接,需要满足2个条件:

  1. Service已启动
  2. 调用bindServce时传递的Intent没有被连接过,即intent数据不一样,会再次触发onBind。

这个设计给我们提供了一种新的思路:在一个Service中,可以根据需要为不同的启动参数,提供不同的binder服务,从而使Service内部逻辑更加清晰。

继续看bind的过程:

代码语言:javascript
复制
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException { 
   
    if ((!i.requested || rebind) && i.apps.size() > 0) { 
   
        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;
    }
    return true;
}

调用ActivityThread进行bind,这里不关注ActivityThread内部消息派发过程,直接看bind的实现。

ActivityThread.java

代码语言:javascript
复制
private void handleBindService(BindServiceData data) { 
   
    Service s = mServices.get(data.token);
          data.intent.setExtrasClassLoader(s.getClassLoader());
    data.intent.prepareToEnterProcess();
    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();
    ......
}

调用Service.onBind后,继续调用AMS.publishService发布Service。

代码语言:javascript
复制
public void publishService(IBinder token, Intent intent, IBinder service) { 
   
    mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}

继续看ActiveServices.publishServiceLocked。

代码语言:javascript
复制
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) { 
   
    final long origId = Binder.clearCallingIdentity();
    Intent.FilterComparison filter = new Intent.FilterComparison(intent);
    IntentBindRecord b = r.bindings.get(filter);
    if (b != null && !b.received) { 
   
        // 1.将IntentBindRecord.binder赋值为onBinder返回的Binder对象
        b.binder = service;
        b.requested = true;
        // 2.将IntentBindRecord.binder置为true,表示已调用onBind
        b.received = true;
        // 3.遍历此Service的所有连接记录
        for (int conni=r.connections.size()-1; conni>=0; conni--) { 
   
            ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
            for (int i=0; i<clist.size(); i++) { 
   
                ConnectionRecord c = clist.get(i);
                if (!filter.equals(c.binding.intent.intent)) { 
   
                    continue;
                }
                // 找到匹配的intent进行连接
                c.conn.connected(r.name, service, false);
            }
        }
    }
    serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}

publish过程主要做了3件事:

  1. 将IntentBindRecord.binder赋值为Service.onBinder返回的Binder对象;
  2. 将IntentBindRecord.binder置为true,表示已调用onBind; 这一步骤后,其他client再通过相同的intent数据进行bind,将直接进行连接,不会再进行onBind的过程。
  3. 遍历此Service的所有连接记录,找到匹配的intent进行连接。 接下看connect的具体过程:

在前面ContextImp.bindServiceCommon中已经知道,传递到AMS中的ServiceConnection是经过包装的IServiceConnectionBinder对象,所以connect的过程实际上是在启动方进程中进行的。此IServiceConnection是LoadedApk的静态内部类InnerConnection。

代码语言:javascript
复制
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);
        }
    }
}

InnerConnection直接将连接的处理逻辑交给了ServiceDispatcher。

代码语言:javascript
复制
static final class ServiceDispatcher { 
   
    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;

        synchronized (this) { 
   
            // 已经unbind,不做处理
            if (mForgotten) { 
   
                return;
            }
            // old不为null,表示此ServiceConnection进行了其他连接
            old = mActiveConnections.get(name);
            // 同一个Connection已进行过连接,不做处理
            if (old != null && old.binder == service) { 
   
                return;
            }

            // IBinder对象不为null,表示建立新连接
            if (service != null) { 
   
                info = new ConnectionInfo();
                info.binder = service;
                info.deathMonitor = new DeathMonitor(name, service);
                try { 
   
                    // linkToDeath
                    service.linkToDeath(info.deathMonitor, 0);
                    // 保存连接信息
                    mActiveConnections.put(name, info);
                } catch (RemoteException e) { 
   
                    mActiveConnections.remove(name);
                    return;
                }
            } else { 
   
                // IBinder对象为null,表示断开连接
                mActiveConnections.remove(name);
            }

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

        // 先移除旧连接
        if (old != null) { 
   
            mConnection.onServiceDisconnected(name);
        }
        if (dead) { 
   
            mConnection.onBindingDied(name);
        }
        // 新连接,回调onServiceConnected
        if (service != null) { 
   
            mConnection.onServiceConnected(name, service);
        }
    }
    
    private final class DeathMonitor implements IBinder.DeathRecipient { 
   
        DeathMonitor(ComponentName name, IBinder service) { 
   
            mName = name;
            mService = service;
        }
        public void binderDied() { 
   
            death(mName, mService);
        }
        final ComponentName mName;
        final IBinder mService;
    }
    
    public void doDeath(ComponentName name, IBinder service) { 
   
        synchronized (this) { 
   
            ConnectionInfo old = mActiveConnections.get(name);
            if (old == null || old.binder != service) { 
   
                return;
            }
            // 移除ConnectionInfo
            mActiveConnections.remove(name);
            // 调用unlinkToDeath
            old.binder.unlinkToDeath(old.deathMonitor, 0);
        }
        // 调用ServiceConnection.onServiceDisconnected
        mConnection.onServiceDisconnected(name);
    }
}

ServiceDispatcher.connected继续调用doConnected,doConnected中做了以下几件事情:

  1. 判断是否为重复bind,如是则直接return;
  2. 如IBinder对象不为null,表示新建连接,保存连接信息,linkToDeath;为null,表示断开连接; linkToDeath的目的是在Service被异常kill后,移除连接信息,并且回调ServiceConnection.onServiceDisconnected。
  3. 如此ServiceConnection已进行了其他连接,则先断开原有连接; 此场景出现在: 1)unbindService,具体过程下面分析; 2)用同一个ServiceConnection去bind同一个Service,onBind触发了多次的情况,即bind时intent参数有变化。
  4. 新连接建立完成,回调ServiceConnection.onServiceConnected

至此,一次bind连接已经完成,之后启动方就可以通过连接成功后返回的IBinder对象与Service进行交互了。

一次bind连接的完整流程如下:

在这里插入图片描述
在这里插入图片描述

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/143803.html原文链接:https://javaforall.cn

本文参与?腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年5月1,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com