samedi 12 octobre 2019

What is causing this exception: java.lang.NoSuchFieldException?

I was trying to use some reflection to workaround and make my text have an outline, but I got the exception below and I could't figure out what caused it.

I already tried to use superclass of AppCompatTextView to find the field, but it throwed same exception.

Exception

E/OutlineTextView: OutlineTextView: throwed exception!
    java.lang.NoSuchFieldException: No field mCurTextColor in class Landroidx/appcompat/widget/AppCompatTextView; (declaration of 'androidx.appcompat.widget.AppCompatTextView' appears in /data/app/com.notyetmidnight.investigativedetective-2hmFJkII1JGtjt1HtBTEcg==/base.apk)
        at java.lang.Class.getDeclaredField(Native Method)
        at com.notyetmidnight.investigativedetective.CustomComponents.OutlineTextView.<init>(OutlineTextView.java:40)
        at com.notyetmidnight.investigativedetective.CustomComponents.OutlineTextView.<init>(OutlineTextView.java:31)
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at android.view.LayoutInflater.createView(LayoutInflater.java:854)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1006)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:1126)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:682)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:534)
        at com.notyetmidnight.investigativedetective.Interface.CharacterSelection.CharacterSelectionFragment.onCreateView(CharacterSelectionFragment.java:114)
        at com.notyetmidnight.investigativedetective.Interface.CharacterSelection.CharacterSelectionFragment.onCreateView(CharacterSelectionFragment.java:43)
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2600)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:881)
        at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238)
        at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303)
        at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:439)
        at androidx.fragment.app.FragmentManagerImpl.executeOps(FragmentManagerImpl.java:2079)
        at androidx.fragment.app.FragmentManagerImpl.executeOpsTogether(FragmentManagerImpl.java:1869)
        at androidx.fragment.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManagerImpl.java:1824)
        at androidx.fragment.app.FragmentManagerImpl.execPendingActions(FragmentManagerImpl.java:1727)
        at androidx.fragment.app.FragmentManagerImpl.executePendingTransactions(FragmentManagerImpl.java:183)
        at com.notyetmidnight.investigativedetective.MainActivity.swapFragments(MainActivity.java:346)
        at com.notyetmidnight.investigativedetective.MainActivity.onEvent(MainActivity.java:365)
        at java.lang.reflect.Method.invoke(Native Method)
        at org.greenrobot.eventbus.EventBus.invokeSubscriber(EventBus.java:507)
        at org.greenrobot.eventbus.EventBus.postToSubscription(EventBus.java:434)
        at org.greenrobot.eventbus.EventBus.postSingleEventForEventType(EventBus.java:411)
        at org.greenrobot.eventbus.EventBus.postSingleEvent(EventBus.java:384)
        at org.greenrobot.eventbus.EventBus.post(EventBus.java:265)
        at com.notyetmidnight.investigativedetective.Interface.Menu.MenuFragment.lambda$setOnClickListeners$0(MenuFragment.java:71)
        at com.notyetmidnight.investigativedetective.Interface.Menu.-$$Lambda$MenuFragment$gQ5F3e04Bn82QqyI2yepYZlvbR8.onClick(Unknown Source:0)
        at android.view.View.performClick(View.java:7125)
        at android.view.View.performClickInternal(View.java:7102)
        at android.view.View.access$3500(View.java:801)
        at android.view.View$PerformClick.run(View.java:27336)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

OutlineTextView

package com.notyetmidnight.investigativedetective.CustomComponents;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;

import androidx.appcompat.widget.AppCompatTextView;
import androidx.core.util.Preconditions;

import com.notyetmidnight.investigativedetective.R;

import java.lang.reflect.Field;

public class OutlineTextView extends AppCompatTextView {
    private static final String TAG = "OutlineTextView";
    private Field colorField;
    private int textColor;
    private int outlineColor;

    public OutlineTextView(Context context) {
        this(context, null);
    }

    public OutlineTextView(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.textViewStyle);
    }

    public OutlineTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        try {
//            Class superClass = Preconditions.checkNotNull(AppCompatTextView.class.getSuperclass());
//            colorField = superClass.getDeclaredField("mCurTextColor");
            colorField = OutlineTextView.class.getSuperclass().getDeclaredField("mCurTextColor");
            colorField.setAccessible(true);

            // If the reflection fails (which really shouldn't happen), we
            // won't need the rest of this stuff, so we keep it in the try-catch

            textColor = getTextColors().getDefaultColor();

            // These can be changed to hard-coded default
            // values if you don't need to use XML attributes

            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.OutlineTextView);
            outlineColor = a.getColor(R.styleable.OutlineTextView_outlineColor, Color.TRANSPARENT);
            setOutlineStrokeWidth(a.getDimensionPixelSize(R.styleable.OutlineTextView_outlineWidth, 0));
            a.recycle();
        }
        catch (NullPointerException | NoSuchFieldException e) {
            Log.e(TAG, "OutlineTextView: throwed exception!", e);
            colorField = null;
        }
    }

    @Override
    public void setTextColor(int color) {
        // We want to track this ourselves
        // The super call will invalidate()

        textColor = color;
        super.setTextColor(color);
    }

    public void setOutlineColor(int color) {
        outlineColor = color;
        invalidate();
    }

    public void setOutlineWidth(float width) {
        setOutlineStrokeWidth(width);
        invalidate();
    }

    private void setOutlineStrokeWidth(float width) {
        getPaint().setStrokeWidth(2 * width + 1);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // If we couldn't get the Field, then we
        // need to skip this, and just draw as usual

        if (colorField != null) {
            // Outline
            setColorField(outlineColor);
            getPaint().setStyle(Paint.Style.STROKE);
            super.onDraw(canvas);

            // Reset for text
            setColorField(textColor);
            getPaint().setStyle(Paint.Style.FILL);
        } else {
            Log.e(TAG, "onDraw: could not get color field. NullPointerException!");
        }

        super.onDraw(canvas);
    }

    private void setColorField(int color) {
        // We did the null check in onDraw()
        try {
            colorField.setInt(this, color);
        }
        catch (IllegalAccessException | IllegalArgumentException e) {
            Log.e(TAG, "OutlineTextView: throwed exception!", e);
        }
    }

    // Optional saved state stuff

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.textColor = textColor;
        ss.outlineColor = outlineColor;
        ss.outlineWidth = getPaint().getStrokeWidth();
        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        textColor = ss.textColor;
        outlineColor = ss.outlineColor;
        getPaint().setStrokeWidth(ss.outlineWidth);
    }

    private static class SavedState extends BaseSavedState {
        int textColor;
        int outlineColor;
        float outlineWidth;

        SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            textColor = in.readInt();
            outlineColor = in.readInt();
            outlineWidth = in.readFloat();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(textColor);
            out.writeInt(outlineColor);
            out.writeFloat(outlineWidth);
        }

        public static final Parcelable.Creator<SavedState>
                CREATOR = new Parcelable.Creator<SavedState>() {

            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
}

Proguard rules

-dontwarn java.lang.ClassValue
-keep class java.lang.ClassValue { *; }
-keepattributes *Annotation*
-keepclassmembers class ** { @org.greenrobot.eventbus.Subscribe <methods>; }
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepattributes Signature
-keepattributes *Annotation*
-keep class androidx.appcompat.widget.AppCompatTextView { *** mCurTextColor; }

What could possibilly cause this exception to be throw? Also, I'm using proguard (but not to debug) so what I need to change to build my app in release mode (aka with proguard)?





Aucun commentaire:

Enregistrer un commentaire