使用反射访问DialogFragment中的某些字段

时间:2015-09-16 13:57:41

标签: java android android-fragments reflection

我有一个班级BaseDialog extends DialogFragment

过了一会儿,我发现默认的DialogFragment.show()会导致一些问题 - 如果活动正在关闭或被破坏等......

在查看DialogFragment的反编译(?)源代码后,我偶然发现了这段代码:

public void show(FragmentManager manager, String tag) {
    mDismissed = false;
    mShownByMe = true;
    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commit();
}

我打算尝试一下我自己的小“黑客”,以便在Activity通过onSaveInstanceState()电话后摆脱与显示/解除对话相关的错误。

我想出了这个:

public void showAllowingStateLoss(FragmentManager manager, String tag) {
    try {
        Class thiz = super.getClass();
        Field dismissed = thiz.getField("mDismissed");
        dismissed.setAccessible(true);
        dismissed.set(thiz, false);
    } catch(NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } //mDismissed = false;

    try {
        Class thiz = super.getClass();
        Field shown = thiz.getField("mShownByMe");
        shown.setAccessible(true);
        shown.set(thiz, true);
    } catch(IllegalAccessException e) {
        e.printStackTrace();
    } catch(NoSuchFieldException e) {
        e.printStackTrace();
    } //mShownByMe = true;

    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commitAllowingStateLoss();
}

哪个工作得很好。

我现在遇到的问题是,我似乎无法访问某些DialogFragment的字段,将它们设置为正确的预期值,就像原始源一样。

W/System.err( 2510): java.lang.NoSuchFieldException: mDismissed
W/System.err( 2510):    at java.lang.Class.getField(Class.java:1048)
W/System.err( 2510):    at com.dpd.navigator.ui.dialogs.BaseDialog.showAllowingStateLoss(BaseDialog.java:70)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin.openConfirmationDialog(ActivityLogin.java:250)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin.access$600(ActivityLogin.java:45)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin$SOAPListener_getUserLogin.onSOAPSuccess(ActivityLogin.java:306)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin$SOAPListener_getUserLogin.onSOAPSuccess(ActivityLogin.java:262)
W/System.err( 2510):    at com.dpd.navigator.backend.soap.SOAPHelper$20.onPostExecute(SOAPHelper.java:1290)
W/System.err( 2510):    at android.os.AsyncTask.finish(AsyncTask.java:632)
W/System.err( 2510):    at android.os.AsyncTask.access$600(AsyncTask.java:177)
W/System.err( 2510):    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
W/System.err( 2510):    at android.os.Handler.dispatchMessage(Handler.java:102)
W/System.err( 2510):    at android.os.Looper.loop(Looper.java:155)
W/System.err( 2510):    at android.app.ActivityThread.main(ActivityThread.java:5696)
W/System.err( 2510):    at java.lang.reflect.Method.invoke(Native Method)
W/System.err( 2510):    at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err( 2510):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
W/System.err( 2510):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
W/System.err( 2510): java.lang.NoSuchFieldException: mShownByMe
W/System.err( 2510):    at java.lang.Class.getField(Class.java:1048)
W/System.err( 2510):    at com.dpd.navigator.ui.dialogs.BaseDialog.showAllowingStateLoss(BaseDialog.java:81)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin.openConfirmationDialog(ActivityLogin.java:250)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin.access$600(ActivityLogin.java:45)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin$SOAPListener_getUserLogin.onSOAPSuccess(ActivityLogin.java:306)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin$SOAPListener_getUserLogin.onSOAPSuccess(ActivityLogin.java:262)
W/System.err( 2510):    at com.dpd.navigator.backend.soap.SOAPHelper$20.onPostExecute(SOAPHelper.java:1290)
W/System.err( 2510):    at android.os.AsyncTask.finish(AsyncTask.java:632)
W/System.err( 2510):    at android.os.AsyncTask.access$600(AsyncTask.java:177)
W/System.err( 2510):    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
W/System.err( 2510):    at android.os.Handler.dispatchMessage(Handler.java:102)
W/System.err( 2510):    at android.os.Looper.loop(Looper.java:155)
W/System.err( 2510):    at android.app.ActivityThread.main(ActivityThread.java:5696)
W/System.err( 2510):    at java.lang.reflect.Method.invoke(Native Method)
W/System.err( 2510):    at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err( 2510):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
W/System.err( 2510):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
W/System.err( 2510): java.lang.NoSuchFieldException: mViewDestroyed
W/System.err( 2510):    at java.lang.Class.getField(Class.java:1048)
W/System.err( 2510):    at com.dpd.navigator.ui.dialogs.BaseDialog.showAllowingStateLoss(BaseDialog.java:95)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin.openConfirmationDialog(ActivityLogin.java:250)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin.access$600(ActivityLogin.java:45)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin$SOAPListener_getUserLogin.onSOAPSuccess(ActivityLogin.java:306)
W/System.err( 2510):    at com.dpd.navigator.ui.activities.ActivityLogin$SOAPListener_getUserLogin.onSOAPSuccess(ActivityLogin.java:262)
W/System.err( 2510):    at com.dpd.navigator.backend.soap.SOAPHelper$20.onPostExecute(SOAPHelper.java:1290)
W/System.err( 2510):    at android.os.AsyncTask.finish(AsyncTask.java:632)
W/System.err( 2510):    at android.os.AsyncTask.access$600(AsyncTask.java:177)
W/System.err( 2510):    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
W/System.err( 2510):    at android.os.Handler.dispatchMessage(Handler.java:102)
W/System.err( 2510):    at android.os.Looper.loop(Looper.java:155)
W/System.err( 2510):    at android.app.ActivityThread.main(ActivityThread.java:5696)
W/System.err( 2510):    at java.lang.reflect.Method.invoke(Native Method)
W/System.err( 2510):    at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err( 2510):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
W/System.err( 2510):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)

