天天看點

Android進階筆記-7. Context詳解

Context數量

  • Activity數量 + Service數量 + 1 (1為Application)

Context的繼承關系

  • Context下有兩個子類,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實作類;
  • ContextWrapper作為Context類的包裝類,其内部維護了一個Context類型的成員變量mBase,mBase最終會指向一個ContextImpl對象,ContextWrapper的方法其内部依賴mBase,ContextWrapper是Context類的修飾類(裝飾器模式),真正的實作類是 ContextImpl,ContextWrapper 裡面的方法調用也是調用 ContextImpl 裡面的方法。又有三個直接的子類,ContextThemeWrapper, Service, Application; ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity;
  • ContextImpl中兩個比較重要的變量:
    • ActivityThread mMainThread:getApplicationContext,startActivity,getMainLooper
    • LoadedApk mPackageInfo: getApplicationContext, getPackageName, getApplicationInfo, getPackageResourcePath

Application Context 建立過程

  • 當一個應用程式啟動完成後,應用程式就會有一個全局的Application Context
  • ActivityThread作為應用程式程序的核心類,它會調用它的内部類ApplicationThread的scheduleLaunchActivity方法來啟動Activity,scheduleLaunchActivity方法中向H類發送LAUNCH_ACTIVITY類型的消息,目的是将啟動Activity的邏輯放在主線程中的消息隊列中,這樣啟動Activity的邏輯會在主線程中執行。
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
        CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
        int procState, Bundle state, PersistableBundle persistentState,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
        boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

    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;
    r.compatInfo = compatInfo;
    r.state = state;
    r.persistentState = persistentState;

    r.pendingResults = pendingResults;
    r.pendingIntents = pendingNewIntents;

    r.startsNotResumed = notResumed;
    r.isForward = isForward;

    r.profilerInfo = profilerInfo;

    r.overrideConfig = overrideConfig;
    updatePendingConfiguration(curConfig);

    sendMessage(H.LAUNCH_ACTIVITY, r);
}
           
  • H類的handleMessage方法對LAUNCH_ACTIVITY類型的消息的處理,通過getPackageInfoNoCheck方法獲得LoadedApk類型的對象,并将該對象指派給ActivityClientRecord 的成員變量packageInfo
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;
          //1.通過getPackageInfoNoCheck方法獲得LoadedApk類型的對象,并将該對象指派給ActivityClientRecord 的成員變量packageInfo
          r.packageInfo = getPackageInfoNoCheck(
              r.activityInfo.applicationInfo, r.compatInfo);
          //2. handleLaunchActivity方法中調用了performLaunchActivity
          handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
      } break;
    ...
}
           
  • handleLaunchActivity方法中調用了performLaunchActivity
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;

    if (r.profilerInfo != null) {
        mProfiler.setProfiler(r.profilerInfo);
        mProfiler.startProfiling();
    }

    // Make sure we are running with the most recent config.
    handleConfigurationChanged(null, null);

    if (localLOGV) Slog.v(
        TAG, "Handling launch of " + r);

    // Initialize before creating the activity
    WindowManagerGlobal.initialize();

    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 {
            ActivityManager.getService()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                        Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}


private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
    try {
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
       ...
    } 
    ...
    return activity;
}
           
  • performLaunchActivity中又調用了LoadedApk的makeApplication方法
public Application makeApplication(boolean forceDefaultAppClass,
        Instrumentation instrumentation) {
    if (mApplication != null) {//1
        return mApplication;
    }
    ...
    try {
      ...
       java.lang.ClassLoader cl = getClassLoader();
      ...
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);//2
        app = mActivityThread.mInstrumentation.newApplication(
                cl, appClass, appContext);//3
        appContext.setOuterContext(app);//4
    } catch (Exception e) {
       ...
    }
    mActivityThread.mAllApplications.add(app);
    mApplication = app;//5
    ...
    return app;
}
           
  • Instrumentation的newApplication方法如下
