2024年Service的startService和bindService源码流程(2),面试题题目

重要知识点

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。

高级进阶篇——高级UI,自定义View(部分展示)

UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!

  • 面试题部分合集

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

注释1:通过 retrieveServiceLocked()查找是否有与service对应的ServiceRecord,如果没有找到就会调用 PackageManagerService去获取参数对应的Service信息,并封装到 ServiceRecord然后再封装为一个 ServiceLookupResult并返回。

注释2:把封装的 ServiceRecord给拿出来。 ServiceRecord用来描述一个Service

注释3:把 ServiceRecord传入到 startServiceInnerLocked()方法中

我们继续看一下这个方法:

// 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);

if (error != null) {

return new ComponentName(“!!”, error);

}

return r.name;

}

startServiceInnerLocked()中调用了这么一个方法 bringUpServiceLocked()

// ActiveServices.java

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,

boolean whileRestarting, boolean permissionsReviewRequired)

throws TransactionTooLargeException {

//1获取Service所要运行的进程

final String procName = r.processName;

String hostingType = “service”;

ProcessRecord app;

if (!isolated) {

//2

app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);

if (DEBUG_MU) Slog.v(TAG_MU, “bringUpServiceLocked: appInfo.uid=” + r.appInfo.uid

  • " app=" + app);

//3

if (app != null && app.thread != null) {

try {

app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);

//4. 启动Service

realStartServiceLocked(r, app, execInFg);

return null;

} catch (TransactionTooLargeException e) {

throw e;

} catch (RemoteException e) {

Slog.w(TAG, "Exception when starting service " + r.shortName, e);

}

// If a dead object exception was thrown – fall through to

// restart the application.

}

} else {

}

// 5

if (app == null && !permissionsReviewRequired) {

// 6

if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,

hostingType, r.name, false, isolated, false)) == null) {

String msg = "Unable to launch app "

  • r.appInfo.packageName + “/”

  • r.appInfo.uid + " for service "

  • r.intent.getIntent() + “: process is bad”;

Slog.w(TAG, msg);

bringDownServiceLocked®;

return msg;

}

}

return null;

}

注释1:从ServiceRecord中获取 processName赋值给 procName

注释2:根据 procName和Service的uid来传入到 AMS的 getProcessRecordLocked()中,查询是否存在一个与Service对应的ProcessRecord类型对象。

如果不存在,则在注释5、6中调用 AMS的 startProcessLocked()来创建一个引用程序进程。

如果存在,则在注释3、4中调用 realStartServiceLocked()来启动一个Service。

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) {

Slog.w(TAG, "Application dead when creating service " + r);

mAm.appDiedLocked(app);

throw e;

} finally {

}

}

这里也有一个Real方法。app.thread.scheduleCreateService即调用调用主线程的 scheduleCreateService()的方法。

我们知道 app.thread就是 IApplicationThread,通过AIDL,这时候的代码从 SystemServer进程到了 应用程序的进程中。

// 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);

}

这里创建了一个 CreateServiceData类,把Service的信息都封装了进去,设置一个 H.CREATE_SERVICE字段,sendMessage出去。

到了 ApplicationThread这里,免不了要使用Handler来将线程切换到主线程去。我们直接来看看 H是怎么处理这个信息的吧

// ActivityThread.java

public void handleMessage(Message msg) {

switch (msg.what) {

case CREATE_SERVICE:

Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));

//1

handleCreateService((CreateServiceData)msg.obj);

Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

break;

}

}

到了主线程中,调用 handleCreateService():

private void handleCreateService(CreateServiceData data) {

// 获取要启动Service的应用程序的 LoadedApk

LoadedApk packageInfo = getPackageInfoNoCheck(

data.info.applicationInfo, data.compatInfo);

Service service = null;

try {

java.lang.ClassLoader cl = packageInfo.getClassLoader();

//1

service = (Service) cl.loadClass(data.info.name).newInstance();

} catch (Exception e) {

}

try {

if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

//创建上下文环境

ContextImpl context = ContextImpl.createAppContext(this, packageInfo);

context.setOuterContext(service);

Application app = packageInfo.makeApplication(false, mInstrumentation);

//2

service.attach(context, this, data.info.name, data.token, app,

ActivityManager.getService());

//3

service.onCreate();

//4

mServices.put(data.token, service);

} catch (Exception e) {

}

}