我想知道为什么我不能访问这些字段......它们不是静态的,所以getField()应该可以工作。我也试过getDeclaredField() - 没有骰子。

所以我开始怀疑 - 尝试使用反射访问这些字段是否可行?我知道一些Android代码(SDK代码)是“仅用于展示”,因为我们无法触摸它 - 它在AndroidRuntime进程中运行,这就是为什么反射在那里无法做任何事情。

这就是我提出这个问题的原因:我找不到这些字段,因为它们是在AndroidRuntime进程中运行还是因为我做错了什么?

我可以忍受第一种情况。如果是第二种情况,我真的想将它们设置为预期值。

P.S。关于为什么使用commitAllowingStateLoss是错误和/或不好的,我真的不感兴趣。问题的焦点不是那个。 问题的焦点是为什么我找不到这些字段?

提前感谢您的帮助:)

编辑: 这是logcat,表明使用getDeclaredField也不起作用。

W/System.err(  330): java.lang.NoSuchFieldException: mDismissed
W/System.err(  330):    at java.lang.Class.getDeclaredField(Class.java:886)
W/System.err(  330):    at com.dpd.navigator.ui.dialogs.BaseDialog.showAllowingStateLoss(BaseDialog.java:70)
W/System.err(  330):    at com.dpd.navigator.ui.activities.ActivityMenu.openProfile(ActivityMenu.java:275)
W/System.err(  330):    at com.dpd.navigator.ui.activities.ActivityMenu.access$000(ActivityMenu.java:31)
W/System.err(  330):    at com.dpd.navigator.ui.activities.ActivityMenu$1.onClick(ActivityMenu.java:74)
W/System.err(  330):    at android.view.View.performClick(View.java:4785)
W/System.err(  330):    at android.view.View$PerformClick.run(View.java:19858)
W/System.err(  330):    at android.os.Handler.handleCallback(Handler.java:739)
W/System.err(  330):    at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err(  330):    at android.os.Looper.loop(Looper.java:155)
W/System.err(  330):    at android.app.ActivityThread.main(ActivityThread.java:5696)
W/System.err(  330):    at java.lang.reflect.Method.invoke(Native Method)
W/System.err(  330):    at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err(  330):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
W/System.err(  330):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)

EDIT2:

感谢@pskink和@derek-fung的输入,我将代码更改为:

public void showAllowingStateLoss(FragmentManager manager, String tag) {
    try {
//      Class<? extends DialogFragment> thiz = DialogFragment.class;
        Class<? extends DialogFragment> thiz = ((DialogFragment)this).getClass();
        Field dismissed = thiz.getDeclaredField("mDismissed"); <-- line 71
        dismissed.setAccessible(true);
        dismissed.set(this, false);
    } catch(NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } //mDismissed = false;

    try {
        Class<? extends DialogFragment> thiz = ((DialogFragment)this).getClass();
//      Field shown = thiz.getField("mShownByMe");
        Field shown = thiz.getDeclaredField("mShownByMe"); <-- line 83
        shown.setAccessible(true);
        shown.set(this, true);
    } catch(IllegalAccessException e) {
        e.printStackTrace();
    } catch(NoSuchFieldException e) {
        e.printStackTrace();
    } //mShownByMe = true;

    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commitAllowingStateLoss();
}

使用DialogFragment.class导致java.lang.IllegalArgumentException: Expected receiver of type android.app.DialogFragment, but got java.lang.Class<android.app.DialogFragment>这就是为什么我尝试将其合并为一个。

结果是:

W/System.err( 3227): java.lang.NoSuchFieldException: mDismissed
W/System.err( 3227):    at java.lang.Class.getDeclaredField(Class.java:886)
W/System.err( 3227):    at com.dpd.navigator.ui.dialogs.BaseDialog.showAllowingStateLoss(BaseDialog.java:71)
W/System.err( 3227):    at com.dpd.navigator.ActivityDPDSplash.openConfirmationDialog(ActivityDPDSplash.java:636)
W/System.err( 3227):    at com.dpd.navigator.ActivityDPDSplash.startApplication(ActivityDPDSplash.java:866)
W/System.err( 3227):    at com.dpd.navigator.ActivityDPDSplash.access$600(ActivityDPDSplash.java:70)
W/System.err( 3227):    at com.dpd.navigator.ActivityDPDSplash$7.run(ActivityDPDSplash.java:765)
W/System.err( 3227):    at android.os.Handler.handleCallback(Handler.java:739)
W/System.err( 3227):    at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err( 3227):    at android.os.Looper.loop(Looper.java:155)
W/System.err( 3227):    at android.app.ActivityThread.main(ActivityThread.java:5696)
W/System.err( 3227):    at java.lang.reflect.Method.invoke(Native Method)
W/System.err( 3227):    at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err( 3227):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
W/System.err( 3227):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
W/System.err( 3227): java.lang.NoSuchFieldException: mShownByMe
W/System.err( 3227):    at java.lang.Class.getDeclaredField(Class.java:886)
W/System.err( 3227):    at com.dpd.navigator.ui.dialogs.BaseDialog.showAllowingStateLoss(BaseDialog.java:83)
W/System.err( 3227):    at com.dpd.navigator.ActivityDPDSplash.openConfirmationDialog(ActivityDPDSplash.java:636)
W/System.err( 3227):    at com.dpd.navigator.ActivityDPDSplash.startApplication(ActivityDPDSplash.java:866)
W/System.err( 3227):    at com.dpd.navigator.ActivityDPDSplash.access$600(ActivityDPDSplash.java:70)
W/System.err( 3227):    at com.dpd.navigator.ActivityDPDSplash$7.run(ActivityDPDSplash.java:765)
W/System.err( 3227):    at android.os.Handler.handleCallback(Handler.java:739)
W/System.err( 3227):    at android.os.Handler.dispatchMessage(Handler.java:95)
W/System.err( 3227):    at android.os.Looper.loop(Looper.java:155)
W/System.err( 3227):    at android.app.ActivityThread.main(ActivityThread.java:5696)
W/System.err( 3227):    at java.lang.reflect.Method.invoke(Native Method)
W/System.err( 3227):    at java.lang.reflect.Method.invoke(Method.java:372)
W/System.err( 3227):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
W/System.err( 3227):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)

实现这一目标的是:

public void showAllowingStateLoss(FragmentManager manager, String tag) {
    try {
        Class<? extends DialogFragment> thiz = DialogFragment.class;
        Field dismissed = thiz.getDeclaredField("mDismissed");
        dismissed.setAccessible(true);
        dismissed.set(this, false);
    } catch(NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } //mDismissed = false;

    try {
        Class<? extends DialogFragment> thiz = DialogFragment.class;
        Field shown = thiz.getDeclaredField("mShownByMe");
        shown.setAccessible(true);
        shown.set(this, true);
    } catch(IllegalAccessException e) {
        e.printStackTrace();
    } catch(NoSuchFieldException e) {
        e.printStackTrace();
    } //mShownByMe = true;

    FragmentTransaction ft = manager.beginTransaction();
    ft.add(this, tag);
    ft.commitAllowingStateLoss();
}

1 个答案:

答案 0 :(得分:2)

您应该将super.getClass()替换为DialogFragment.class

getDeclaredField()

一起

这是因为super.getClass()将返回您的实例的类,而不是您想要的超类。 getField()无法获得private字段。

修改

dismissed.set(thiz, false);应替换为dismissed.set(this, false);,因为您需要提供对象而不是类才能设置字段。