Android 에서 응용 되 는 시작 프로 세 스 분석

머리말
우리 가 시작 하기 전에 다음 과 같은 조건 을 만족 시 키 는 것 이 좋 겠 습 니 다.
     1.컴 파일 된 Android 소스 코드 가 있 습 니 다(직접 실천 해 야 더 깊이 이해 할 수 있 습 니 다)
     2.Binder 메커니즘 에 대해 어느 정도 알 고 있 음
본 논문 의 시작 절차 분석 은 안 드 로 이 드 5.1 의 소스 코드 를 바탕 으로 한다.왜 5.1 의 소스 입 니까?손 에 컴 파일 된 코드 는 이 버 전 밖 에 없 기 때문에....................................................................................
1.시작 순서 그림
가 벼 운 강박 증 을 가 진 사람 으로서 정리 한 순서 도 는 모두 가 그림 에 따라 준 마 를 찾 으 면 반드시 전체 작 동 절 차 를 이해 할 수 있 을 것 이 라 고 믿는다.

설명:전체 과정 을 더 잘 이해 할 수 있 도록 순서 도 를 세 부분 으로 나 누 었 습 니 다.Launcher 프로 세 스,System 프로 세 스,App 프로 세 스,그 중에서 공용 과 관련 된 클래스 가 L/A 로 구분 되 어 어느 프로 세 스 와 관련 이 있 는 지 이해 하기 쉽 습 니 다.
2.관건 적 인 설명
전체 시작 프로 세 스 는 여러 번 Binder 통신 과 관련 되 기 때문에 여기 서 몇 가지 용 도 를 간략하게 설명 하고 전체 상호작용 프로 세 스 를 이해 할 수 있 습 니 다.
      1.Activity Manager Service:AMS 는 Android 에서 가장 핵심 적 인 서비스 중 하나 로 주로 시스템 에서 4 대 구성 요소 의 시작,전환,스케줄 링 과 응용 프로 세 스 의 관리 와 스케줄 링 등 업 무 를 담당 하 는데 그 직책 은 운영 체제 의 프로 세 스 관리 와 스케줄 링 모듈 과 유사 하기 때문에 Android 에서 매우 중요 하고 그 자체 도 Binder 의 실현 유형 이다.
      2.Instrumentation:말 그대로 응용 프로그램 과 시스템 의 상호작용 을 모니터링 하 는 데 사 용 됩 니 다.
      3.Activity Thread:응 용 된 입구 류,시스템 은 main 함 수 를 호출 하여 메시지 순환 대기 열 을 엽 니 다.Activity Thread 가 있 는 스 레 드 를 주 스 레 드(UI 스 레 드)라 고 합 니 다.
      4.ApplicationThread:ApplicationThread 는 Binder 통신 인 터 페 이 스 를 제공 하고 AMS 는 이 앱 프로 세 스 의 로 컬 방법 을 프 록 시 를 통 해 호출 합 니 다.
      5.Activity Manager Proxy:AMS 서 비 스 는 현재 프로 세 스 의 프 록 시 클래스 에서 AMS 와 통신 하 는 것 을 책임 집 니 다.
      6.ApplicationThreadProxy:ApplicationThread 는 AMS 서비스 에서 의 프 록 시 클래스 로 ApplicationThread 와 통신 합 니 다.
3.절차 분석
먼저 전체 프로 세 스 분석 장면 을 설명 합 니 다.사용 자 는 Launcher 의 응용 아이콘 을 클릭 하여 이 응용 메 인 인터페이스 에서 시작 하여 사용자 의 눈앞 에 보 여 줍 니 다.
이 전체 과정 은 크로스 프로 세 스 통신 과 관련 되 기 때문에 우 리 는 이 를 순서 그림 에서 보 여 주 는 세 개의 프로 세 스 로 나 누 었 다.Launcher 프로 세 스,System 프로 세 스,App 프로 세 스.너무 긴 코드 를 붙 이지 않 고 프로 세 스 간 의 상호작용 절 차 를 분명하게 말 하기 위해 몇 가지 중요 한 상호작용 점 을 약술 한다.
타 이 밍 도 에서 볼 수 있 듯 이 호출 체인 이 상당히 길 고 해당 하 는 코드 의 양도 비교적 많 으 며 타 이 밍 도 는 이 장면 에서 의 절 차 를 분 석 했 을 뿐이다.길이 막 히 고 길 면 곧 옵 니 다!
3.1 Launcher 응답 사용자 클릭,알림 AMS
Launcher 는 응용 프로그램의 입구 로 서 설명 할 필요 가 있 습 니 다.Launcher 의 코드 세 션 을 살 펴 보 겠 습 니 다.Launcher 는 packages/apps/Launcher 3 의 소스 코드 를 사용 합 니 다.

