안드로이드 면접의Activity 시작 과정

26491 단어 Android
오늘 우리는Activity의 시작 절차를 분석해 보자.Activity가 언제 만들어졌는지, 창이 언제 만들어졌는지,Context는?
Activity를 시작할 때는 일반적으로 다음과 같습니다.
Intent intent = new Intent(MainActivity.this, TestActivity.class);
startActivity(intent);

실제로 startActivity는 Activity 클래스의 방법을 사용합니다. Activity 클래스에서 startActivity는 여러 가지 재부팅 방법이 있지만, 마지막에 사용하는 것은 모두 startActivityForResult 방법입니다.
 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {
    //mParent    ActivityGroup,   null 
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
        if (requestCode >= 0) {
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);
        // TODO Consider clearing/flushing other event sources and events for child windows.
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

위의 원본을 보십시오. 여기서 주의해야 할 것은 mParent==null 이 지점을 걷는 것입니다.mParent는 Activity Group, 즉 Activity에 Activity를 덧붙이는 방식을 대표하는데 이런 방식은 이미 Fragment에 의해 대체되었다.
이곳의 mMainThread는 바로ActivityThread이고, mMainThread이다.getapplicationThread ()는ActivityThread의 내부 클래스인 ApplicationThread입니다.ActivityThread와 ApplicationThread는Activity를 시작하는 과정에서 중요한 역할을 발휘했다.
//Instrumentation.class
public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    //      ApplicationThread    Binder
    IApplicationThread whoThread = (IApplicationThread) contextThread;
    Uri referrer = target != null ? target.onProvideReferrer() : null;
    if (referrer != null) {
        intent.putExtra(Intent.EXTRA_REFERRER, referrer);
    }
    ...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        //  ActivityManagerNative getDefault    ActivityManagerService
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        //        Activity     ,              
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

Instrumentation의execStartActivity 소스에서 볼 수 있듯이 ApplicationThread는 사실은Binder입니다. 이것은 주로Activity Manager 서비스의 프로세스에서 응용 프로세스로 리셋하는 데 사용됩니다.Activity를 동시에 시작하는 과정은 Binder 메커니즘을 통해 Activity Manager 서비스로 넘어갑니다.
Activity Manager 서비스에서의Activity의 시작 과정은 상대적으로 복잡하고 많은 이동과 처리를 거쳐야 한다. 예를 들어 시작할 Activity의 작업 창고가 존재하는지 판단해야 하고 존재하지 않으면 작업 창고를 만들어야 한다.현재 Activity를 일시 정지합니다. 시작하기 전에 Activity가 초점을 잡을 수 있도록 합니다.여기에서 우리는 원본 코드를 따라 들어가지 않아서 어지럽기 쉽다.
Activity Manager 서비스가 자신의 일을 끝낸 후에, Application Thread의 schedule LaunchActivity를 호출하여 현재 응용 프로그램의 프로세스로 프로세스를 전환시켜Activity를 시작하는 작업을 계속할 수 있도록 합니다.
//ApplicationThread.class
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ...) {

    updateProcessState(procState, false);

    ActivityClientRecord r = new ActivityClientRecord();

    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.referrer = referrer;
    r.voiceInteractor = voiceInteractor;
    r.activityInfo = info;
    ...

    sendMessage(H.LAUNCH_ACTIVITY, r);
}

ActivityClientRecord에는 시작할 Activity에 대한 정보가 저장됩니다.ApplicationThread는 Binder이기 때문에 scheduleLaunchActivity는 Binder 스레드 탱크에서 실행됩니다.handler를 통해 스레드를ActivityThread의 스레드로 전환해야 합니다.위 원본의 H는ActivityThread의Handler입니다.
private class H extends Handler {
    public static final int LAUNCH_ACTIVITY         = 100;
    public static final int PAUSE_ACTIVITY          = 101;
    public static final int STOP_ACTIVITY_SHOW      = 103;
    public static final int STOP_ACTIVITY_HIDE      = 104;
    public static final int RECEIVER                = 113;
    public static final int CREATE_SERVICE          = 114;
    public static final int SERVICE_ARGS            = 115;
    public static final int STOP_SERVICE            = 116;
    ...

