深入浅出Service启动流程

在这里插入图片描述

转载请标明出处:【顾林海的博客】
本篇文章已授权微信公众号 顾林海 独家发布

Service启动方式有两种,一种是通过Context的startService启动Service,另一种是通过Context的bindService绑定Service,下面对这两种启动方式的启动流程进行详细的讲解。

startService的启动流程

通过startService方法启动Service会调用ContextWrapper的startService方法,如下所示:

//路径:/frameworks/base/core/java/android/content/ContextWrapper.java
public class ContextWrapper extends Context {
    
    Context mBase;
    
    ...
    
    @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
    
    ...
}

在ContextWrapper的startService方法中调用mBase的startService方法,mBase的类型是Context,而Context是一个抽象类,内部定义了很多方法以及静态常量,它的具体实现类是ContextImpl,进入ContextImpl的startService方法:

    //路径:/frameworks/base/core/java/android/app/ContextImpl.java
    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }

ContextImpl的startService方法中又调用了startServiceCommon方法:

    //路径:/frameworks/base/core/java/android/app/ContextImpl.java
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            //注释1
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            ...
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

注释1处通过ActivityManager的getService方法获取ActivityManagerService的代理类IActivityManager,进入ActivityManager的getService方法:

    //路径:/frameworks/base/core/java/android/app/ActivityManager.java
    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

getService方法通过IActivityManagerSingleton的get方法获取IActivityManager对象,IActivityManagerSingleton是一个单例类,在create方法中从ServiceManager中获取一个名叫“activity”的Service引用,同时也是IBinder类型的ActivityManagerService的引用,最后通过IActivityManager.Stub.asInterface方法将它转换成IActivityManager,看到IActivityManager.Stub.asInterface这段代码时可以知道这里采用的是AIDL方式来实现进程间通信,也就是说服务端ActivityManagerService会实现IActivityManager.Stub类并实现相应的方法。

继续回到ContextImpl的startServiceCommon方法:

    //路径:/frameworks/base/core/java/android/app/ContextImpl.java
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            //注释1
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            ...
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

在注释1处获取到ActivityManagerService的代理类IActivityManager,接着通过这个代理类向ActivityManagerService发送startActivity的消息。

将上面的知识点进行总结,如下图所示:

在这里插入图片描述

之前的操作是在应用程序进程中进行的,ActivityManagerService属于SystemServer进程,因此两者通过Binder通信,进入ActivityManagerService的startService方法:

    //路径:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    @Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {
        ...
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                //注释1
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

注释1处调用mServices的startServiceLocked方法,mServices的类型是ActiveServices,进入ActiveServices的startServiceLocked方法:

    //路径:/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {
        ...
        //注释1
        ServiceLookupResult res =
            retrieveServiceLocked(service, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false);
        if (res == null) {
            return null;
        }
        ...
        //注释2
        ServiceRecord r = res.record;
        ...
        //注释3
        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
        return cmp;
    }

注释1处retrieveServiceLocked方法获取与参数service对应的ServiceRecord,如果没有对应的就会从PackageManagerService中查找与service对应的Service信息,并封装成ServiceRecord中,最后将ServiceRecord赋值给ServiceLookupResult的成员变量record,ServiceRecord是一个用于描述Service相关信息的类,在注释2处将ServiceLookupResult中的成员变量record赋值给r,同时将ServiceRecord作为参数传递给注释3处的startServiceInnerLocked。

startServiceInnerLocked方法如下所示:

    //路径:/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ...
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        ...
        return r.name;
    }
    

接着调用bringUpServiceLocked方法,如下所示:

    //路径:/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
       ...
        final String procName = r.processName;
        String hostingType = "service";
        ProcessRecord app;

        if (!isolated) {
            //注释1
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            ...
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    //注释2
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    ...
                }

            }
        } else {
            ...
        }
        //注释3
        if (app == null && !permissionsReviewRequired) {
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingType, r.name, false, isolated, false)) == null) {
                ...
            }
            ...
        }

        if (!mPendingServices.contains(r)) {
            mPendingServices.add(r);
        }

        ...

        return null;
    }

启动Service时会在注释3处判断app==null,app的类型是ProcessRecord,用来描述运行的应用程序进程的信息,在注释1处将Service运行的进程名和uid传递给ActivityManagerService的getProcessRecordLocked方法,从而获取运行Service的应用程序进程信息ProcessRecord,如果用来运行Service的应用程序进程不存在,就会调用ActivityManagerService的startProcessLocked方法来创建对应的应用程序进程;如果用来运行Service的应用程序进程存在,会调用注释2处的realStartServiceLocked方法。