public class Launcher extends Activity
  implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
     View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
 ...
 /**
  * Launches the intent referred by the clicked shortcut.
  *
  * @param v The view representing the clicked shortcut.
  */
 public void onClick(View v) {
  // Make sure that rogue clicks don't get through while allapps is launching, or after the
  // view has detached (it's possible for this to happen if the view is removed mid touch).
  if (v.getWindowToken() == null) {
   return;
  }

  ...

  Object tag = v.getTag();
  if (tag instanceof ShortcutInfo) {
   onClickAppShortcut(v);
  } else if (tag instanceof FolderInfo) {
   ...
  } else if (v == mAllAppsButton) {
   onClickAllAppsButton(v);
  } else if (tag instanceof AppInfo) {
   startAppShortcutOrInfoActivity(v);
  } else if (tag instanceof LauncherAppWidgetInfo) {
   ...
  }
 }
 
 private void startAppShortcutOrInfoActivity(View v) {
  ...
  boolean success = startActivitySafely(v, intent, tag);
  ...
 }
 
 boolean startActivitySafely(View v, Intent intent, Object tag) {
  ...
  try {
   success = startActivity(v, intent, tag);
  } catch (ActivityNotFoundException e) {
   ...
  }
  return success;
 }
 
 boolean startActivity(View v, Intent intent, Object tag) {
  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  try {
   ...

   if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
    // Could be launching some bookkeeping activity
    startActivity(intent, optsBundle);
   } else {
    ...
   }
   return true;
  } catch (SecurityException e) {
   ...
  }
  return false;
 }
}
starActivity 를 통 해 전전 호출Activity:startActivityForResult후 호출Instrumentation:execStartActivity,코드 세 션 은 다음 과 같 습 니 다.

public class Instrumentation {
 ...
 public ActivityResult execStartActivity(
   Context who, IBinder contextThread, IBinder token, Activity target,
   Intent intent, int requestCode, Bundle options) {
  IApplicationThread whoThread = (IApplicationThread) contextThread;
  ...
  try {
   ...
   int result = ActivityManagerNative.getDefault()
    .startActivity(whoThread, who.getBasePackageName(), intent,
      intent.resolveTypeIfNeeded(who.getContentResolver()),
      token, target != null ? target.mEmbeddedID : null,
      requestCode, 0, null, options);
   ...
  } catch (RemoteException e) {
  }
  return null;
 }
 ...
}
이곳ActivityManagerNative.getDefault은 원 격 인터페이스,즉ActivityManagerService인 터 페 이 스 를 되 돌려 주 고 왜ActivityManagerProxy인지 물 어 볼 수도 있다.이 는 Binder 통신 과 관련 된 것 이 고 여 기 는 더 이상 전개 되 지 않 는 다.Binder 드라이버 를 통 해ActivityManagerProxyAMS 서비스 와 통신 하면 크로스 프로 세 스 를 System 프로 세 스 로 실현 합 니 다.
3.2 AMS 응답 Launcher 프로 세 스 요청
위의 절 차 를 통 해 알 수 있 듯 이 이때 AMS 는 Launcher 프로 세 스 가 보 낸 요청 을 처리 해 야 합 니 다.순서 그림 과 소스 코드 를 참조 하 십시오.이때 우 리 는ActivityManagerProxy방법 을 살 펴 보 겠 습 니 다.이 방법 은 600 줄 이 넘 는 코드 를 눈대중 하여 관건 적 인 코드 세 션 을 보 겠 습 니 다.

