我正在制作一个Android库,我需要删除由使用我的库的开发人员添加的游戏服务依赖项。我需要用户位置最后位置,所以我需要使用反射,因为位置库不会直接包含在我的库中。
但是如何通过反射从构建器创建googleApiClient ?
深入研究reflection和Play Services生成以下脚本。它包含获取AdvertisingId和位置的代码。
Gist也可用。
private void getLocation(Context context) {
Log.d(LOG_TAG, "getLocation");
if (context.getPackageManager().checkPermission(
Manifest.permission.ACCESS_FINE_LOCATION, context.getPackageName()) == PackageManager.PERMISSION_GRANTED ||
context.getPackageManager().checkPermission(
Manifest.permission.ACCESS_COARSE_LOCATION, context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
try {
Class<?> apiClientBuilderClass = Class.forName("com.google.android.gms.common.api.GoogleApiClient$Builder");
Class<?> connectionCallbackClass = Class.forName("com.google.android.gms.common.api.GoogleApiClient$ConnectionCallbacks");
Class<?> connectionFailedCallbackClass = Class.forName("com.google.android.gms.common.api.GoogleApiClient$OnConnectionFailedListener");
Class<?> locationServicesClass = Class.forName("com.google.android.gms.location.LocationServices");
Constructor<?> constructorApiBuilder = apiClientBuilderClass.getConstructor(Context.class);
Object objectApiBuilder = constructorApiBuilder.newInstance(mContext);
// Create intance of listener ConnectionCallbacks
Class<?>[] connectionClassArray = new Class<?>[1];
connectionClassArray[0] = connectionCallbackClass;
sGoogleApiClientListener = Proxy.newProxyInstance(
connectionCallbackClass.getClassLoader(), connectionClassArray, new GoogleApiClientListener());
Method connectionMethodObject = apiClientBuilderClass.getMethod("addConnectionCallbacks", connectionCallbackClass);
connectionMethodObject.invoke(objectApiBuilder, sGoogleApiClientListener);
// Create instance of OnConnectionFailedListener listener
Class<?>[] connectionFailedClassArray = new Class<?>[1];
connectionFailedClassArray[0] = connectionFailedCallbackClass;
sGoogleApiClientFailedListener = Proxy.newProxyInstance(
connectionFailedCallbackClass.getClassLoader(), connectionFailedClassArray, new GoogleApiClientFailedListener());
Method connectionFailedMethodObject = apiClientBuilderClass.getMethod("addOnConnectionFailedListener", connectionFailedCallbackClass);
connectionFailedMethodObject.invoke(objectApiBuilder, sGoogleApiClientFailedListener);
// Add Api
Method addApiMethod = apiClientBuilderClass.getMethod("addApi", Class.forName("com.google.android.gms.common.api.Api"));
addApiMethod.invoke(objectApiBuilder, locationServicesClass.getField("API").get(null));
// Build
Method buildMethod = apiClientBuilderClass.getMethod("build");
sGoogleApiClient = buildMethod.invoke(objectApiBuilder);
// Connect
for (Method method : sGoogleApiClient.getClass().getMethods()) {
if (!"connect".equals(method.getName())) {
continue;
}
method.invoke(sGoogleApiClient);
break;
}
} catch (Exception e) {
// Location is not essential, so it's not an error
Log.v(LOG_TAG, "Exception on getLocation " + Log.getStackTraceString(e));
}
} else {
sLocation = "";
}
}
// GoogleApiClient for Locationsucceded
public void onConnected() {
Location lastLocation = null;
try {
// FusedLocationApi is a field of LocationServices, getting it.
Class<?> locationServicesClass = Class.forName("com.google.android.gms.location.LocationServices");
Class<?> locationProviderClass = Class.forName("com.google.android.gms.location.FusedLocationProviderApi");
for (Method method : locationProviderClass.getMethods()) {
if (!"getLastLocation".equals(method.getName())) {
continue;
}
Object object = locationServicesClass.getField("FusedLocationApi").get(null);
lastLocation = (Location) method.invoke(object, sGoogleApiClient);
break;
}
} catch (Exception e) {
// Location is not essential, so it's not an error
Log.v(LOG_TAG, "Exception on invoke onConnected for getLastLocation" + Log.getStackTraceString(e));
}
// Location mLastLocation = LocationServices.FusedLocationApi.getLastLocation(sGoogleApiClient);
if (lastLocation != null) {
sLocation = lastLocation.getLatitude() + "," + lastLocation.getLongitude();
Log.d(LOG_TAG, "Location received : " + sLocation);
} else {
sLocation = "";
}
newResourcesAvailable();
unregisterConnectionCallbacks();
}
// GoogleApiClient for Location failed somewhere
public void onConnectionSuspended() {
sLocation = "";
newResourcesAvailable();
unregisterConnectionCallbacks();
}
// GoogleApiClient for Location failed somewhere
public void onConnectionFailed() {
sLocation = "";
newResourcesAvailable();
unregisterConnectionCallbacks();
}
private void unregisterConnectionCallbacks(){
for (Method method : sGoogleApiClient.getClass().getMethods()) {
if (!"unregisterConnectionCallbacks".equals(method.getName())) {
continue;
}
try {
method.invoke(sGoogleApiClient, sGoogleApiClientListener);
} catch (Exception ignored) {
// Exception always occur here but cannot make it work
}
break;
}
for (Method method : sGoogleApiClient.getClass().getMethods()) {
if (!"unregisterConnectionFailedListener".equals(method.getName())) {
continue;
}
try {
method.invoke(sGoogleApiClient, sGoogleApiClientFailedListener);
} catch (Exception ignored) {
// Exception always occur here but cannot make it work
}
break;
}
}
protected class RetrieveAdvertisingId extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
String id = "";
try {
Object AdvertisingInfoObject = getAdvertisingInfoObject(mContext);
id = (String) invokeInstanceMethod(AdvertisingInfoObject, "getId", null);
} catch (Exception e) {
// Catch reflection exception and GooglePlayServicesNotAvailableException |
// GooglePlayServicesRepairableException
Log.e(LOG_TAG, "Exception while getting AdvertisingId", e);
}
return id;
}
@Override
protected void onPostExecute(String advertisingId) {
super.onPostExecute(advertisingId);
if (advertisingId != null && !advertisingId.isEmpty()) {
Log.i(LOG_TAG, "Advertising ID retrieved: " + advertisingId);
sAdvertisingId = advertisingId;
} else {
Log.e(LOG_TAG, "AdversitingId is not available");
sAdvertisingId = "";
}
AsynchronousParameterManager.this.newResourcesAvailable();
}
}
/**
* Returns the AdvertisingIdInfo object
*
* @param context the android context
* @return the advertising id information object
* @throws Exception
*/
private Object getAdvertisingInfoObject(Context context) throws Exception {
return invokeStaticMethod(
"com.google.android.gms.ads.identifier.AdvertisingIdClient",
"getAdvertisingIdInfo",
new Class[]{Context.class},
context
);
}
/**
* Invokes a static method within a class
* if it can be found on the classpath.
*
* @param className The full defined classname
* @param methodName The name of the method to invoke
* @param cArgs The args that the method can take
* @param args The args to pass to the method on invocation
* @return the result of the method invoke
* @throws Exception
*/
private Object invokeStaticMethod(String className, String methodName,
Class[] cArgs, Object... args) throws Exception {
Class classObject = Class.forName(className);
return invokeMethod(classObject, methodName, null, cArgs, args);
}
/**
* Invokes a method on a static instance
* within a class by reflection.
*
* @param instance The instance to invoke a method on
* @param methodName The name of the method to invoke
* @param cArgs The args that the method can take
* @param args The args to pass to the method on invocation
* @return the result of the method invoke
* @throws Exception
*/
private Object invokeInstanceMethod(Object instance, String methodName,
Class[] cArgs, Object... args) throws Exception {
Class classObject = instance.getClass();
return invokeMethod(classObject, methodName, instance, cArgs, args);
}
/**
* Invokes methods of a class via reflection
*
* @param classObject The class to attempt invocation on
* @param methodName The name of the method to invoke
* @param instance The object instance to invoke on
* @param cArgs The args that the method can take
* @param args The args to pass to the method on invocation
* @return the result of the method invoke
* @throws Exception
*/
private Object invokeMethod(Class classObject, String methodName, Object instance,
Class[] cArgs, Object... args) throws Exception {
Method methodObject = classObject.getMethod(methodName, cArgs);
return methodObject.invoke(instance, args);
}
private class GoogleApiClientListener implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (args != null) {
if (method.getName().equals("onConnected")) {
AsynchronousParameterManager.this.onConnected();
} else if (method.getName().equals("onConnectionSuspended") ) {
AsynchronousParameterManager.this.onConnectionSuspended();
}
} else {
return 0;
}
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
}
return null;
}
}
private class GoogleApiClientFailedListener implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
try {
if (method.getName().equals("onConnectionFailed")) {
AsynchronousParameterManager.this.onConnectionFailed();
}
return 0;
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
}
}
}