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

24 篇文章 0 订阅
22 篇文章 0 订阅

之前有同事小伙伴问我说,他在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<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);
        }

参数中这个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<ReferrerIntent> 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<ReferrerIntent> 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<ReferrerIntent> intents) {
        final int N = intents.size();
        for (int i=0; i<N; i++) {
            ReferrerIntent intent = intents.get(i);
            intent.setExtrasClassLoader(r.activity.getClassLoader());
            intent.prepareToEnterProcess();
            r.activity.mFragments.noteStateNotSaved();
            mInstrumentation.callActivityOnNewIntent(r.activity, intent);
        }
    }

变量了传递过来的这个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传递的最新的值了。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
如果你在Android的Activity中设置了intent-filter和action,并且通过`getAction()`方法获取不到Action,可能是由于以下原因导致的: 1. 确保在设置intent-filter时,正确地指定了Action。例如,如果你的intent-filter中指定了`android.intent.action.MAIN`作为Action,则应该使用`Intent.ACTION_MAIN`进行匹配。 2. 确保在获取Intent对象后,立即调用`getAction()`方法。如果在其他方法(如`onCreate()`之外的方法)中调用此方法,可能会导致获取不到Action。 3. 如果你在启动Activity时没有设置Action,那么通过`getAction()`方法是无法获取到Action的。你可以尝试通过`hasExtra()`方法来检查Intent中是否包含指定的Action。 以下是一个示例代码,展示了如何设置intent-filter和Action,并获取Intent中的Action: 在AndroidManifest.xml中的Activity节点下添加intent-filter: ```xml <activity android:name=".YourActivity"> <intent-filter> <action android:name="com.example.ACTION_CUSTOM" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> ``` 在Activity获取Intent中的Action: ```java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = getIntent(); String action = intent.getAction(); if (action != null && action.equals("com.example.ACTION_CUSTOM")) { // 获取到自定义Action } else { // 未获取到自定义Action } } ``` 确保你在获取Intent的Action时,使用正确的Action常量或自定义的Action字符串。如果问题仍然存在,请检查你的代码逻辑和配置,确保一切设置正确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值