public final class ActivityStackSupervisor implements DisplayListener {
 ...
 final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
   IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, 
 int startFlags, boolean doResume, Bundle options, TaskRecord inTask) {
 final Intent intent = r.intent;
 final int callingUid = r.launchedFromUid;
 ...
 final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
 final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK; 

 int launchFlags = intent.getFlags();
 ...
 // We'll invoke onUserLeaving before onPause only if the launching
 // activity did not explicitly state that this is an automated launch.
 mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
 ...
   
   ActivityRecord notTop =
    (launchFlags & Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP) != 0 ? r : null;

 // If the onlyIfNeeded flag is set, then we can do this if the activity
 // being launched is the same as the one making the call... or, as
 // a special case, if we do not know the caller then we count the
 // current top activity as the caller.
 if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
 ...
 }
   ...
   // If the caller is not coming from another activity, but has given us an
 // explicit task into which they would like us to launch the new activity,
 // then let's see about doing that.
 if (sourceRecord == null && inTask != null && inTask.stack != null) {
 final Intent baseIntent = inTask.getBaseIntent();
 final ActivityRecord root = inTask.getRootActivity();
 ...
 // If this task is empty, then we are adding the first activity -- it
 // determines the root, and must be launching as a NEW_TASK.
 if (launchSingleInstance || launchSingleTask) {
 ...
 }
 ...
 }
   ...
   if (inTask == null) {
 if (sourceRecord == null) {
 // This activity is not being started from another... in this
 // case we -always- start a new task.
 if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && inTask == null) {
  Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
       "Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
  launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
 }
 } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
 // The original activity who is starting us is running as a single
 // instance... this new activity it is starting must go on its
 // own task.
 launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
 } else if (launchSingleInstance || launchSingleTask) {
 // The activity being started is a single instance... it always
 // gets launched into its own task.
 launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
 }
 }
   
 ...
   
   // We may want to try to place the new activity in to an existing task. We always
 // do this if the target activity is singleTask or singleInstance; we will also do
 // this if NEW_TASK has been requested, and there is not an additional qualifier telling
 // us to still place it in a new task: multi task, always doc mode, or being asked to
 // launch this as a new task behind the current one.
 if (((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
    (launchFlags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
    || launchSingleInstance || launchSingleTask) {
 // If bring to front is requested, and no result is requested and we have not
 // been given an explicit task to launch in to, and
 // we can find a task that was started with this same
 // component, then instead of launching bring that one to the front.
 if (inTask == null && r.resultTo == null) {
 // See if there is a task to bring to the front. If this is
 // a SINGLE_INSTANCE activity, there can be one and only one
 // instance of it in the history, and it is always in its own
 // unique task, so we do a special search.
 ActivityRecord intentActivity = !launchSingleInstance ?
  findTaskLocked(r) : findActivityLocked(intent, r.info);
 if (intentActivity != null) {
  ...
 }
 }
 }
  
 ...
   
   if (r.packageName != null) {
   // If the activity being launched is the same as the one currently
   // at the top, then we need to check if it should only be launched
   // once.
   ActivityStack topStack = getFocusedStack();
   ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(notTop);
   if (top != null && r.resultTo == null) {
    if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
     ...
    }
   }
   } else{
   ...
   }
  
 boolean newTask = false;
 boolean keepCurTransition = false;

 TaskRecord taskToAffiliate = launchTaskBehind && sourceRecord != null ?
    sourceRecord.task : null;

 // Should this be considered a new task?
 if (r.resultTo == null && inTask == null && !addingToTask
    && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
 ...
 if (reuseTask == null) {
    r.setTask(targetStack.createTaskRecord(getNextTaskId(),
      newTaskInfo != null ? newTaskInfo : r.info,
      newTaskIntent != null ? newTaskIntent : intent,
      voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
      taskToAffiliate);
    ...
   } else {
    r.setTask(reuseTask, taskToAffiliate);
   }
    ...
 } else if (sourceRecord != null) {
   
 } else if (!addingToTask &&
     (launchFlags&Intent.FLAG_ACTIVITY_REORDER_TO_FRONT) != 0) {
      
 } else if (inTask != null){
   
 } else {
   
 }
  
 ...
  
 targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
   
 ...
   return ActivityManager.START_SUCCESS;
 }
 ...
}
함 수 는 intent 의 플래그 값 설정 을 통 해ActivityStackSupervisor:startActivityUncheckedLocked함 수 를 통 해 이러한 Task 가 저장 되 어 있 는 지 찾 습 니 다.여기 서 돌아 온 결 과 는 null,즉findTaskLockednull 입 니 다.따라서 새로운 Task 를 만들어 서 시작 해 야 합 니 다intentActivity.현재 스 택 상단 을 처리 하 는ActivityActivity입 니 다.우리 가 시작 할Launcher과 같은MainActivity이 아 닙 니 다.새로운 Task 를 만 들 었 습 니 다Activity.
스 택 지붕 검 사 를 통 해 Launcher 를 Paused 상태 로 밀어 넣 어야 새로운Activity을 시작 할 수 있 습 니 다.다음 에는Activity로 호출 됩 니 다.이 함 수 를 살 펴 보 겠 습 니 다.

final class ActivityStack {
 ...
 final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
   boolean dontWait) {
 if (mPausingActivity != null) {
  ...
 } 
 ActivityRecord prev = mResumedActivity;
 if (prev == null) {
  ...
 }
 ...
 mResumedActivity = null;
 mPausingActivity = prev;
 mLastPausedActivity = prev;
 mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
    || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
 prev.state = ActivityState.PAUSING;
 ...
 
 if (prev.app != null && prev.app.thread != null) {
  try {
 ...
 prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
      userLeaving, prev.configChangeFlags, dontWait);
  } catch (Exception e) {
 ...
  }
 } else {
  ...
 }
 ...
 }
 ...
}
이곳ActivityStack:startPausingLockedprev.app.thread대상 의 원 격 인터페이스 로 이 원 격 인터페이스ApplicationThread를 호출 하여 Launcher 에 Paused 상태 로 들 어 오 라 고 알 립 니 다.이로써 AMS 가 Launcher 에 대한 요청 에 응 했 습 니 다.이것 은 우리 가 Binder 통신 을 통 해 Launcher 프로 세 스 로 되 돌아 간 것 을 발 견 했 습 니 다.
3.3 Launcher 프로 세 스 가 Launcher 를 끊 고 AMS 에 다시 알려 줍 니 다.
이 절 차 는 상대 적 으로 간단 할 것 이다.우리 가 보기에schedulePauseActivity:

