简体   繁体   English

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

[英]Using reflection to access some fields in DialogFragment

I have a class BaseDialog extends DialogFragment 我有一个类BaseDialog extends DialogFragment

After a while, i found out that the default DialogFragment.show() can cause some problems - if the activity is being shutdown, or destroyed etc... 过了一会儿,我发现默认的DialogFragment.show()可能会导致一些问题 - 如果活动正在关闭或被破坏等...

After looking at the decompiled(?) source of DialogFragment, i stumble on this piece of code: 在查看DialogFragment的反编译(?)源代码后,我偶然发现了这段代码:

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

And i figure to try a small 'hack' of my own to get rid of bugs relating to showing/dismissing dialogs after Activity has passed it's onSaveInstanceState() call. 我打算尝试一下我自己的一个小“黑客”来摆脱在Activity通过onSaveInstanceState()调用后显示/解除对话框的错误。

I came up with this: 我想出了这个:

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();
}

Which works quite alright. 哪个工作得很好。

The problem I have now is this, i can't seem to access some of DialogFragment's fields to set them to their proper expected values like the original source does. 我现在遇到的问题是,我似乎无法访问某些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)

And I'm wondering why can't I access those fields.... they're not static, so getField() should work. 而且我想知道为什么我不能访问这些字段......它们不是静态的,所以getField()应该可以工作。 I've tried getDeclaredField() as well - no dice. 我也试过了getDeclaredField() - 没有骰子。

So I'm starting to wonder - is it even feasible to try and access those fields using reflection? 所以我开始怀疑 - 尝试使用反射访问这些字段是否可行? I know that some Android code (SDK code) is there "just for show" because we can't touch it - it runs in the AndroidRuntime process which is why reflection can't do anything there. 我知道有些Android代码(SDK代码)是“仅用于展示”,因为我们无法触摸它 - 它在AndroidRuntime进程中运行,这就是为什么反射在那里无法做任何事情。

Which is why i'm asking the question: am I not finding these fields because they're running in the AndroidRuntime process OR because i'm doing something wrong? 这就是我提出这个问题的原因:我找不到这些字段,因为它们是在AndroidRuntime进程中运行的,或者因为我做错了什么?

I can live with the first case. 我可以忍受第一种情况。 I would really like to set them to their expected values if it's the second case. 如果是第二种情况,我真的想将它们设置为预期值。

PS I'm really not interested in preachings about why using commitAllowingStateLoss is wrong and/or bad. PS我真的对讲道为什么使用commitAllowingStateLoss错误和/或不好感兴趣。 The focus of the question isn't that. 问题的焦点不是那个。 The focus of the question is why can't I find these fields? 问题的焦点是为什么我找不到这些字段?

Thanks for help in advance :) 在此先感谢您的帮助:)

EDIT: Here's the logcat to show that using getDeclaredField doesn't work either. 编辑:这是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: EDIT2:

Thanks to @pskink 's and @derek-fung 's inputs, i changed the code to this: 感谢@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();
}

Using DialogFragment.class was causing java.lang.IllegalArgumentException: Expected receiver of type android.app.DialogFragment, but got java.lang.Class<android.app.DialogFragment> which is why I tried casting it into one. 使用DialogFragment.class导致java.lang.IllegalArgumentException: Expected receiver of type android.app.DialogFragment, but got java.lang.Class<android.app.DialogFragment>这就是为什么我尝试将它DialogFragment.class成一个。

And the result was: 结果是:

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)

The thing that did the trick is this: 做到这一点的是:

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();
}

You should replace super.getClass() with DialogFragment.class 此时应更换super.getClass()DialogFragment.class

together with getDeclaredField() getDeclaredField()一起使用

It is because super.getClass() would return the class of your instance, instead of your superclass which you want. 这是因为super.getClass()将返回实例的类,而不是您想要的超类。 And getField() cannot get private field. 并且getField()无法获取private字段。

Edit: 编辑:

dismissed.set(thiz, false); should be replaced with dismissed.set(this, false); 应该用dismissed.set(this, false);替换dismissed.set(this, false); because you need to provide the object not the class in order to set a field. 因为您需要提供对象而不是类才能设置字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM