Android Service 시작 과정 전체 분석
우 리 는 Service 가 백 스테이지 임 무 를 수행 할 수 있다 는 것 을 알 고 있 습 니 다.백 스테이지 임 무 는 시간 이 걸 리 는 임무 가 아니 라 백 스테이지 와 시간 이 걸 리 는 것 과 차이 가 있 습 니 다.
이렇게 하면 음악 재생 기 를 쉽게 생각 할 수 있 고 일기예보 등 응용 프로그램 은 Service 를 사용 해 야 한다.물론 Service 에서 시간 소모 작업 을 수행 하려 면 스 레 드 를 켜 면 됩 니 다.
Service 의 운행 상 태 는 두 가지 가 있 습 니 다.시작 상태 와 바 인 딩 상태,두 가지 상태 가 함께 할 수 있 습 니 다.
하나의 Service 를 시작 하려 면 Context 의 startService 방법 을 호출 하여 Intent 에 전송 하면 됩 니 다.쉽게 말 하면 안 드 로 이 드 가 개발 자 들 의 편 의 를 위해 어느 정도 패 키 징 을 했 기 때 문 으로 보인다.그럼 서비스 가 어떻게 작 동 하 는 지 배 워 본 적 있어 요?Service 의 onCreate 방법 을 되 돌리 기 전에 어떤 준 비 를 했 습 니까?
먼저 위의 그림 에서 대체적으로 알 아 보면 회색 배경 상 자 는 같은 유형의 방법 으로 다음 과 같다.
다음은 소스 코드 의 측면 에서 Service 의 시작 과정 을 분석 하 겠 습 니 다.
물론 Context 의 startService 방법 부터 시작 합 니 다.Context 의 실현 류 는 ContextImpl 입 니 다.그러면 우 리 는 ContextImpl 의 startService 방법 을 보면 다음 과 같 습 니 다.
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, mUser);
}
start Service Common 방법 으로 넘 어 갑 니 다.그럼 start Service Common 방법 을 따라 보 세 요.
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess();
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
//
return cn;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
Activity Manager Native.getDefault()를 호출 한 startService 방법 을 볼 수 있 습 니 다.Activity Manager Native.getDefault()는 Activity Manager Service 로 AMS 라 고 부 릅 니 다.그러면 현재 서 비 스 를 시작 하 는 과정 은 Activity Manager Service 로 이전 되 었 습 니 다.저 희 는 Activity Manager Service 의 startService 방법 에 관심 을 가지 면 됩 니 다.다음 과 같 습 니 다.
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, 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 = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
상기 코드 에서 ActiveServices 의 startServiceLocked 방법 을 호출 하면 현재 Service 의 시작 과정 은 AMS 에서 ActiveServices 로 이전 되 었 습 니 다.ActiveServices 의 startServiceLocked 방법 을 계속 따라 갑 니 다.다음 과 같 습 니 다.
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, int userId)
throws TransactionTooLargeException {
//
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg);
//
ServiceRecord r = res.record;
//
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}
startServiceLocked 방법 에서 startServiceInnerLocked 방법 을 호출 합 니 다.startService InnerLocked 방법 을 봅 시다.
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
ProcessStats.ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
}
r.callStart = false;
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
//
return r.name;
}
startServiceInnerLocked 방법 은 내부 에서 bringUpServiceLocked 방법 을 호출 했 는데 이 때 시작 과정 은 이미 ActiveServices 를 떠 날 것 이다.bringUp Service Locked 방법 을 계속 보 세 요.다음 과 같다.
private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting) throws TransactionTooLargeException {
//
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
realStartServiceLocked(r, app, execInFg);
return null;
}
//
return null;
}
대부분의 if 판단 을 생략 하고 눈치 빠 른 당신 이 핵심 적 인 방법 을 발견 했다 고 믿 습 니 다.그것 은 바로...realStart Service Locked,맞아요.이름 을 보 니 서 비 스 를 시작 하 는 것 같 아 요.그럼 뒤 늦게 들 어가 서 알 아 보 는 게 좋 을 것 같 습 니 다.다음 과 같다.
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
//
boolean created = false;
try {
//
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
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;
}
//
sendServiceArgsLocked(r, execInFg, true);
//
}
찾 았 다.app.thread 는 scheduleCreateService 를 호출 하여 서 비 스 를 시작 합 니 다.app.thread 는 applicationThread 이자 Activity Thread 의 내부 클래스 입 니 다.이 때 는 이미 메 인 스 레 드 에 도착 했다.그럼 애플 리 케 이 션 Thread 의 schedule Create Service 방법 을 알 아 보 겠 습 니 다.다음 과 같다.
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 구성 요소 정 보 를 포장 하고 메 시 지 를 보 냈 습 니 다.우 리 는 이 CREATE 에 주목 합 니 다.서비스 소식 만 전해 주시 면 됩 니 다.
public void handleMessage(Message msg) {
//
case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
//
}
handle Message 방법 에서 이 메 시 지 를 받 은 다음 handle Create Service 방법 을 호출 하여 handle Create Service 에 따라 탐색 합 니 다.
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), 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);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
마침내 격파 하 는 이 방법 은 매우 핵심 적 이다.조금씩 분석 하 다우선 LoadedApk 대상 을 가 져 옵 니 다.이 LoadedApk 대상 을 통 해 클래스 로 더 를 가 져 옵 니 다.이 클래스 로 더 를 통 해 Service 를 만 듭 니 다.다음 과 같다.
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
이 어 ContextImpl 의 createAppContext 방법 을 호출 하여 ContextImpl 대상 을 만 들 었 습 니 다.그 다음 에 LoadedApk 의 MakeApplication 방법 을 사용 하여 Application 을 만 듭 니 다.이 생 성 과정 은 다음 과 같 습 니 다.
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
Application app = null;
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to instantiate application " + appClass
+ ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
// Rewrite the R 'constants' for all library apks.
SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
.getAssignedPackageIdentifiers();
final int N = packageIdentifiers.size();
for (int i = 0; i < N; i++) {
final int id = packageIdentifiers.keyAt(i);
if (id == 0x01 || id == 0x7f) {
continue;
}
rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
}
return app;
}
물론 애플 리 케 이 션 은 하나 뿐 이 며,상기 코드 에서 도 볼 수 있다.돌아 와 서 handle Create Service 방법 을 계속 본 후에 service 는 attach 방법 으로 ContextImpl 과 Application 등 을 연결 했다.
마지막 으로 service 는 onCreate 방법 을 되 돌 렸 습 니 다.
service.onCreate();
mServices.put(data.token, service);
이 서 비 스 를 목록 에 추가 하여 관리 합 니 다.이로써 서비스 가 시작 되 었 습 니 다.이상 은 서비스의 시작 과정 입 니 다.
onStartCommand 방법 이 어떻게 바 뀌 었 는 지 알 고 싶 을 수도 있 습 니 다.세심 한 당신 은 ActiveServices 의 realStart Service Locked 방법 중 하나 인 sendServiceArgs Locked 방법 을 발 견 했 을 것 입 니 다.네,그게 입구 예요.
그럼 sendServiceArgs Locked 방법 을 따라 가서 onStart Command 방법 이 어떻게 바 뀌 었 는 지 봅 시다.
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
final int N = r.pendingStarts.size();
//
try {
//
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
} catch (TransactionTooLargeException e) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Transaction too large: intent="
+ si.intent);
caughtException = e;
} catch (RemoteException e) {
// Remote process gone... we'll let the normal cleanup take care of this.
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while sending args: " + r);
caughtException = e;
}
//
}
onStartCommand 방법 리 셋 과정 과 onCreate 방법 이 비슷 한 것 을 볼 수 있 습 니 다.모두 app.thread 로 전 환 됩 니 다.그럼 지금 애플 리 케 이 션 스 레 드 의 schedule ServiceArgs 를 따라 가 겠 습 니 다.또 Service 메 시 지 를 봉인 하고 메 시 지 를 보 내 handle Message 를 받 아야 한 다 는 것 을 알 수 있 습 니 다.예,소스 코드 는 다음 과 같 습 니 다.
public final void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
int flags ,Intent args) {
ServiceArgsData s = new ServiceArgsData();
s.token = token;
s.taskRemoved = taskRemoved;
s.startId = startId;
s.flags = flags;
s.args = args;
sendMessage(H.SERVICE_ARGS, s);
}
public void handleMessage(Message msg) {
//
case SERVICE_ARGS:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStart");
handleServiceArgs((ServiceArgsData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
//
}
어,정말 그래.수수 께 끼 는 handle ServiceArgs 방법 에 있 을 것 입 니 다.그럼 빨리 보 세 요.소스 코드 는 다음 과 같 습 니 다.
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
data.args.prepareToEnterProcess();
}
int res;
if (!data.taskRemoved) {
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
QueuedWork.waitToFinish();
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
} catch (RemoteException e) {
// nothing to do.
}
ensureJitEnabled();
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to start service " + s
+ " with " + data.args + ": " + e.toString(), e);
}
}
}
}
onStartCommand 를 되 돌 리 는 방법 을 볼 수 있 습 니 다.이상 은 Service 의 시작 과정의 소스 코드 분석 입 니 다.
그 중에서 저 는 Service 의 시작 과정 을 이해 하 는 동시에 소스 코드 를 읽 는 능력 도 향상 되 었 습 니 다.소스 코드 를 분석 할 때 저 는 모든 변 수 를 이해 할 능력 이 없습니다.모든 방법 을 이해 할 수 있 습 니 다.제 가 주목 하 는 것 은 모두 관건 적 인 단어 입 니 다.예 를 들 어 이 글 이 start 야,service 야.그런 느낌 이 들 거 야.바로 여기 가 맞 아.물론 골목 에 빠 지면 털 어 내야 지.
이런 분석 도 전체적인 과정 을 파악 할 수 있 고 세부 적 인 부분 에 대해 저 는 탄탄 한 기 초 를 가지 고 연구 하 겠 습 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.