static public Application newApplication(Class<?> clazz, Context context)
        throws InstantiationException, IllegalAccessException, 
        ClassNotFoundException {
    Application app = (Application)clazz.newInstance();//1
    app.attach(context);
    return app;
}

//frameworks/base/core/java/android/app/Application.java
/* package */ final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

//frameworks/base/core/java/android/content/ContextWrapper.java
protected void attachBaseContext(Context base) {
     if (mBase != null) {
         throw new IllegalStateException("Base context already set");
     }
     mBase = base;
}
           

Application Context 擷取過程

  • 調用getApplicationContext方法來獲得Application Context,實作在ContextWrapper中
@Override
public Context getApplicationContext() {
    return mBase.getApplicationContext();
}
           
  • mBase指的是ContextImpl,我們來檢視 ContextImpl的getApplicationContext
@Override
public Context getApplicationContext() {
    return (mPackageInfo != null) ?
            mPackageInfo.getApplication() : mMainThread.getApplication();
}
           
  • 由于應用程式這時已經啟動,是以LoadedApk不會為null,則會調用LoadedApk的getApplication方法
Application getApplication() {
     return mApplication;//在上文LoadedApk的makeApplication方法的注釋5處被指派
}
           

Activity的Context建立過程

  • 前面 Application Context 建立過程講到了ActivityThread啟動Activity的過程,ActivityThread會調用它的内部類ApplicationThread的scheduleLaunchActivity方法來啟動Activity,scheduleLaunchActivity方法中向H類發送LAUNCH_ACTIVITY類型的消息,H類的handleMessage方法對LAUNCH_ACTIVITY類型的消息的處理,調用了handleLaunchActivity方法,handleLaunchActivity方法中又調用了performLaunchActivity,performLaunchActivity中調用了LoadedApk類型的packageInfo的makeApplication方法
  • 而performLaunchActivity中還有很多重要的邏輯,如下可以看到還有調用了一個createBaseContextForActivity方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
   
    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);
    }
    //建立Activity的ContextImpl
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        //建立Activity的執行個體
        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);
        }
    }

    try {
        // 前面說的調用makeApplication建立Application
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        if (activity != null) {
            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;
            }
            //調用了ContextImpl的setOuterContext方法,
            //将此前建立的Activity執行個體指派給ContextImpl的成員變量mOuterContext,
            //這樣ContextImpl也可以通路Activity的變量和方法
            appContext.setOuterContext(activity);
            //将ContextImpl傳入activity的attach方法中
            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, r.configCallback);

            if (customIntent != null) {
                activity.mIntent = customIntent;
            }
            r.lastNonConfigurationInstances = null;
            checkAndBlockForNetworkAccess();
            activity.mStartedActivity = false;
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }

            activity.mCalled = false;
            //Instrumentation的callActivityOnCreate方法中會調用Activity的onCreate方法
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                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.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            if (!r.activity.mFinished) {
                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state,
                            r.persistentState);
                } else {
                    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;
}
           
  • createBaseContextForActivity方法
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
    ...
    //調用ContextImpl的createActivityContext方法來建立ContextImpl
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

    ...
    return appContext;
}
           
  • Activity的attach方法調用了ContextThemeWrapper的attachBaseContext方法
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback) {
        
    //調用了ContextThemeWrapper的attachBaseContext方法
    attachBaseContext(context);

    mFragments.attachHost(null /*parent*/);
    //建立PhoneWindow,PhoneWindow在運作中會間接觸發很多事件,
    //比如點選事件、菜單彈出、螢幕焦點變化等事件,
    //這些事件需要轉發給與PhoneWindow關聯的Actvity,
    //轉發操作通過Window.Callback接口實作,Actvity實作了這個接口
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);//将目前Activity通過Window的setCallback方法傳遞給PhoneWindow
    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;
    mReferrer = referrer;
    mComponent = intent.getComponent();
    mActivityInfo = info;
    mTitle = title;
    mParent = parent;
    mEmbeddedID = id;
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    if (voiceInteractor != null) {
        if (lastNonConfigurationInstances != null) {
            mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;
        } else {
            mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,
                    Looper.myLooper());
        }
    }
    //給PhoneWindow設定WindowManager
    mWindow.setWindowManager(
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    //擷取WindowManager并指派給Activity的成員變量mWindowManager,
    //這樣在Activity中就可以通過getWindowManager方法來擷取WindowManager
    mWindowManager = mWindow.getWindowManager();
    mCurrentConfig = config;

    mWindow.setColorMode(info.colorMode);
}
           
  • 上面第一行就調用了ContextThemeWrapper的attachBaseContext方法,

    其中直接調用了其父類ContextWrapper的attachBaseContext方法

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(newBase);
}