这里的代码和创建Activity时的非常相似。

注释1:通过LoadedApk的getClassLoader()来获取一个类加载器。然后根据 CreateServiceData 来创建一个 Service实例。

注释2:通过 service.attach()来初始化一个service

注释3:调用这个 service的 onCreate()方法。这里 service就启动了。

注释4:最后,把这个service放到 ActivityThread的成员变量 mServices中,它是一个 ArrayMap

至此,一个Service的完整的创建启动流程就over了。

下面来讲解绑定一个Service。

2.Service的绑定流程

===============================================================================

因为Service的开启方法有 startService() , 也有 bindService()

Service的绑定过程分成3个部分

  1. ContextImpl到AMS的调用过程

  2. AMS调用 bindService() 到 publishService() 过程

  3. AMS到 ServiceConnection的过程

2.1 ContextImpl到AMS的调用过程


我们先来看一下时序图:

在这里插入图片描述

在代码中调用 context.bindService(),就是调用 mBase.bindService()

从之前我们学过, mBase就是 ContextImpl。我们来看看它的 bindService()

// ContextImpl.java

@Override

public boolean bindService(Intent service, ServiceConnection conn,

int flags) {

warnIfCallingFromSystemProcess();

return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),

Process.myUserHandle());

}

这个方法传入了 IntentServiceConntection(Service连接的回调),和 flags,然后这个方法里面带上了 主线程的Handler,传入到 bindServiceCommom里面去:

// 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中,通过 LoadedApk.getServiceDispatcher()将传进来的 ServiceConnection封装成一个 IServiceConnection对象。

也就是说 ServiceConnection也是实现了Binder的。

注释2中:调用 ActivityManager.getSerive()IActivityManager.bindSerive(),这里代码进入到 AMS层。

2.2 AMS调用 bindService() 到 publishService() 过程


先来看看时序图:

在这里插入图片描述

我们来看看 AMS的 bindService()

// ActivityManagerService

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);

}

}

省略号中的代码都是异常判断处理。

synchronized中调用了 ActiveService.bindServiceLocked()

// ActivityManagerService.java

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,

String resolvedType, final IServiceConnection connection, int flags,

String callingPackage, final int userId) throws TransactionTooLargeException {

try {

//1

AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);

if ((flags&Context.BIND_AUTO_CREATE) != 0) {

s.lastActivity = SystemClock.uptimeMillis();

//2

if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,

permissionsReviewRequired) != null) {

return 0;

}

}

//3

if (s.app != null && b.intent.received) {

try {

//4

c.conn.connected(s.name, b.intent.binder, false);

} …

if (b.intent.apps.size() == 1 && b.intent.doRebind) {

//5

requestServiceBindingLocked(s, b.intent, callerFg, true);

}

} else if (!b.intent.requested) {

//6

requestServiceBindingLocked(s, b.intent, callerFg, false);

}

}

这个方法里面出现了几个 Record,我们来看一下他们分别描述什么:

  • ServiceRecord

用来描述一个 Service

  • ProcessRecord

描述一个进程

  • ConnectionRecord

描述应用程序进程和 Service建立的一次通信

  • AppBindRecord

应用程序进程通过Intent绑定Service时,会通过AppBindRecord来维护Service与应用程序进程之间的关联。

