activity getIntent() 方法获取不到值的问题分析

之前有同事小伙伴问我说,他在activity里边调用getIntent()方法没有获取到应该传递过来的值。看了下他这个activity是single task属性的,怀疑是由于activity的single task属性引起的。本文主要是针对这个getIntent()方法分析下源码定位原因:

    /** Return the intent that started this activity. */
    public Intent getIntent() {
        return mIntent;
    }

getIntent()方法实际上返回的是mItent这个Activity的成员变量,然后我们看下赋值的地方在哪。有两个地方给mIntent赋值,一个是setIntent()方法:

     /**
     * Change the intent returned by {@link #getIntent}.  This holds a
     * reference to the given intent; it does not copy it.  Often used in
     * conjunction with {@link #onNewIntent}.
     *
     * @param newIntent The new Intent object to return from getIntent
     *
     * @see #getIntent
     * @see #onNewIntent
     */
    public void setIntent(Intent newIntent) {
        mIntent = newIntent;
    }

另一个地方是在Activity的attach方法中赋值的:

       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) {
        attachBaseContext(context);

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

        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        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;
        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());
            }
        }

        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());
        }
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
    }

这个attach方法是一个很重要的方法,在第一次启动这个activity时完成很多重要变量的初始化赋值操作。根据之前分析Activity的启动流程步骤(参考 https://www.jianshu.com/p/3cf90c633bb1),AMS执行启动activity的最后时通过binder通信,通知ActivityThread中的ApplicationThread对象执行scheduleLaunchActivity函数:

        // we use token to identify this activity without having to send the
        // activity itself back to the activity manager. (matters more with ipc)
        @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 pendingResults, List 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);
        }

参数中这个intent是AMS传过来的,intent被赋给了新建的ActivityClientRecord对象,最后通过sendMessage调用到了ActivityThread的handleLauncherActivity方法,我们看看这个方法:

        private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        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) {
                // 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.
                performPauseActivityIfNeeded(r, reason);

                // 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;
                }
            }
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            try {
                ActivityManager.getService()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

聪明的同学能看出来我们Activity的onCreate、onResume等方法都是从这个函数发起的。我们看下performLaunchActivity这个函数,这个函数稍微有点长,先看前面的这块代码:

        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.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);
            }
        }

Instrumentation.newActivity这个方法实际上只构造了Activity对象,但是并未给它初始化系统信息:

    public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

然后看后面剩下的这块代码:

        try {
            // 创建Application,实质上Application已经创建,只是获得已有的Application 实例
            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) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
                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;
                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);
            }
        }

我们看到了这个又调用了makeApplication方法,之前在应用进程启动流程文章(https://blog.csdn.net/ws6013480777777/article/details/85004821)里写过,应用进程创建在activity启动前早已完成,这个地方实际上Application已经创建好了,只有特殊情况mApplication为空时才去重新创建:

        public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }
        ...
        }

然后往下看,我们看到了新建的这个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);

这个地方就回到了我们最初看到的activity调用attach的地方了。我们看到在调用attach后,activity的onCreate才被调用:

if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }

callActivityOnCreate方法是这样的:

    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }

最终我们熟悉的activity的onCreate方法被回调了:

    final void performCreate(Bundle icicle) {
        restoreHasCurrentPermissionRequest(icicle);
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

也就是说我们在Activity中getIntent方法获取的值是Activity创建执行attach时传递过来的,它在onCreate方法之前执行。

但是同事说getIntent获取的值不对是为什么呢?因为使用的是singleTask属性,之前如果已经有activity实例在栈中时,再次启动它不会重新创建实例,而是复用这个activity,调用onNewIntent方法。

onNewIntent方法我们还是从ActivityThread的ApplicationThread收到AMS的binder通信,调用scheduleNewIntent函数开始:

       public final void scheduleNewIntent(
                List intents, IBinder token, boolean andPause) {
            NewIntentData data = new NewIntentData();
            data.intents = intents;
            data.token = token;
            data.andPause = andPause;

            sendMessage(H.NEW_INTENT, data);
        }

通过handler sendMessage调用到ActivityThread的handleNewIntent方法:

    private void handleNewIntent(NewIntentData data) {
        performNewIntents(data.token, data.intents, data.andPause);
    }

然后去调用了performNewIntents方法:

void performNewIntents(IBinder token, List intents, boolean andPause) {
        final ActivityClientRecord r = mActivities.get(token);
        if (r == null) {
            return;
        }

        final boolean resumed = !r.paused;
        if (resumed) {
            r.activity.mTemporaryPause = true;
            mInstrumentation.callActivityOnPause(r.activity);
        }
        deliverNewIntents(r, intents);
        if (resumed) {
            r.activity.performResume();
            r.activity.mTemporaryPause = false;
        }

        if (r.paused && andPause) {
            // In this case the activity was in the paused state when we delivered the intent,
            // to guarantee onResume gets called after onNewIntent we temporarily resume the
            // activity and pause again as the caller wanted.
            performResumeActivity(token, false, "performNewIntents");
            performPauseActivityIfNeeded(r, "performNewIntents");
        }
    }

这个方法先来查找对应的ActivityClientRecord对象,没有的话既然找不到也没必要执行后续,直接return掉了。然后看这个ActivityClientRecord对象是不是resume状态,是的话先调用callActivityOnPause让它pause掉,然后调用deliverNewIntents函数执行真正的newIntent事件传递,后面我们看到onNewIntent完后还会调用performResume让activity的onResume被调用。下面我们看看这个deliverNewIntents函数:

private void deliverNewIntents(ActivityClientRecord r, List intents) {
        final int N = intents.size();
        for (int i=0; i

变量了传递过来的这个ReferrerIntent类型的list,分别取执行callActivityOnNewIntent方法,此方法最终调用到activity的onNewIntent方法中:

    /**
     * Perform calling of an activity's {@link Activity#onNewIntent}
     * method.  The default implementation simply calls through to that method.
     * 
     * @param activity The activity receiving a new Intent.
     * @param intent The new intent being received.
     */
    public void callActivityOnNewIntent(Activity activity, Intent intent) {
        activity.onNewIntent(intent);
    }

    /**
     * @hide
     */
    public void callActivityOnNewIntent(Activity activity, ReferrerIntent intent) {
        final String oldReferrer = activity.mReferrer;
        try {
            if (intent != null) {
                activity.mReferrer = intent.mReferrer;
            }
            callActivityOnNewIntent(activity, intent != null ? new Intent(intent) : null);
        } finally {
            activity.mReferrer = oldReferrer;
        }
    }

activity的onNewIntent方法是这样的:

    protected void onNewIntent(Intent intent) {
    }

是个空的函数,什么也没操作,也就是说如果开发者不复写处理,这个intent不会赋给activity的mIntent的。

如果想将intent赋给activity成员遍历以便随时通过getIntent()取用的话,可以在onNewIntent方法中手动写一下:

setIntent(intent),这样之后再通过getIntent() 获取的就是onNewIntent传递的最新的值了。

你可能感兴趣的:(framework,经验总结,android)