protected void attachBaseContext(Context base) {
    if (mBase != null) {
        throw new IllegalStateException("Base context already set");
    }
    mBase = base;
}

//base指的是一路傳遞過來的Activity的ContextImpl,
//将它指派給ContextWrapper的成員變量mBase。
//這樣ContextWrapper的功能就可以交由ContextImpl處理,例如
@Override
public Resources.Theme getTheme() {
    return mBase.getTheme();
}
//調用ContextWrapper的getTheme方法,其實就是調用的ContextImpl的getTheme方法
           

Service的Context建立過程

  • ActivityThread的内部類ApplicationThread會調用scheduleCreateService方法來啟動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);
}
           
  • 可以看到上面也是通過向H類發送CREATE_SERVICE類型的消息,H類的handleMessage方法中會對CREATE_SERVICE類型的消息進行處理,其中調用了handleCreateService方法
case CREATE_SERVICE:
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
    handleCreateService((CreateServiceData)msg.obj);
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    break;
    
private void handleCreateService(CreateServiceData data) {
    unscheduleGcIdler();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        //建立service
        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 {
        //建立ContextImpl執行個體
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        //調用了ContextImpl的setOuterContext方法,
        //将service執行個體指派給ContextImpl的成員變量mOuterContext,
        //這樣ContextImpl也可以通路service的變量和方法
        context.setOuterContext(service);
        //建立(擷取)Application執行個體
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        //将ContextImpl執行個體傳入service的attach方法
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        //調用service的onCreate方法
        service.onCreate();
        mServices.put(data.token, service);
        try {
            ActivityManager.getService().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(service, e)) {
            throw new RuntimeException(
                "Unable to create service " + data.info.name
                + ": " + e.toString(), e);
        }
    }
}
           
  • 上面可以看到其中建立了ContextImpl,并将該ContextImpl傳入service的attach方法中
public final void attach(
        Context context,
        ActivityThread thread, String className, IBinder token,
        Application application, Object activityManager) {
        
    //調用了ContextWrapper的attachBaseContext方法
    attachBaseContext(context);
    mThread = thread;           // NOTE:  unused - remove?
    mClassName = className;
    mToken = token;
    mApplication = application;
    mActivityManager = (IActivityManager)activityManager;
    mStartCompatibility = getApplicationInfo().targetSdkVersion
            < Build.VERSION_CODES.ECLAIR;
}
           
  • 可以看到最終也是調用到了ContextWrapper的attachBaseContext方法

幾個容易混亂方法

  • 再來看一下幾個常見的擷取context的方法
  • getBaseContext是ContextWrapper中的方法,其傳回的mBase也就是上面提到的通過attachBaseContext指派的ContextImpl執行個體
/**
 * @return the base context as set by the constructor or setBaseContext
 */
public Context getBaseContext() {
    return mBase;
}
           
  • getApplicationContext是ContextWrapper中的方法,其傳回是調用了ContextImpl執行個體mBase的getApplicationContext方法,最終傳回的是一個Application執行個體
@Override
public Context getApplicationContext() {
    return mBase.getApplicationContext();
}

