Android Service 시작 과정 전체 분석

서 비 스 를 처음 배 웠 을 때 는 스 레 드 패키지 라 고 생각 했 고 시간 소모 작업 도 할 수 있 었 다.사실은 그렇지 않 습 니 다.서 비 스 는 메 인 스 레 드 에서 실 행 됩 니 다.시간 소모 작업 을 직접 실행 하면 메 인 스 레 드 를 막 을 수 있 습 니 다.오랫동안 ANR 이 었 어 요.
우 리 는 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 야.그런 느낌 이 들 거 야.바로 여기 가 맞 아.물론 골목 에 빠 지면 털 어 내야 지.
이런 분석 도 전체적인 과정 을 파악 할 수 있 고 세부 적 인 부분 에 대해 저 는 탄탄 한 기 초 를 가지 고 연구 하 겠 습 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기