public final class ActivityThread {
 ...
 private void handlePauseActivity(IBinder token, boolean finished,
   boolean userLeaving, int configChanges, boolean dontReport) {
  ActivityClientRecord r = mActivities.get(token);
  if (r != null) {
   ...
   performPauseActivity(token, finished, r.isPreHoneycomb());

   // Make sure any pending writes are now committed.
   if (r.isPreHoneycomb()) {
    QueuedWork.waitToFinish();
   }

   // Tell the activity manager we have paused.
   if (!dontReport) {
    try {
     ActivityManagerNative.getDefault().activityPaused(token);
    } catch (RemoteException ex) {
    }
   }
   ...
  }
 }
 ...
}
이 부분 Launcher 의ActivityThread처리 페이지 Paused 를 통 해 다시ActivityThreadAMS 에 알 립 니 다.
3.4 AMS 새 프로 세 스 만 들 기
새 프로 세 스 를 만 들 때 AMS 는ActivityManagerProxy정 보 를 저장 합 니 다.프로그램 에 있 는 AndroidManifest.xml 프로필 에 애플 리 케 이 션 탭 의 process 속성 이 지정 되 어 있 지 않 으 면 시스템 은 기본적으로 package 이름 을 사용 합 니 다.모든 프로그램 은 자신의 uid 가 있 기 때문에 uid+process 의 조합 은 모든 프로그램 에 하나의ProcessRecord을 만 들 수 있 습 니 다.

public final class ActivityManagerService extends ActivityManagerNative
  implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
 ...
 private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
 ...
 try {
  ...
  // Start the process. It will either succeed and return a result containing
  // the PID of the new process, or else throw a RuntimeException.
  boolean isActivityProcess = (entryPoint == null);
  if (entryPoint == null) entryPoint = "android.app.ActivityThread";
  Process.ProcessStartResult startResult = Process.start(entryPoint,
     app.processName, uid, uid, gids, debugFlags, mountExternal,
     app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
     app.info.dataDir, entryPointArgs);
  ...
 } catch () {
  ...
 }
 }
 ...
}
이것 은 주로ProcessRecord인 터 페 이 스 를 호출 하여 새로운 프로 세 스 를 만 듭 니 다.새로운 프로 세 스 는Process:start클래스 를 가 져 오고android.app.ActivityThread함 수 를 실행 합 니 다.이것 은 모든 프로그램 이 하나의main인 스 턴 스 를 가지 고 대응 하 는 이유 입 니 다.
3.5 응용 프로 세 스 초기 화
우 리 는ActivityThreadActivity함 수 를 보 았 습 니 다.여 기 는 메 인 스 레 드 의 Looper 를 연결 하고 메시지 순환 에 들 어 갔 습 니 다.전체 Android 시스템 이 메시지 구동 이라는 것 을 알 아야 합 니 다.이것 도 메 인 스 레 드 가 기본적으로 Looper 를 연결 하 는 이유 입 니 다.

public final class ActivityThread {
 ...
 public static void main(String[] args) {
  ...
  Looper.prepareMainLooper();

  ActivityThread thread = new ActivityThread();
  thread.attach(false);

  ...

  Looper.loop();
  
  ...
 }
 
 private void attach(boolean system) {
  ...
  if (!system) {
   ...
   final IActivityManager mgr = ActivityManagerNative.getDefault();
   try {
    mgr.attachApplication(mAppThread);
   } catch (RemoteException ex) {
    // Ignore
   }
  } else {
   ...
  }
  ...
 }
 ...
}
attach 함수 가 최종 적 으로main원 격 인터페이스ActivityManagerService함 수 를 호출 했 습 니 다.들 어 오 는 매개 변 수 는ActivityManagerProxy attachApplication입 니 다.이것 은mAppThread유형의ApplicationThread대상 입 니 다.AMS 와 응용 프로 세 스 간 의 통신 역할 을 합 니 다.
3.6 AMS 에 응용 프로 세 스 를 등록 하고 스 택 상단 페이지 를 시작 합 니 다.
앞에서 언급 했 듯 이 AMS 는 시스템 에서 4 대 구성 요소 의 시작,전환,스케줄 링 과 응용 프로 세 스 의 관리 와 스케줄 링 등 업 무 를 담당 합 니 다.지난 절 차 를 통 해 우 리 는 응용 프로 세 스 가 생 성 된 후에 Binder 구동 을 통 해 AMS 와 상호작용 을 하 는 것 을 알 게 되 었 습 니 다.이때 AMS 는 응용 프로 세 스 가 생 성 된 후의 정 보 를 한 번 등록 하 였 습 니 다.윈도 시스템 프로그램 에 등 록 된 레 지 스 트 를 가지 고 이 과정 을 이해 하면 좀 더 이미지 가 있 을 수 있 습 니 다.Binder스 택 상단 에서 시작 할mMainStack.topRunningActivityLocked(null)을 꺼 내 고Activity함수 에서realStartActivityLockedhan앱 프로 세 스 시작 페이지 를 되 돌려 줍 니 다.

public final class ActivityStackSupervisor implements DisplayListener {
 ...
 final boolean realStartActivityLocked(ActivityRecord r,
   ProcessRecord app, boolean andResume, boolean checkConfig)
   throws RemoteException {
  ...
  r.app = app;
  ...
   
  try {
  ...
  app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
     System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
     r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState,
     r.icicle, r.persistentState, results, newIntents, !andResume,
     mService.isNextTransitionForward(), profilerInfo);
  ...
  } catch (RemoteException e) {
  ...
  }
  ...
 }
 ...
}
이때 App 프로 세 스에 서 우 리 는 일부 열의 호출 체인 을 거 쳐 최종 적 으로ApplicationThreadProxy함수 로 호출 된 후에MainActivity:onCreate로 호출 되 고 AMS 에 이onResume가 이미MainActivity상태 에 있다 는 것 을 알 릴 수 있다.이로써 전체 가동 절 차 는 일 단락 되 었 다.
4.총화
상기 절 차 를 통 해 여러분 들 이 기본 적 인 인식 을 가 질 수 있 을 것 이 라 고 믿 습 니 다.여기 서 우 리 는 세부 적 인 절 차 를 무시 하고 단순히 프로 세 스 측면 에서 다음 그림 을 볼 수 있 습 니 다.launchapp_sim

그림 에 그 려 진 것 은 여기 서 군말 이 아 닙 니 다.Activity 가 시 작 된 후에 Resume 상태 까지 이 때 상호작용 을 할 수 있 습 니 다.이상 은 안 드 로 이 드 에서 시작 프로 세 스 를 응용 하 는 모든 내용 을 분석 한 것 입 니 다.어떻게 의문 이 있 으 십 니까?여러분 의 지적 과 교 류 를 환영 합 니 다.

좋은 웹페이지 즐겨찾기