//ContextImpl的getApplicationContext方法
@Override
public Context getApplicationContext() {
    return (mPackageInfo != null) ?
            mPackageInfo.getApplication() : mMainThread.getApplication();
}
           
  • getApplication是Activity/service中的方法,實在上面的attach方法中指派給mApplication的Application執行個體
/** Return the application that owns this activity. */
public final Application getApplication() {
    return mApplication;
}
           
  • getContext是Fragment中的方法,傳回的是通過調用FragmentHostCallback類型的mHost的getContext方法傳回其mContext變量
@Nullable
public Context getContext() {
    return mHost == null ? null : mHost.getContext();
}
           
  • FragmentHostCallback的getContext方法
@NonNull
Context getContext() {
    return mContext;
}

FragmentHostCallback(@NonNull FragmentActivity activity) {
    this(activity, activity /*context*/, new Handler(), 0 /*windowAnimations*/);
}

FragmentHostCallback(@Nullable Activity activity, @NonNull Context context,
        @NonNull Handler handler, int windowAnimations) {
    mActivity = activity;
    mContext = Preconditions.checkNotNull(context, "context == null");
    mHandler = Preconditions.checkNotNull(handler, "handler == null");
    mWindowAnimations = windowAnimations;
}
           
  • 其調用是在
public class FragmentActivity extends ComponentActivity implements
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
    ...
    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    ...
    class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements  ViewModelStoreOwner, OnBackPressedDispatcherOwner {
        public HostCallbacks() {
            super(FragmentActivity.this /*fragmentActivity*/);
        }
        ...
    }
    ...
}
           
  • 上面可以看到其實mContext和mActivity都是通過FragmentActivity.this指派的
  • 列印log看一下
getBaseContext():androidx.appcompat.view.ContextThemeWrapper
getApplicationContext():android.app.Application
getApplication():android.app.Application
this:com.example.viewpagerdemo.MainActivity
Fragment getContext():com.example.viewpagerdemo.MainActivity
           

正确使用Context

  • 一般Context造成的記憶體洩漏,幾乎都是當Context銷毀的時候,卻因為被引用導緻銷毀失敗
  • 優先使用Application的Context,或用弱引用進行封裝
  • ImageView等都會持有上下文的引用,如果設定了static Drawable對象,就會導緻該記憶體無法釋放

activity的startActivity和context的startActivity差別?

  1. 從Activity中啟動新的Activity時可以直接mContext.startActivity(intent)就好,如果從其他Context中啟動Activity則必須給intent設定Flag:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ;
mContext.startActivity(intent);
           
原理:
  1. 首先startActivity是Context中的抽象方法
public abstract void startActivity(@RequiresPermission Intent intent);
           
  1. ContextWrapper中是調用了mBase的,也就是ContextImpl的實作
@Override
public void startActivity(Intent intent) {
    mBase.startActivity(intent);
}
           
  1. ContextImpl的實作,會先檢查有沒有設定Flag:FLAG_ACTIVITY_NEW_TASK,再調用了mMainThread.getInstrumentation().execStartActivity方法
@Override
public void startActivity(Intent intent) {
    warnIfCallingFromSystemProcess();
    startActivity(intent, null);
}



@Override
public void startActivity(Intent intent, Bundle options) {
    warnIfCallingFromSystemProcess();

    // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
    // generally not allowed, except if the caller specifies the task id the activity should
    // be launched in.
    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
            && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
        throw new AndroidRuntimeException(
                "Calling startActivity() from outside of an Activity "
                + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                + " Is this really what you want?");
    }
    mMainThread.getInstrumentation().execStartActivity(
            getOuterContext(), mMainThread.getApplicationThread(), null,
            (Activity) null, intent, -1, options);
}
           
  1. ContextThemeWrapper中沒有實作此方法
  2. Activity中重寫了startActivity方法,最終調用了mInstrumentation.execStartActivity方法,跳過了檢查FLAG_ACTIVITY_NEW_TASK