其内部会创建并存储了 谁绑定的Service(ProcessRecord)、被绑定的Service(AppBindRecord)、绑定Service的 Intent(IntentBindRecord)和所有绑定通信记录的信息(ArraySet<ConnectionRecord>

  • IntentBindRecord

用于描述绑定Service的Intent

注:不同的应用进程也可能会创建相同的Intent来绑定同一个Serivce。比如说一些公共的、常用的Service。

注释1:调用了 ServiceRecord.retrieveAppBindingLocked(),获取一个service的 AppBindRecord对象。我们在前面关于AppBindRecord的介绍可以看到,它内部会创建 IntentBindRecord并赋值。

注释2:如果传进来的flags == BIND_AUTO_CREATE就会调用了 bringUpServiceLocked(),其内部会调用 realStartServiceLocked(),这个方法我们学习Service的启动流程的时候学到过,其为与服务端的最后一个方法,它会通过Binder最终到 ActivityThread去,然后调用 Service.onCreate()。这也就是说在Service的绑定流程中也会去启动Serivce。

注释3: s.app != nul表示Service已经运行。其中s是ServiceRecord, app是ProcessRecord。 b.intent.received表示当前应用进程已经接收到绑定Service时返回的Binder,这样应用程序就可以通过Binder来获取要绑定的Service的访问接口。

注释4:调用c.conn的 connected,其中 c.conn就是我们一开始封装好的 IServiceConenction,它的具体实现类为 ServiceDispatcher.InnerConnection,其中ServiceDispatcher是 LoadedApk的内部类,InnerConnection的 connected方法会调用H的post()向主线程发送消息。

注释5:如果当前应用程序进程是第一个与Service进行绑定的,并且Service已经调用过 onUnBind(),则就会调用 requestServiceBindingLocked(...,true),最后的参数 rebind是true

注释6:如果应用程序进程的Client端没有发送过绑定Service的请求,则也会调用 requestServiceBindingLocked(...,flase)。最后的rebind是flase

我们来看下注释5的 requestServiceBindingLocked()

// ActivesServices.java

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,

boolean execInFg, boolean rebind) throws TransactionTooLargeException {

//1

if ((!i.requested || rebind) && i.apps.size() > 0) {

try {

bumpServiceExecutingLocked(r, execInFg, “bind”);

r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);

//2

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) {

}

}

return true;

}

注释1中的 i.requested表示 是否发送过绑定Service的请求,从bindServiceLocked可以得知已经发送过绑定了,因此 !i.requested == false。从 bindServiceLocked中我们得知 rebind为true。所以这里是可以继续走下去的。

i.apps.size() 中,i指的是 IntenBindRecord,AMS会为每个绑定的Service的Intent分配一个IntentBindRecord对象。我们来看一下IntentBindRecord的代码:

// ActiveServices.java

final class IntentBindRecord {

//被绑定的Service

final ServiceRecord service;

//绑定Service的Intent

final Intent.FilterComparison intent; //

//所有用当前Intent绑定Service应用程序进程

final ArrayMap<ProcessRecord, AppBindRecord> apps

= new ArrayMap<ProcessRecord, AppBindRecord>();

}

不同的应用程序进程可能使用同一个Intent来绑定Service,所以我们看到 apps用来存储这些进程。

之前说过 i.apps.size() > 0就是说明已经有Intent绑定了Serivice了。下面来验证 i.apps.size()>0为true。我们回到 bindServiceLocked()的注释1代码处,即 retrieveAppBindingLocked()

// ServiceRecord.java

public AppBindRecord retrieveAppBindingLocked(Intent intent,

ProcessRecord app) {

Intent.FilterComparison filter = new Intent.FilterComparison(intent);

IntentBindRecord i = bindings.get(filter);

if (i == null) {

//1

i = new IntentBindRecord(this, filter);

bindings.put(filter, i);

}

//2

AppBindRecord a = i.apps.get(app);

if (a != null) {

return a;

}

//3

a = new AppBindRecord(this, i, app);

i.apps.put(app, a);

return a;

}

注释1:创建了 IntentBindRecord

注释2:根据ProcessRecord获取了 AppBindRecord,如果其不为null就会返回。

如果为null就在注释3中,创建一个新的 AppBindRecord,并将其put到 apps中,key为 ProcessRecord。

这里代码也就表明, i.apps.size() > 0是为true的。 这样也就会调用上述 requestServiceBindingLocked(...,true)中,注释2 的代码。

r.app.thread 是 IApplicationThread,所以 它执行的 scheduleBindService()肯定就是通过调用 H类来切换线程到ActivityThread中。

因为这里很熟悉,我们直接看ActivityThread最终会执行什么代码:

// ActivityThread.java

public void handleMessage(Message msg) {

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;

}

}

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 {

//2

if (!data.rebind) {

//3

IBinder binder = s.onBind(data.intent);

//4

最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。

image

Android高级架构师之路很漫长,一起共勉吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

Message msg) {

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;

}

}

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 {

//2

if (!data.rebind) {

//3

IBinder binder = s.onBind(data.intent);

//4

最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。

[外链图片转存中…(img-OymBJtvf-1715692423517)]

Android高级架构师之路很漫长,一起共勉吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值