How to create a callback using reflection in android?

1.7k views Asked by At

I am trying to access a method using reflection and one of the parameter is a callback. The callback type is generic interface of different class type. These classes are @SystemApi can be accessed by reflection. Here is the class I am working with above sayings.

Below is my sample code :

 String sClassName = "android.telephony.euicc.EuiccCardManager";
 Class classToInvestigate = Class.forName(sClassName);
 Class interfaceclass = classToInvestigate.getClasses()[0]; //android.telephony.euicc.EuiccCardManager.ResultCallback

 Method getallprofiles = classToInvestigate.getDeclaredMethod("requestAllProfiles", String.class,Executor.class, interfaceclass);
 getallprofiles.invoke(null,getEid(), AsyncTask.THREAD_POOL_EXECUTOR,null);

In the above invoke signature as a last parameter I need to pass the callback created using reflection and which is equivalent or similar to the below callback sample.

ResultCallback<EuiccProfileInfo[]> callback =
new ResultCallback<EuiccProfileInfo[]>() {
@Override
public void onComplete(int resultCode, EuiccProfileInfo[] result) { }
};
  • The above ResultCallback interface can be found in the same class link I provided above and the same instance in interfaceclass field above.
  • EuiccProfileInfo is the another class need to access from reflection because it is @SystemApi

I have failed/stuck to translate the above callback logic with reflection.Anyone can help me on this?

1

There are 1 answers

5
GotoFinal On

Seems that you need to create a proxy class:

// that class you don't have access too. imagine that it is not here, it is just to show example signature of method we want to run.
interface Callback {
    void run();
}

public static void main(String[] args) throws Exception {
    Class<?> callbackClass = Class.forName("package.Callback");
    Callback callback = Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{callbackClass}, new CallbackInvocationHandler(() -> {
        System.out.println("callback");
    }));
    callbackClass.getMethod("run").invoke(callback); // works!
}

static class CallbackInvocationHandler implements InvocationHandler {
    private final Runnable myCallback;

    CallbackInvocationHandler(Runnable myCallback) {
        this.myCallback = myCallback;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("toString") && (method.getParameterCount() == 0)) {
            // just random implementation
            return proxy.getClass() + "@" + Integer.toHexString(System.identityHashCode(proxy));
        }
        if (method.getName().equals("hashCode") && (method.getParameterCount() == 0)) {
            return System.identityHashCode(proxy);
        }
        if (method.getName().equals("equals") && (method.getParameterCount() == 1) && (method.getParameterTypes()[0] == Object.class)) {
            return proxy == args[0];
        }
        if (method.getName().equals("run") && (method.getParameterCount() == 0)) {
            // do what you want.
            myCallback.run();
            return null;
        }
        throw new IllegalStateException("Method not implemented: " + method);
    }
}

But if you really need to use something like that - there are huge chances you are doing something wrong, can't you just add dependency on that project? But you should not depend on system classes too.