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?
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.
Related
How can I write code below in java version?
I have read similar questions, but they are confusing, they answered that java didn't have delegate feature like c# had, in other hand they answered with their delegate implementation in java, but nothing is similar with my condition.
I really hope it's clear on this question.
I have been getting stuck since a week
class Action
{
public delegate void ActionDelegate();
public static ActionDelegate OnAction;
public void DoAction()
{
Console.WriteLine("Action A");
if (!ReferenceEquals(OnAction, null))
OnAction();
}
}
class TaskA
{
public TaskA()
{
Action.OnAction += DoTaskA;
}
private void DoTaskA()
{
Console.WriteLine("Do Task A");
}
}
class TaskB
{
public TaskB()
{
Action.OnAction += DoTaskB;
}
private void DoTaskB()
{
Console.WriteLine("Do Task B");
}
}
class Program
{
static void Main(string[] args)
{
TaskA taskA = new TaskA();
TaskB task = new TaskB();
Action action = new Action();
action.DoAction();
}
}
Output:
Action A
Do Task A
Do Task B
Press any keys to continue...
something similar in java will be to use an interface
you can get the following results
Action A
Do Task A
Do Task B
with the codes below.
import java.util.ArrayList;
public class HelloWorld{
public static void main(String []args){
TaskA taskA = new TaskA();
TaskB task = new TaskB();
Action action = new Action();
action.doAction();
}
}
interface ActionDelegate {
void doAction();
}
class Action{
static public ArrayList<ActionDelegate> onAction = new ArrayList<>();
public void doAction(){
System.out.println("Action A");
for(ActionDelegate ad: onAction){
ad.doAction();
}
}
}
class TaskA implements ActionDelegate{
TaskA(){
Action.onAction.add(this);
}
public void doAction(){
System.out.println("Do Task A");
}
}
class TaskB implements ActionDelegate{
TaskB(){
Action.onAction.add(this);
}
public void doAction(){
System.out.println("Do Task B");
}
}
With the project safety-mirror on your classpath your get library support for delegates and events.
<dependency>
<groupId>com.github.hervian</groupId>
<artifactId>safety-mirror</artifactId>
<version>3.0.0</version>
</dependency>
Here's a snippet from the project's README:
Cheat sheet of features
Fun and friends: No more functional interfaces:
Fun.With0Params<String> myFunctionField = " hello world "::trim;
Fun.With2Params<Boolean, Object, Object> equals = Objects::equals;
public void foo(Fun.With1ParamAndVoid<String> printer) throws Exception {
printer.invoke("hello world);
}
foo(System.out::println); //This signature match the the Fun defined by method Foo. If it did not, the compiler would emit an error.
It is all type safe: you will get compile time errors if the Method Reference's signature does not match what is defined by the Fun subclass.
Method m1 = Fun.toMethod(String::isEmpty)
Method m2 = Fun.<String>toMethod(Class::forName)); // to get overloaded method you must specify parameters in generics
assertEquals("isEmpty", Fun.getName(String::isEmpty)); //use Fun's static getName method to get the method name. The Method objects returned from toMethod will not return the correct String.
Delegates in Java!
Delegate.With1Param<String, String> greetingsDelegate = new Delegate.With1Param<>();
greetingsDelegate.add(str -> "Hello " + str);
greetingsDelegate.add(str -> "Goodbye " + str);
DelegateInvocationResult<String> invocationResult = greetingsDelegate.invokeAndAggregateExceptions("Sir");
invocationResult.getFunctionInvocationResults().forEach(funInvRes -> System.out.println(funInvRes.getResult()));
//prints: "Hello sir" and "Goodbye Sir"
Events
//Create a private Delegate. Make sure it is private so only *you* can invoke it.
private static Delegate.With0Params<String> trimDelegate = new Delegate.With0Params<>();
//Create a public Event using the delegate you just created.
public static Event.With0Params<String> trimEvent= new Event.With0Params<>(trimDelegate);
Type safe method creation
Method m1 = Fun.toMethod(Thread::isAlive) // Get final method
Method m2 = Fun.toMethod(String::isEmpty); // Get method from final class
Method m3 = Fun.toMethod(BufferedReader::readLine); // Get method that throws checked exception
Method m4 = Fun.<String, Class[]>toMethod(getClass()::getDeclaredMethod); //to get vararg method you must specify parameters in generics
Method m5 = Fun.<String>toMethod(Class::forName); // to get overloaded method you must specify parameters in generics
Method m6 = Fun.toMethod(this::toString); //Works with inherited methods
Disclaimer: I am the author of the project.
I have a library which parse URLs and extract some data. There is one class per URL. To know which class should handle the URL provided by the user, I have the code below.
public class HostExtractorFactory {
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
switch (host) {
case HostExtractorABC.HOST_NAME:
return HostExtractorAbc.getInstance();
case HostExtractorDEF.HOST_NAME:
return HostExtractorDef.getInstance();
case HostExtractorGHI.HOST_NAME:
return HostExtractorGhi.getInstance();
default:
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
}
The problem is users are requesting more URL to be parsed, which means my switch statement is growing. Every time someone comes up with a parser, I have to modify my code to include it.
To end this, I've decided to create a map and expose it to them, so that when their class is written, they can register themselves to the factory, by providing the host name, and the extractor to the factory. Below is the factory with this idea implemented.
public class HostExtractorFactory {
private static final Map<String, HostExtractor> EXTRACTOR_MAPPING = new HashMap<>();
private HostExtractorFactory() {
}
public static HostExtractor getHostExtractor(URL url)
throws URLNotSupportedException {
String host = url.getHost();
if(EXTRACTOR_MAPPING.containsKey(host)) {
return EXTRACTOR_MAPPING.get(host);
} else {
throw new URLNotSupportedException(
"The url provided does not have a corresponding HostExtractor: ["
+ host + "]");
}
}
public static void register(String hostname, HostExtractor extractor) {
if(StringUtils.isBlank(hostname) == false && extractor != null) {
EXTRACTOR_MAPPING.put(hostname, extractor);
}
}
}
And the user would use it that way:
public class HostExtractorABC extends HostExtractor {
public final static String HOST_NAME = "www.abc.com";
private static class HostPageExtractorLoader {
private static final HostExtractorABC INSTANCE = new HostExtractorABC();
}
private HostExtractorABC() {
if (HostPageExtractorLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
HostExtractorFactory.register(HOST_NAME, this);
}
public static HostExtractorABC getInstance() {
return HostPageExtractorLoader.INSTANCE;
}
...
}
I was patting my own back when I realized this will never work: the user classes are not loaded when I receive the URL, only the factory, which means their constructor never runs, and the map is always empty. So I am back to the drawing board, but would like some ideas around getting this to work or another approach to get rid of this pesky switch statement.
S
Another option is to use the Service Loader approach.
Having your implementers add something like the following in ./resources/META-INF/services/your.package.HostExtractor:
their.package1.HostExtractorABC
their.package2.HostExtractorDEF
their.package3.HostExtractorGHI
...
Then in your code, you can have something like:
HostExtractorFactory() {
final ServiceLoader<HostExtractor> loader
= ServiceLoader.load(your.package.HostExtractor.class);
for (final HostExtractor registeredExtractor : loader) {
// TODO - Perform pre-processing which is required.
// Add to Map? Extract some information and store? Etc.
}
}
I would advice for you to learn about dependency injection (I love spring implementation). You will then be able to write an interface like
public interface HostExtractorHandler {
public String getName();
public HostExtractor getInstance();
}
Than your code can "ask" for all classes that implements this interface, you then would be able to build your map in the initialization phase of your class.
I would use the Reflections library to locate the parsers. They all appear to derive from the HostExtractor class, so use the library to locate all subtypes:
Reflections reflections = new Reflections("base.package");
Set<Class<? extends HostExtractor>> extractorTypes =
reflections.getSubTypesOf(HostExtractor.class);
Use the results to create the instances in your factory:
for (Class<? extends HostExtractor> c : extractorTypes) {
HostExtractor he = c.newInstance();
EXTRACTOR_MAPPING.put(he.getHostName(), he);
}
I made up the getHostName method, but it should be trivial to add to the HostExtractor base class.
I have two helper methods:
public String load(URL url) {...}
public String load(File file) {...}
I want to have a method that calls the appropriate helper method depending on what object type it receives:
public void operate(Object object) {...}
I understand that there is a convoluted way of doing it which is:
public void operate(Object object) {
String data = null;
if (object.getClass().equals(File.class)) {
data = load((File) object);
}
if (object.getClass().equals(URL.class)) {
data = load((URL) object);
}
// operate on the data....
}
However, this does not seem elegant and was curious if there was a better way..
However, this does not seem elegant and was curious if there was a better way.
That's right. This violates the Open-Closed principle. A class must be open to extension but closed to modification. You are also correct when you say that you need a generic Object. Here's what you can do :
Create a Loader interface
public interface Loader<T> {
public String load(T t);
}
Create a loader for loading from File
public class FileLoader implements Loader<File> {
public String load(File f) {
//load from file
}
}
Create a loader for loading from Url
public class UrlLoader implements Loader<Url> {
public String load(URL r) {
//load from url
}
}
Create a class that operates on the data
class DataOperator<T> {
Loader<T> loader;
public SomeClient(Loader<T> loader) {
this.loader = loader;
}
public void operate(T inputSource) {
String data = loader.load(inputSource);
//operate on the data
}
}
Client code can then use the above API as shown below :
DataOperator<File> fileDataOperator = new DataOperator<>(new FileLoader());
fileDataOperator.operate(new File("somefile.txt"));
DataOperator<URL> urlDataOperator = new DataOperator<>(new UrlLoader());
urlDataOperator.operate(new URL("http://somesite.com"));
You might be thinking that this is a lot of classes for solving a simple problem. However, this is actually inline with the well known Open-Closed design principle. Notice how you control what technique is used for loading the data by creating an instance of an appropriate class. Another advantage you get is that you can decide which technique to use at runtime by creating a Factory that takes user input and creates the appropriate concrete subclass. This is a simplified version of the Strategy pattern.
Disclaimer : The code samples presented above have not been tested for compilation errors as I don't have Java on this machine.
A slightly less convoluted way is instanceof, e.g.
if (object instanceof File)) {
data = load((File) object);
}
However, most of the time, using instanceof is a sign that there's a better structure for what you're trying to achieve, e.g.
public void operate(File file) {
operate(load(file));
}
public void operate(URL url) {
operate(load(url));
}
public void operate(String data) {
// operate on the data....
}
You can use instanceof and check it and then cast the object and call the method:
if (obj instanceof File) {
((File) obj).method();
}
else if (obj instanceof URL) {
((URL) obj).method();
}
or the inverse like:
if (obj instanceof File) {
load((File) obj)
}
else if (obj instanceof URL) {
load((URL) obj)
}
You're correct: casting is necessary but inelegant.
Another way to do this if you like GoF Design Patterns is the Visitor pattern aka double dispatch.
A third way is to use Java reflection.
I just stumbled on the same problem and found a different approach using Reflection:
public void operate(Object object) {
Method method = this.getClass().getDeclaredMethod("load", object.getClass());
String data = (String) method.invoke(this, object);
// [...]
}
Of course, it comes with the burden of handling quite a few exceptions.
Overload the operate-method as well. The use the data which you received to call a method which accepts an String.
public static void operate(URL url) {
String data = load(url);
doOperations(data);
}
public static void operate(File file) {
String data = load(file);
doOperations(data);
}
private static void doOperations(String data) {
//TODO Do something with data
}
Im beginner JAVA developer. Here is a method:
private Method getSomething()
{
for (Method m : getClass().getDeclaredMethods())
{
return m;
}
return notFound;
}
private void notFound()
{
throw new Exception();
}
it doesnt matter what it does - if it finds something, then returns a Method - if not, the notFound() method itself should be returned. So the hot spot is at the return notFound; line: if I use return notFound(); then it returns its value, not the method itself. I want something like a reference/pointer. So getSomething() returns something what can be called, and if the returned method is used wrong, it should trigger that Exception - so its not an option to replace return notFound; with throw new Exception(); !
Or the 2nd option is to create a lambda method....
You need to call
this.getClass().getMethod("notFound")
to get the notFound method of the current/this object's class.
So just do this:
return this.getClass().getMethod("notFound");
More details here:
Class.getMethod
EDIT:
You can retrieve i.e. get and call private methods too via reflection.
Here is an example.
import java.lang.reflect.Method;
public class Test001 {
public static void main(String[] args) throws Exception {
Test002 obj = new Test002();
Method m = obj.getClass().getDeclaredMethod("testMethod", int.class);
m.setAccessible(true);
m.invoke(obj, 10);
m.invoke(obj, 20);
System.out.println(m.getName());
}
}
class Test002 {
private void testMethod(int x){
System.out.println("Hello there: " + x);
}
}
You need to use reflection to achieve this:
http://docs.oracle.com/javase/tutorial/reflect/
e.g. to get all methods of a given class:
Class aClass = ...//obtain class object
Method[] methods = aClass.getMethods();
I have to develop an application for Android 1.6 (API 4), which should be able to use the OnAudioFocusChangeListener (available since Android 2.2 - API 8) in the phones with Android 2.2 or later.
Anyone can tell me how to instantiate a listener by reflection?
I have already managed to run static and also non-static methods by reflection, but I don't know how to do with listeners.
This is the listener to reflect:
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
OnAudioFocusChangeListener audioListener = new OnAudioFocusChangeListener() {
#Override
public void onAudioFocusChange(int focusChange) {
// code to execute
}
};
public void getAudioFocus() {
audioManager.requestAudioFocus(audioListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
public void releaseAudioFocus() {
audioManager.abandonAudioFocus(audioListener);
}
This is a code example with methods I managed to run by reflection:
Class BluetoothAdapter = Class.forName("android.bluetooth.BluetoothAdapter");
Method methodGetDefaultAdapter = BluetoothAdapter.getMethod("getDefaultAdapter"); // static method from the BluetoothAdapter class returning a BluetoothAdapter object
Object bluetooth = methodGetDefaultAdapter.invoke(null);
Method methodGetState = bluetooth.getClass().getMethod("getState"); // non-static method executed from the BluetoothAdapter object (which I called "bluetooth") returning an int
int bluetoothState = (Integer) methodGetState.invoke(bluetooth);
In the end I solved it by using a Proxy class. Here is the code!
private AudioManager theAudioManager;
private Object myOnAudioFocusChangeListener = null;
private static final int AUDIOMANAGER_AUDIOFOCUS_GAIN = 1;
private static final int AUDIOMANAGER_AUDIOFOCUS_LOSS = -1;
theAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
// instantiating the OnAudioFocusChangeListener by reflection (as it only exists from Android 2.2 onwards)
// we use a Proxy class for implementing the listener
public void setOnAudioFocusChangeListener() {
Log.i(this, "setOnAudioFocusChangeListener()");
Class<?>[] innerClasses = theAudioManager.getClass().getDeclaredClasses();
for (Class<?> interfaze : innerClasses) {
if (interfaze.getSimpleName().equalsIgnoreCase("OnAudioFocusChangeListener")) {
Class<?>[] classArray = new Class<?>[1];
classArray[0] = interfaze;
myOnAudioFocusChangeListener = Proxy.newProxyInstance(interfaze.getClassLoader(), classArray, new ProxyOnAudioFocusChangeListener());
}
}
}
// called by onResume
public void getAudioFocus() {
if (myOnAudioFocusChangeListener != null) {
Log.i(this, "getAudioFocus()");
try {
Method[] methods = theAudioManager.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equalsIgnoreCase("requestAudioFocus")) {
method.invoke(theAudioManager, myOnAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AUDIOMANAGER_AUDIOFOCUS_GAIN);
Log.i(this, "requestAudioFocus");
}
}
} catch (Exception e) {
Log.e(this, e.getMessage());
}
}
}
// called by onPause
public void releaseAudioFocus() {
if (myOnAudioFocusChangeListener != null) {
Log.i(this, "releaseAudioFocus()");
try {
Method[] methods = theAudioManager.getClass().getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equalsIgnoreCase("abandonAudioFocus"))
method.invoke(theAudioManager, myOnAudioFocusChangeListener);
}
} catch (Exception e) {
Log.e(this, e.getMessage());
}
}
}
PROXY OnAudioFocusChangeListener class
private class ProxyOnAudioFocusChangeListener implements InvocationHandler {
// implements the method onAudioFocusChange from the OnAudioFocusChangeListener
public void onAudioFocusChange(int focusChange) {
Log.e(this, "onAudioFocusChange() focusChange = " + focusChange);
if (focusChange == AUDIOMANAGER_AUDIOFOCUS_LOSS) {
Log.i(this, "AUDIOMANAGER_AUDIOFOCUS_LOSS");
Message msg = mHandler.obtainMessage(ControllerHandler.SET_ON_PAUSE);
mHandler.sendMessage(msg);
} else if (focusChange == AUDIOMANAGER_AUDIOFOCUS_GAIN) {
Log.i(this, "AUDIOMANAGER_AUDIOFOCUS_GAIN");
// no action is taken
}
}
// implements the method invoke from the InvocationHandler interface
// it intercepts the calls to the listener methods
// in this case it redirects the onAudioFocusChange listener method to the OnAudioFocusChange proxy method
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
if (args != null) {
if (method.getName().equals("onAudioFocusChange") && args[0] instanceof Integer) {
onAudioFocusChange((Integer) args[0]);
}
}
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
}
return result;
}
}
IMHO reflection will make your classes less readable. Also reflection is quite a bit slower then normal field or class access.
As an alternative see the wrapper class approach described here: http://android-developers.blogspot.com/2009/04/backward-compatibility-for-android.html
Create interface and two implementations of it, one for API 8+ and the other for the earlier versions. In your API8 class you can use API 8 classes including OnAudioFocusChangeListener. Then instantiate the version based on version of OS, which you can check via Build.VERSION.SDK_INT.