@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}

@Override
public void startActivity(Intent intent, @Nullable 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(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
    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);
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}
           
  • 當調用startActivity()方法來啟動一個Activity時,預設是将它放入到目前的任務當中。但是,如果在Intent中加入了FLAG_ACTIVITY_NEW_TASK flag的話,情況就會變的複雜起來。首先,系統會去檢查這個Activity的affinity是否與目前Task的affinity相同。如果相同的話就會把它放入到目前Task當中,如果不同則會先去檢查是否已經有一個名字與該Activity的affinity相同的Task,如果有,這個Task将被調到前台,同時這個Activity将顯示在這個Task的頂端;如果沒有的話,系統将會嘗試為這個Activity建立一個新的Task。需要注意的是,如果一個Activity在manifest檔案中聲明的啟動模式是”singleTask”,那麼他被啟動的時候,行為模式會和前面提到的指定FLAG_ACTIVITY_NEW_TASK一樣。
  • 幾種啟動模式
類型 含義 說明
standard 标準模式 每啟動會建立一個新 Activity 執行個體并置于棧頂。誰啟動了這個 Activity,那麼這個 Activity 就運作在啟動它的那個 Activity 所在的棧中。
singleTop 棧頂模式 如果棧頂存在該activity的執行個體,則複用,不存在建立放入棧頂,例如通知欄點選後需要啟動一個活動頁
singleTask 棧内複用模式 如果棧記憶體在該 Activity 的執行個體則進行複用(會将該執行個體上邊的 Activity 全部出棧,将該執行個體置于棧頂),如果不存在則建立。例如主 Activity 的啟動模式改為棧内複用,再跳轉到另一個二級頁,按home鍵回到桌面,再切回,二級頁會被銷毀。
singleInstance 單執行個體模式 這種模式啟動的activity隻能單獨的位于一個任務棧中,在整個應用中僅存在單個執行個體,APP首頁/首頁/呼叫來電界面
singleInstancePerTask 棧内根單例 每個任務裡存在于根部的單個執行個體,多視窗并排功能的時候可以使用,例如:Chrome 浏覽器的多視窗
  • 啟動模式優先級: Intent标記 > AndroidManifest檔案

Android12 中 Activity 生命周期的變化

  • Android 12 以前,當我們處于 Root Activity 時,點選傳回鍵時,應用傳回桌面, Activity 執行 onDestroy,程式結束。 Android 12 起同樣場景下 Activity 隻會 onStop,不再執行 onDestroy。其他 Activity 點選傳回鍵後行為不變,依然會 onDestroy
  • ViewModel 的銷毀在 onDestroy 中,這樣改動後 ViewModel 中的狀态可以儲存,再次啟動後可以直接使用。對于使用者來說直接感受就是冷啟動變為了熱啟動,啟動速度更快。
Android12 之前的裝置:
// 初次啟動
D/SampleActivity: ON_CREATE
D/SampleActivity: ON_START
D/SampleActivity: ON_RESUME
// 傳回桌面
D/SampleActivity: ON_PAUSE
D/SampleActivity: ON_STOP
D/SampleActivity: ON_DESTROY
// 再次啟動
D/SampleActivity: ON_CREATE
D/SampleActivity: ON_START
D/SampleActivity: ON_RESUME

Android12 之後的裝置:
// 初次啟動
D/SampleActivity: ON_CREATE
D/SampleActivity: ON_START
D/SampleActivity: ON_RESUME
// 傳回桌面
D/SampleActivity: ON_PAUSE
D/SampleActivity: ON_STOP
// 再次啟動
D/SampleActivity: ON_START
D/SampleActivity: ON_RESUME

           

參考

  • Android Context詳解(全解析)
  • 溫故知新—Activity的五種啟動模式

我是今陽,如果想要進階和了解更多的幹貨,歡迎關注微信公衆号 “今陽說” 接收我的最新文章