进入realStartServiceLocked方法:

    //路径:/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
            ...
            try {
               ...
               //注释1
                app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
                r.postNotification();
                created = true;
        } catch (DeadObjectException e) {
            ...
        } finally {
            ...
        }
                
    }

注释2处调用了app.thread的scheduleCreateService方法,app.thread是IApplicationThread类型,它的实现类ActivityThread的内部类ApplicationThread,通过ApplicationThread与应用程序进程进行Binder通信。

进入ApplicationThread的scheduleCreateService方法:

    //路径:/frameworks/base/core/java/android/app/ActivityThread.java
        public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;

            sendMessage(H.CREATE_SERVICE, s);
        }

将启动Service的参数封装成CreateServiceData对象并发送CREATE_SERVICE消息。

sendMessage方法如下所示:

    //路径:/frameworks/base/core/java/android/app/ActivityThread.java
    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

向mH类发送CREATE_SERVICE类型的消息,并将CreateServiceData传递过去,mH指的是H,它是ActivityThread的内部类并继承自Handler,AMS通过IApplicationThread向应用程序进程发送消息,接受消息的操作是在应用程序进程的Binder线程池中进行,因此需要Handler来发送消息切换到主线程,查看H的handleMessage方法:

//路径:/frameworks/base/core/java/android/app/ActivityThread.java
 public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                ...
                 case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                ...
            }
            ...
}

根据消息类型CREATE_SERVICE,调用handleCreateService方法:

//路径:/frameworks/base/core/java/android/app/ActivityThread.java
    private void handleCreateService(CreateServiceData data) {
        ...
        //注释1
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            //获取类加载器
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            //注释2
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            ...
        }

        try {
            //注释3
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            //注释4
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            //注释5
            service.onCreate();
            //注释6
            mServices.put(data.token, service);
            try {
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
           ...
        }
    }


注释1处获取启动Service的应用程序的LoadedApk,LoadedApk是一个APK文件的描述类,从中获取类加载器并在注释2处加载Service类,在注释5处调用Service的onCreate方法,Service就被启动了,同时在注释6处将启动的Service加入到ActivityThread的成员变量mServices中。其中注释3处通过ContextImpl的createAppContext方法创建ContextImpl也就是Service的上下文,并将该ContextImpl传入注释2处service的attach方法中,如下所示:

路径:/frameworks/base/core/java/android/app/Service.java

public final void attach(
            Context context,
            ActivityThread thread, String className, IBinder token,
            Application application, Object activityManager) {
        attachBaseContext(context);//注释1
        mThread = thread;           // NOTE:  unused - remove?
        mClassName = className;
        mToken = token;
        mApplication = application;
        mActivityManager = (IActivityManager)activityManager;
        mStartCompatibility = getApplicationInfo().targetSdkVersion
                < Build.VERSION_CODES.ECLAIR;
    }

在注释1处调用ContextWrapper的attachBaseContext方法,如下所示:

路径:/frameworks/base/core/java/android/content/ContextWrapper.java

protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }

Service的ContextImpl最终被赋值给ContextWrapper的成员变量mBase,由于Service继承自ContextWrapper,因此Service也可以使用Context的方法。

总结如下:

在这里插入图片描述

bindService的绑定流程

通过bindService方法绑定Service会调用ContextWrapper的bindService方法,如下所示:

    //路径:/frameworks/base/core/java/android/content/ContextWrapper.java
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }

mBase具体实现类是ContextImpl,ContextImpl的bindService方法:

    //路径:/frameworks/base/core/java/android/app/ContextImpl.java
    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }

bindService方法又调用了bindServiceCommon方法:

    //路径:/frameworks/base/core/java/android/app/ContextImpl.java
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            //注释1
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            ...
            //注释2
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            if (res < 0) {
                throw new SecurityException(
                        "Not allowed to bind to service " + service);
            }
            return res != 0;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

注释1处将ServiceConnection封装成IServiceConnection类型的对象sd,IServiceConnection实现了Binder机制,这样Service的绑定就支持跨进程通信了,在注释2处获取ActivityManagerService的代理类IActivityManager,向ActivityManagerService发送bindService消息。

到这里总结如下:

在这里插入图片描述


ActivityManagerService的bindService方法如下所示:
    //路径:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    
    final ActiveServices mServices;
    
    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);
        }
    }

最后在同步代码块中调用了mServices的bindServiceLocked方法,mServices的类型是ActiveServices,ActiveServices的bindServiceLocked方法如下:

    //路径:/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
            ...
            if ((flags&Context.BIND_AUTO_CREATE) != 0) {
                s.lastActivity = SystemClock.uptimeMillis();
                //注释1
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired) != null) {
                    return 0;
                }
            }
            ...
            if (s.app != null && b.intent.received) {
                ...
            } else if (!b.intent.requested) {
                //注释2
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }
            
    }

