Android Activity 의 시작 프로 세 스 원본 분석
Activity 는 Android 에서 매우 중요 한 개념 으로 4 대 구성 요소 의 첫 번 째 라 고 할 수 있 습 니 다.Activity 에 대해 많은 내용 이 있 습 니 다.예 를 들 어 생명 주기 와 Flags 를 시작 하 는 것 입 니 다.이 두 가 지 는 분명히 말 하고 싶 습 니 다.아마도 두 편의 긴 글 을 써 야 할 것 입 니 다.게다가 그들의 소스 코드 를 분석 해 야 할 것 입 니 다.그러나 본 논문 의 중심 은 그들 이 아니 라 제 가 소개 하고 자 하 는 것 은 Activity 의 전형 적 인 작 동 과정 입 니 다.본 고 는 소스 코드 의 측면 에서 이 를 분석 할 것 입 니 다.startActivity 가 호출 될 때 Activity 를 시작 할 수 있다 는 것 을 알 고 있 습 니 다.그런데 이 Activity 가 어떻게 시작 되 는 지 아 십 니까?모든 Activity 도 하나의 대상 입 니 다.이 대상 이 언제 만 들 어 졌 는 지 아 세 요?왜 onCreate 가 Activity 의 실행 입구 입 니까?이 모든 것 이 시스템 에 의 해 봉 인 됩 니 다.우리 에 게 투명 합 니 다.우리 가 사용 할 때 하나의 intent 를 전달 한 다음 에 startActivity 만 목적 을 달성 할 수 있 습 니 다.그러나 본 고 를 읽 은 후에 그 뒤에 어떤 일 을 했 는 지 알 게 될 것 입 니 다.분석 하기 전에 나 는 먼저 몇 가지 유형 을 소개 한다.
일단 입 구 를 볼 게 요.
code:Activity#startActivity
@Override
public void startActivity(Intent intent) {
startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
public void startActivityForResult(Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
설명:위 에서 아래로,결국 startActivity ForResult 로 이 루어 진 것 이 분명 합 니 다.이어서 보다
code:Activity#startActivityForResult
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
// Activity mParent null,mParent ActivityGroup ,ActivityGroup
if (mParent == null) {
// Activity, mMainThread.getApplicationThread()
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
// , onActivityResult
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
final View decor = mWindow != null ? mWindow.peekDecorView() : null;
if (decor != null) {
decor.cancelPendingInputEvents();
}
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
// ActivityGroup Activity startActivity ,
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
설명:상기 코드 의 관건 은 모두 주석 이 있 습 니 다.activity 를 진정 으로 여 는 것 이 Instrumentation 의 exec Start Activity 방법 에서 이 루어 진 것 을 발견 할 수 있 습 니 다.code:Instrumentation#execStartActivity
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
// whoThread , scheduleLaunchActivity activity
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
synchronized (mSync) {
// activity
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
//
am.mHits++;
// activity , return
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess();
// activity , whoThread 。
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, null, options);
// , , activity,
// ActivityNotFoundException
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
}
return null;
}
설명:이 방법 을 다시 한 번 말씀 드 리 고 싶 습 니 다.checkStart Activity Result 도 전문 적 으로 이상 한 것 을 던 집 니 다.코드 를 보면 아래 의 이상 한 정보 가 낯 설 지 않 을 것 이 라 고 믿 습 니 다.바로 이 방법 입 니 다.그 중에서 가장 익숙 한 것 은 Unable to find explicit activity class 입 니 다.xml 에 목표 activity 를 등록 하지 않 으 면 이 이상 은 던 집 니 다.
/*package*/ static void checkStartActivityResult(int res, Object intent) {
if (res >= ActivityManager.START_SUCCESS) {
return;
}
switch (res) {
case ActivityManager.START_INTENT_NOT_RESOLVED:
case ActivityManager.START_CLASS_NOT_FOUND:
if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
+ ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
case ActivityManager.START_PERMISSION_DENIED:
throw new SecurityException("Not allowed to start activity "
+ intent);
case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
throw new AndroidRuntimeException(
"FORWARD_RESULT_FLAG used while also requesting a result");
case ActivityManager.START_NOT_ACTIVITY:
throw new IllegalArgumentException(
"PendingIntent is not an activity");
default:
throw new AndroidRuntimeException("Unknown error code "
+ res + " when starting " + intent);
}
}
다음은 IApplicationThread 를 살 펴 보 겠 습 니 다.핵심 기능 은 내부 의 scheduleLaunchActivity 방법 으로 이 루어 집 니 다.IApplicationThread 는 인터페이스 이기 때문에 실현 류 를 찾 아야 합 니 다.제 가 찾 아 드 렸 습 니 다.이것 이 바로 Activity Thread 의 내부 류 ApplicationThread 입 니 다.계승 관 계 를 보 겠 습 니 다.private class ApplicationThread extends ApplicationThreadNative;
public abstract class ApplicationThreadNative extends Binder implements IApplicationThread;
애플 리 케 이 션 스 레 드 는 IApplicationThread 인 터 페 이 스 를 간접 적 으로 실현 한 것 으로 나 타 났 다.먼저 이러한 구 조 를 살 펴 보 자.
ApplicationThread 의 대체적인 구 조 를 보면 Activity 의 생명주기 중의 resume,newIntent,pause,stop 등 사건 이 모두 그것 에 의 해 촉발 되 었 다 는 것 을 추측 할 수 있 을 것 이다.사실은 그렇다.여기 서 우 리 는 문 제 를 설명 하기 위해 scheduleLaunchActivity 방법 만 본다.
code:ApplicationThread#scheduleLaunchActivity
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
int procState, Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
updateProcessState(procState, false);
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profileFile = profileName;
r.profileFd = profileFd;
r.autoStopProfiler = autoStopProfiler;
updatePendingConfiguration(curConfig);
queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
}
설명:상기 코드 는 이해 하기 쉽 고 activity 기록 을 작성 한 다음 에 메 시 지 를 보 냅 니 다.그래서 우 리 는 Handler 가 이 메 시 지 를 어떻게 처리 하 는 지 봐 야 합 니 다.지금 이 Handler 로 넘 어가 면 아주 짧 은 이름 이 H 라 고 합 니 다.code:ActivityThread#H
// ,
private class H extends Handler {
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
// LAUNCH_ACTIVITY
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
// startActivity
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;
handleRelaunchActivity(r);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case PAUSE_ACTIVITY:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
maybeSnapshot();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
}
설명:handleLaunchActivity 도 봐 야 할 것 같 아 요.code:ActivityThread#handleLaunchActivity
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
if (r.profileFd != null) {
mProfiler.setProfiler(r.profileFile, r.profileFd);
mProfiler.startProfiling();
mProfiler.autoStopProfiler = r.autoStopProfiler;
}
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
// , , ,
//performLaunchActivity activity ,
// activity , onCreate
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
// , activity onResume
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out
// paused, because it needs to be visible but isn't in the
// foreground. We accomplish this by going through the
// normal startup (because activities expect to go through
// onResume() the first time they run, before their window
// is displayed), and then pausing it. However, in this case
// we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just
// retain the current state it has.
try {
r.activity.mCalled = false;
// , activity , activity onPause
mInstrumentation.callActivityOnPause(r.activity);
// We need to keep around the original state, in case
// we need to be created again. But we only do this
// for pre-Honeycomb apps, which always save their state
// when pausing, so we can not have them save their state
// when restarting from a paused state. For HC and later,
// we want to (and can) let the state be saved as the normal
// part of stopping the activity.
if (r.isPreHoneycomb()) {
r.state = oldState;
}
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPause()");
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
"Unable to pause activity "
+ r.intent.getComponent().toShortString()
+ ": " + e.toString(), e);
}
}
r.paused = true;
}
} else {
// If there was an error, for any reason, tell the activity
// manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null);
} catch (RemoteException ex) {
// Ignore
}
}
}
설명:원래 activity 와 새로운 activity 간 의 상태 동기 화 에 대해 여러분 이 관심 이 있 으 면 스스로 연구 할 수 있 습 니 다.논리 가 너무 복잡 하기 때문에 저 는 모든 문 제 를 분명하게 말 할 수 없습니다.그렇지 않 으 면 디 테 일 한 부분 에 너무 깊이 들 어가 전체적인 논 리 를 파 묻 히 고 소스 코드 를 연구 하 는 데 필요 한 것 은 전체적인 논 리 를 잘 알 아야 합 니 다.다음은 마지막 방법 을 보 겠 습 니 다.이 방법 은 activity 의 시작 과정의 진정한 실현 입 니 다.code:ActivityThread#performLaunchActivity
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
// intent activity
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
// ClassLoader( ) activity newInstance
// Activity new , new 。
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource()
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
// activity onCreate , ,Activity , Activity ,
// , ApplicationThread
mInstrumentation.callActivityOnCreate(activity, r.state);
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
mInstrumentation.callActivityOnPostCreate(activity, r.state);
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
총결산이곳 을 보 았 을 때 Activity 의 작 동 과정 에 대해 감성 적 인 인식 을 가지 게 될 것 이 라 고 믿 습 니 다.Activity 는 매우 복잡 하고 특성 이 많아 서 본 고 는 각 세부 사항 에 대해 깊이 분석 할 수 없다.그리고 정말 각 세부 사항 에 대해 깊이 분석 을 했 더 라 도 그 문장 은 얼마나 길 어야 하 는 지,그리고 인내심 을 가지 고 계속 볼 수 있 는 사람 이 있 습 니까?본문 이 여러분 에 게 도움 을 줄 수 있 기 를 바 랍 니 다.읽 어 주 셔 서 감사합니다.많은 응원 부 탁 드 리 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 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에 따라 라이센스가 부여됩니다.