getLastLocation使用Reflection与GoogleApiClient和LocationServices

时间:2015-07-17 08:09:47

标签: java android reflection google-play-services

我正在使用Android库,我需要删除由使用我的库的开发人员添加的播放服务依赖项。我需要用户位置的最后位置,所以我需要使用反射,因为位置库不会直接包含在我的库中。

但是如何通过反射从构建器创建googleApiClient?

1 个答案:

答案 0 :(得分:1)

深入研究反射和Play服务会产生以下脚本。它包含获取AdvertisingId和位置的代码。

Gist also available

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());
        }
    }
}