Android - onAttach(Context)在API 23中未被调用

72

我已将SDK更新至最新版本(API 23),并且针对Fragment的onAttach(Activity)方法已被弃用。所以现在我使用onAttach(Context)来代替该方法,但是在生命周期中此方法不会被调用。Activity是v7的AppCompatActivity实例,而Fragment是android.app.Fragment类的实例。

有任何想法如何让onAttach在API 23中正常工作吗?

解决方案

我找到了一些答案,可以帮助您理解和解决这个问题:

  • 如我之前提到的,我正在使用来自support v7(AppCompatActivity)的activity和android.app的fragment。

  • 为了显示fragment,我使用activity上的getFragmentManager方法获取fragment manager,因此fragment manager是来自于android.app的FragmentManager实例 - 这就是问题所在。

  • 如果在具有API 23的设备上运行应用程序(我在AS模拟器上运行),则会看到将调用onAttach(Context)方法。在通过FragmentManager显示fragment的过程中,在特定时刻将调用onAttach(...)方法。使用getFragmentManager()将获得可用于运行在该设备上的Android版本(例如API 22、23)的fragment manager实例。如果应用程序在API < 23的设备上运行,则无法使用onAttach(Context)方法,因为fragment manager不知道在API 23中有一个onAttach(Context)方法,因此对于API < 23不会被调用 - 这种问题的解决方案是使用来自support库的FragmentManager。

  • 解决方案:

    1. 使用getSupportFragmentManager()将强制您使用来自support库的Fragment。因此,第一种解决方案是使用来自support库的fragment替换所有fragment,并使用getSupportFragmentManager()。

    2. 我已经实现了的解决方案是处理2种可能性(1. 应用程序正在运行在具有API < 23的设备上;2. 应用程序正在运行在具有API >= 23的设备上)。

    简而言之,在我的实现中,我为项目中的所有fragment提供了一个基类,并在其中添加了这段代码:

    /*
     * onAttach(Context) is not called on pre API 23 versions of Android and onAttach(Activity) is deprecated
     * Use onAttachToContext instead
     */
    @TargetApi(23)
    @Override
    public final void onAttach(Context context) {
        super.onAttach(context);
        onAttachToContext(context);
    }
    
    /*
     * Deprecated on API 23
     * Use onAttachToContext instead
     */
    @SuppressWarnings("deprecation")
    @Override
    public final void onAttach(Activity activity) {
        super.onAttach(activity);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            onAttachToContext(activity);
        }
    }
    
    /*
     * Called when the fragment attaches to the context
     */
    protected void onAttachToContext(Context context) {
    }
    

    现在我只需在需要这个功能的所有片段上覆盖 onAttachToContext(Context) 方法即可。


    1
    你的 onAttach(Context) 方法上有加上 @Override 吗?如果没有,请加上。如果你在编译时遇到了“找不到 onAttach(Context) 方法”的错误,请确保你使用的是 appcompat-v7 的 v23 版本。 - CommonsWare
    1
    是的。我在 onAttach(Context) 上使用了 @Override。此外,v7 版本为 23.0.0。 - David Rauca
    1
    刚刚测试了一下。我的活动也扩展了 AppCompatActivity。我已经重写了 onAttach(Activity)onAttach(Context),并且两者都被调用了。在 Nexus 5 (MPA44G) 上进行了测试,minSdk = 16,targetSdk = 23。 - Mygod
    2
    还没有。我已经尝试过使用v4支持库并且它可以工作。你可以尝试使用v4支持库。 - David Rauca
    2
    根据这个答案,在运行API 23的设备上,onAttach(Context)和onAttach(Activity)都会被调用。因此,您需要进行上述检查,以确保在运行API级别为23的设备上,不会重复执行onAttach的工作。 - Janus Varmarken
    显示剩余9条评论
    7个回答

    28

    我是这样解决问题的:

    @TargetApi(23)
    @Override public void onAttach(Context context) {
        //This method avoid to call super.onAttach(context) if I'm not using api 23 or more
        //if (Build.VERSION.SDK_INT >= 23) {
            super.onAttach(context);
            onAttachToContext(context);
        //}
    }
    
    /*
     * Deprecated on API 23
     * Use onAttachToContext instead
     */
    @SuppressWarnings("deprecation")
    @Override public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (Build.VERSION.SDK_INT < 23) {
            onAttachToContext(activity);
        }
    }
    
    /*
     * This method will be called from one of the two previous method
     */
    protected void onAttachToContext(Context context) {}
    

    3
    关于 onAttach(Context) 方法... 该方法在 API < 23 中不会被调用,因此您不需要在内部检查版本。 - David Rauca
    onDetach()也没有被调用。如果我想设置this.context = null,该怎么做? - Honghe.Wu
    E/AndroidRuntime: 致命异常:主要, java.lang.ClassCastException:, 在com.fragment.fragment_home_new.onAttachToContext, 在com.fragment.fragment_home_new.onAttach, 在android.support.v4.app.FragmentManagerImpl.moveToState, 这里我添加了一些必要的错误信息。请帮我解决这个问题。提前致谢。 - MohanRaj S

    18

    您应该尝试使用Support库版本的Fragment。

    您需要切换到import android.support.v4.app.Fragment;而不是import android.app.Fragment;。您还需要确保您正在使用getSupportFragmentManager()而不是getFragmentManager()


    我认为这是正确的答案。我假设我可以使用内置的Fragment,因为我针对的是SDK版本18,但是Fragment API在各种SDK版本中存在差异。支持库Fragment在各个版本中保持一致,因此它调用onAttach(Activity activity)。 - Ben
    假设您想使用不包含在支持库中的PreferenceFragment,并且希望将其作为导航抽屉中的一个片段添加,那么切换到支持片段的解决方案将无法正常工作。 - Jayshil Dave

    6

    5
    我遇到了同样的问题。 我的minSdkVersion为19,targetSdkVersion为23,并且我正在使用AppCompatActivity中的fragmentManager。 onAttach(Activity activity)被标记为过时的方法,但是当我用onAttach(Context context)替换它时,我遇到了应用程序崩溃的问题。
    因此,我现在同时保留了两个版本,在API22上运行我的应用程序时,即使它被标记为过时的方法,onAttach(Activity activity)仍会被触发。
    我还没有在Android Marshmallow设备上测试过它,但是我的理解是它将运行onAttach(Context context)版本,忽略其他版本。
    更新以考虑下面评论中@skaar的观察: 我现在可以在API23上进行测试,并确认在平台23上运行会调用带有Activity和Context的两种方法。感谢您的指出!

    有人能确认一下吗? - Martin Mlostek
    这是我看到的行为,除了最后一句话。假设您正在使用android.app.Fragment而不是支持库版本。如果您的编译/目标版本为23,则Fragment.onAttach(Activity)方法已过时。但是,如果您的目标平台<23,则仍然需要使用onAttach(Activity),因为它们将没有onAttach(Context)方法。此外,如果您在平台版本23上运行,则两种方法都将被调用。 - vman

    4

    在我阅读了您的回答@David Rauca之后,我认为第二个方案可以解决我的问题。然而,当我查看源代码时,我发现在Android M版本上onAttach(Context context)实际上没有做任何事情,只是调用了旧方法。

    作为解决方案:

    @SuppressWarnings("deprecation")
    

    将其添加到旧方法中是最佳解决方案。


    3
    我遇到了同样的问题。为了解决它,我做了以下几个步骤:
    1. 将onAttach回调函数的参数类型从Activity改为Context。由于未知原因,这个修改导致在片段生命周期中不再调用onAttach(Context)方法。因此,我的应用程序崩溃了,因为没有执行onAttach方法中的代码。

    2. 将onAttach方法中的代码移到onCreate方法中,因为它仍然会被执行。

    通过这些修改,应用程序恢复正常运行。不需要额外的import语句。

    1
    @Override
    public void onAttach(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            super.onAttach(context);
            onAttachToContext(context);
        } else {
            Activity activity = getActivity();
            super.onAttach(activity);
            onAttachToContext(activity);
        }
    }
    

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接