    String codeToString(int code) {
        if (DEBUG_MESSAGES) {
            switch (code) {
                case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
                case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
                case RECEIVER: return "RECEIVER";
                case CREATE_SERVICE: return "CREATE_SERVICE";
                case SERVICE_ARGS: return "SERVICE_ARGS";
                case STOP_SERVICE: return "STOP_SERVICE";
                ...
            }
        }
        return Integer.toString(code);
    }
    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case LAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                r.packageInfo = getPackageInfoNoCheck(
                        r.activityInfo.applicationInfo, r.compatInfo);
                handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            ...
    }

이Handler에서 4대 구성 요소와 관련된 대량의 방법을 볼 수 있으며, 다른 구성 요소의 일부 시작 정지 등은 여기로 되돌아와 이Handler에서 처리됩니다.
저희가 Handler H 대 LAUNCH를 봤는데...ACTIVITY의 처리는ActivityThread의handleLaunchActivity 방법을 통해
//ActivityThread.class
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    ...
    Activity a = performLaunchActivity(r, customIntent);

    if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

        if (!r.activity.mFinished && r.startsNotResumed) {

            performPauseActivityIfNeeded(r, reason);

            if (r.isPreHoneycomb()) {
                r.state = oldState;
            }
        }
    } else {
        try {
            ActivityManagerNative.getDefault()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

원본에서Activity를 시작하는 과정은 마지막으로performLaunchActivity가 완성하고 완성된 후에handleResumeActivity 방법을 호출하여Activity의onResume 방법을 호출할 수 있습니다.
performLaunchActivity는 주로 다음과 같은 몇 가지 일을 완성했다.
(1) ActivityClientRecord에서 시작할 Activity 구성 요소에 대한 정보 얻기
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
    r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
            Context.CONTEXT_INCLUDE_CODE);
}

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);
}

(2) Instrumentation 클래스의 newActivity 방법을 통해 클래스 로더를 이용하여Activity를 만듭니다
Activity activity = null;
try {
    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    activity = mInstrumentation.newActivity(
            cl, component.getClassName(), r.intent);
    StrictMode.incrementExpectedActivityCount(activity.getClass());
    r.intent.setExtrasClassLoader(cl);
    r.intent.prepareToEnterProcess();
    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);
    }
}

newActivity 메서드는 클래스 로더로 클래스를 직접 로드합니다.
//newActivity            
public Activity newActivity(ClassLoader cl, String className,
        Intent intent)
        throws InstantiationException, IllegalAccessException,
        ClassNotFoundException {
    return (Activity)cl.loadClass(className).newInstance();
}

(3) LoadApk의makeApplication을 호출하여 Application 대상을 만들고 Application 대상이 이미 존재하면 다시 만들지 않습니다
public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    //      mApplication,    ,          Application
    if (mApplication != null) {
        return mApplication;
    }

    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");

    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")) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                    "initializeJavaContextClassLoader");
            initializeJavaContextClassLoader();
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        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)) {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            throw new RuntimeException(
                "Unable to instantiate application " + appClass
                + ": " + e.toString(), e);
        }
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
        try {
            //  Application OnCreate  
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!instrumentation.onException(app, e)) {
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                throw new RuntimeException(
                    "Unable to create application " + app.getClass().getName()
                    + ": " + e.toString(), e);
            }
        }
    }
    ...

    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);

    return app;
}

(4) ContextImpl 객체를 만들고 Activity의attach 방법을 통해 관련 및 기타 초기화 작업을 완성합니다
//  createBaseContextForActivity    ContextImpl
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
    config.updateFrom(r.overrideConfig);
}

Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
    window = r.mPendingRemoveWindow;
    r.mPendingRemoveWindow = null;
    r.mPendingRemoveWindowManager = null;
}
activity.attach(appContext, this, getInstrumentation(), r.token,
        r.ident, app, r.intent, r.activityInfo, title, r.parent,
        r.embeddedID, r.lastNonConfigurationInstances, config,
        r.referrer, r.voiceInteractor, window);

