[英]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.