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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With