Activity의attach 방법은 ContextImpl 귀속, 윈도 만들기, Application 관련 작업 완료
//Activity.class
final void attach(Context context, ActivityThread aThread,
        ...
        Window window) {
    //  ContextImpl 
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);

    //  Window       
    mWindow = new PhoneWindow(this, window);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
        mWindow.setSoftInputMode(info.softInputMode);
    }
    if (info.uiOptions != 0) {
        mWindow.setUiOptions(info.uiOptions);
    }
    mUiThread = Thread.currentThread();

    mMainThread = aThread;
    mInstrumentation = instr;
    mToken = token;
    mIdent = ident;
    mApplication = application;
    mIntent = intent;

    ...
}

(5) Activity를 호출하는 onCreate 방법
activity.mCalled = false;
if (r.isPersistable()) {
    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
    mInstrumentation.callActivityOnCreate(activity, r.state);
}

이렇게 하면 Activity가 시작됩니다.
총결산
응용 프로그램에서 새로운Activity를 시작하는 과정에 대한 원본 추적을 통해 우리는 이 안에 주로 몇 가지 종류가 관련되어 있음을 발견했다. 그것이 바로Activity,ActivityThread,ActivityManager 서비스이다.
(1)Activity는 우리가 새로운 Activity를 시작하는 입구이자 마지막으로 Activity 작업이 끝난 곳이다.Activity의 startActivity를 통해 새로운Activity를 시작하기 위한 요청을 시작합니다. 마지막으로Activity의attach 방법으로Context의 귀속과 창 윈도우의 창설과 귀속을 완성합니다.
(2)ActivityThread는Activity를 시작하는 처리자이자 중간자의 역할로 다른 몇 가지 종류를 호출하여Activity를 시작하는 작업을 완성한다.우선 Binder 메커니즘을 통해Activity Manager Service를 호출하여Activity와 관련된 시스템 수준의 작업을 완성합니다. 예를 들어 작업 창고, 다른Activity를 일시 중지하는 등, 내부의 Binder 클래스 Application Thread를 통해Activity Manager Service의 프로세스 간 요청을 받고, 시작된 작업을 현재 응용 프로그램으로 되돌려줍니다.그런 다음 Instrumentation 및 LoadApk를 호출하여 Activity 클래스와 Application을 로드하는 작업을 완료합니다.마지막으로Activity의attach 방법을 호출하여 일련의 귀속 작업을 완성합니다.
(3) ApplicationThread는 Binder 클래스로 Activity Manager Service의 프로세스와 통신하는 데 사용됩니다.
(4)Activity Manager 서비스는 시스템의 서비스로Activity의 상태와 관련 정보, 예를 들어 작업 창고 등을 관리하는 데 사용된다.
데스크톱에 있는 앱 아이콘을 직접 눌러서 앱을 시작하면요?사실 이 과정은 Activity를 시작하는 것과 유사하며, 모두 Activity를 시작해야 한다.그러나 응용 프로그램을 시작하는 것은 응용 프로그램의 입구 Activity이고 데스크톱 응용 프로그램에서 다른 응용 프로그램을 시작하는 Activity이기 때문에 과정은 응용 프로그램의 입구 Activity를 찾거나 새로운 응용 프로그램 프로세스를 만들거나 작업 창고를 만들거나 데스크톱의 초점을 제거하는 등 여러 단계가 있을 것이다.이 준비 작업이 다 된 후에 뒤에는Activity를 시작하는 과정과 같다.관심 있는 아동화는 연구해 볼 수 있어요.
나의 위챗 공식계정에 관심을 가져주시는 것을 환영합니다. 나와 함께 매일 조금씩 진보합시다.

좋은 웹페이지 즐겨찾기