bindService流程详解

bindService流程详解bindService的流程,入口同样在ContextImpl中。ContextImpl.javapublicbooleanbindService(Intentservice,ServiceConnectionconn,intflags){warnIfCallingFromSystemProcess();returnbindServiceCommon(service,conn,flags,mMainThread.getHandler(),Process.my

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

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

ContextImpl.java

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

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的代码:

// 3.创建连接信息
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);

具体看ServiceRecord的retrieveAppBindingLocked方法实现:

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的过程:

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

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。

public void publishService(IBinder token, Intent intent, IBinder service) { 
   
    mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}

继续看ActiveServices.publishServiceLocked。

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。

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。

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连接的完整流程如下:

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号