mercredi 16 septembre 2015

Using reflection to access some fields in DialogFragment

I have a class 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...

After looking at the decompiled(?) source of DialogFragment, i stumble on this piece of code:

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.

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.

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. I've tried getDeclaredField() as well - no dice.

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.

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?

I can live with the first case. I would really like to set them to their expected values if it's the second case.

P.S. I'm really not interested in preachings about why using commitAllowingStateLoss is wrong and/or bad. 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 :)





Aucun commentaire:

Enregistrer un commentaire