簡體   English   中英

使用反射訪問DialogFragment中的某些字段

[英]Using reflection to access some fields in DialogFragment

我有一個類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進程中運行的,或者因為我做錯了什么?

我可以忍受第一種情況。 如果是第二種情況,我真的想將它們設置為預期值。

PS我真的對講道為什么使用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>這就是為什么我嘗試將它DialogFragment.class成一個。

結果是:

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

此時應更換super.getClass()DialogFragment.class

getDeclaredField()一起使用

這是因為super.getClass()將返回實例的類,而不是您想要的超類。 並且getField()無法獲取private字段。

編輯:

dismissed.set(thiz, false); 應該用dismissed.set(this, false);替換dismissed.set(this, false); 因為您需要提供對象而不是類才能設置字段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM