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 드라이버 를 통 해ActivityManagerProxy
AMS 서비스 와 통신 하면 크로스 프로 세 스 를 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,즉findTaskLocked
null 입 니 다.따라서 새로운 Task 를 만들어 서 시작 해 야 합 니 다intentActivity
.현재 스 택 상단 을 처리 하 는Activity
은Activity
입 니 다.우리 가 시작 할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:startPausingLocked
은prev.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 를 통 해 다시ActivityThread
AMS 에 알 립 니 다.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 응용 프로 세 스 초기 화
우 리 는
ActivityThread
의Activity
함 수 를 보 았 습 니 다.여 기 는 메 인 스 레 드 의 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 상태 까지 이 때 상호작용 을 할 수 있 습 니 다.이상 은 안 드 로 이 드 에서 시작 프로 세 스 를 응용 하 는 모든 내용 을 분석 한 것 입 니 다.어떻게 의문 이 있 으 십 니까?여러분 의 지적 과 교 류 를 환영 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.