注释1处会调用bringUpServiceLocked方法,最终会调用realStartServiceLocked方法,在该方法中通过ApplicationThread与应用程序进程进行Binder通信,调用ApplicationThread的scheduleCreateService方法以此来创建并启动Service,关于创建和启动Service已经在第一部分讲过了。

注释2处当Service没有绑定时调用requestServiceBindingLocked方法:

     //路径:/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        ...
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                //注释1
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
               ...
            } catch (RemoteException e) {
               ...
            }
        }
        return true;
    }

注释1处,r.app.thread的类型是IApplicationThread,实现类是ActivityThread的内部类ApplicationThread,通过Binder机制调用ApplicationThread的scheduleBindService方法:

        //路径:/frameworks/base/core/java/android/app/ActivityThread.java
        public final void scheduleBindService(IBinder token, Intent intent,
                boolean rebind, int processState) {
            updateProcessState(processState, false);
            BindServiceData s = new BindServiceData();
            s.token = token;
            s.intent = intent;
            s.rebind = rebind;

            if (DEBUG_SERVICE)
                Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                        + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
            sendMessage(H.BIND_SERVICE, s);
        }

scheduleBindService放中将Service信息封装成BindServiceData对象,并通过H类发送BIND_SERVICE消息。

 //路径:/frameworks/base/core/java/android/app/ActivityThread.java
 public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                ...
                case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                ...
            }

H类在处理BIND_SERVICE消息时调用了handleBindService方法:

//路径:/frameworks/base/core/java/android/app/ActivityThread.java
    private void handleBindService(BindServiceData data) {
        //注释1
        Service s = mServices.get(data.token);
        ...
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                try {
                    if (!data.rebind) {
                        //注释2
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        ...
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                ...
            }
        }
    }

注释1处获取要绑定的Service,注释2处当Service还没绑定时调用Service的onBind方法进行绑定并调用ActivityManagerService的publishService方法:

    //路径:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
    public void publishService(IBinder token, Intent intent, IBinder service) {
        ...
        synchronized(this) {
            if (!(token instanceof ServiceRecord)) {
                throw new IllegalArgumentException("Invalid service token");
            }
            mServices.publishServiceLocked((ServiceRecord)token, intent, service);
        }
    }

进入mServices的publishServiceLocked方法:

    //路径:/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
        ...
        try {
            ...
            if (r != null) {
                ...
                if (b != null && !b.received) {
                    b.binder = service;
                    b.requested = true;
                    b.received = true;
                    for (int conni=r.connections.size()-1; conni>=0; conni--) {
                        ...
                        for (int i=0; i<clist.size(); i++) {
                            ...
                            try {
                                //注释1
                                c.conn.connected(r.name, service, false);
                            } catch (Exception e) {
                                ...
                            }
                        }
                    }
                }

                ...
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

注释1处的c.conn的类型是IServiceConnection,它是ServiceConnection在本地的代理,用于解决当前应用程序进程和Service跨进程通信的问题,IServiceConnection的具体实现类是ServiceDispatcher.InnerConnection,ServiceDispatcher是LoadedApk的内部类。ServiceDispatcher.InnerConnection的connected方法如下所示:

    //路径:/frameworks/base/core/java/android/app/LoadedApk.java
    static final class ServiceDispatcher {
        ...
        private final Handler mActivityThread;
        ...
        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) {
                    //注释1
                    sd.connected(name, service, dead);
                }
            }
        }
        ...
         public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityThread != null) {
                //注释2
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
                doConnected(name, service, dead);
            }
        }
        ...
    }

注释1处获取ServiceDispatcher类型sd的connected,也是就是调用注释2处的connected方法,并调用Handler类型的对象mActivityThread的post方法,mActivityThread实际指向的是ActivityThread的内部类H,最终通过H类的post方法将RunConnection对象的内容运行在主线程中,RunConnections是LoadedApk的内部类。代码如下所示:

//路径:/frameworks/base/core/java/android/app/LoadedApk.java
        private final class RunConnection implements Runnable {
            RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
                mName = name;
                mService = service;
                mCommand = command;
                mDead = dead;
            }

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

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

在run方法中调用注释1处的doConnected方法:

//路径:/frameworks/base/core/java/android/app/LoadedApk.java
        public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ...
            if (service != null) {
                //注释1
                mConnection.onServiceConnected(name, service);
            }
        }

注释1处调用mConnection的onServiceConnected,mConnection的类型是ServiceConnection,这样客户端实现ServiceConnection接口类的onServiceConnected方法就会被调用。

最后总结如下:

在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值