8
votes

Precondition and build config: I am trying to build my app with AndroidStudio 2.4 review 5 by setting below build config paramters for Android O perview device, compileSdkVersion 'android-O' buildToolsVersion '25.0.0' compileOptions.encoding = 'ISO-8859-1' minSdkVersion 16 targetSdkVersion 'O' // Enabling multidex support. multiDexEnabled true

Issue Description: My app uses these below deprecated methods of ConnectivityManager class for devices with older version of android. -ConnectivityManager.startUsingNetworkFeature() - ConnectivityManager.stopUsingNetworkFeature() - ConnectivityManager.requestRouteToHost()

When I try to build my app which has these above api, it is giving compile time error as follows,

Error:(626, 48) error: cannot find symbol method startUsingNetworkFeature(int,String) Error:(7393, 27) error: cannot find symbol method stopUsingNetworkFeature(int,String) Error:(69, 36) error: cannot find symbol method requestRouteToHost(int,int)

Ideally these api should not give compile time error for backward compatibity purpose. Please let me know how to resolve these compile time errors.

I can't remove these methods from code as they are required for devices with older android version(less than Android L version)

5
Hey Mallik. Still no luck? with this? - Filipe Figueiredo
with android L check and reflection will help! - NitZRobotKoder
The methods are still there but they're missing from public API. Either compile against SDK 25, or use reflection to access these methods on devices older than Lollipop. OR What if you made a library module which compiles against SDK 25 and serves a compatibility class for your main module? - Eugen Pechanec

5 Answers

3
votes

Make an new android library module in your project. Make sure this library has compileSdkVersion 25. Then put the following class inside the library:

@SuppressWarnings("Deprecation")
public class NetworkFeaturesCompat {
    public static NetworkFeaturesCompat from(final ConnectivityManager cm) {
        return new NetworkFeaturesCompat(cm);
    }

    private final ConnectivityManager mConnectivityManager;

    private NetworkFeaturesCompat(final ConnectivityManager cm) {
        mConnectivityManager = cm;
    }

    public int startUsingNetworkFeature(final int networkType, final String feature) {
        return mConnectivityManager.startUsingNetworkFeature(networkType, feature);
    }

    public int stopUsingNetworkFeature(final int networkType, final String feature) {
        return mConnectivityManager.stopUsingNetworkFeature(networkType, feature);
    }

    public boolean requestRouteToHost(final int networkType, final int hostAddress) {
        return mConnectivityManager.requestRouteToHost(networkType, hostAddress);
    }
}

Now make your main module depend on this library by altering its build.gradle file:

dependencies {
    compile project(":your-new-library-module-name")
}

Build your project.

Now you can use this helper class from your main project.

You may need to add this to your proguard rules (I haven't actually tested this bit):

-dontwarn android.net.ConnectivityManager

Please note that this approach will still throw UnsupportedOperationException on Android 6+.

Why this works?

The original methods are still present in the SDK, they're just hidden from public API in SDK 26. This is why it doesn't crash at run time.

The library module is compiled against SDK 25 where the methods are still part of public API. This is why it doesn't crash at compile time.

Since the whole project is compiled against SDK 26 proguard may complain about missing methods. In that case just ignore the complaint.

3
votes

Because I cannot add comments to Antilope's answer, I will correct it here.

It should be method.invoke(connectivityManager, ConnectivityManager.TYPE_MOBILE, "enableHIPRI");

So in general the code looks like:

try{
    java.lang.reflect.Method method = connectivityManager.getClass().getMethod("startUsingNetworkFeature",
    int.class, String.class); 
    resultInt = (int) method.invoke(connectivityManager, ConnectivityManager.TYPE_MOBILE, 
"enableHIPRI");
} catch (Exception e) { 
    //Do something 
}

and

try{
    java.lang.reflect.Method method = connectivityManager.getClass().getMethod("requestRouteToHost", int.class, int.class);
    resultBool = (boolean) method.invoke(connectivityManager, ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
} catch (Exception e) { 
    //Do something 
}

The above works on targetSDK 27

1
votes

As suggested on thes bug report thread (https://issuetracker.google.com/issues/64992660), calling though reflection works even while targeting API 27. Tested on different devices, even on a Galaxy Note8 withe Oreo (8.0.0).

What i previously called:

resultInt = connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");

now can be called as:

try{
    java.lang.reflect.Method method = connectivityManager.getClass().getMethod("startUsingNetworkFeature",
    int.class, String.class); resultInt = (int) method.invoke(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");
} catch (Exception e) { 
    //Do something 
}

And also works for:

resultBool = connectivityManager.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);

now can be called as:

try{
    java.lang.reflect.Method method = connectivityManager.getClass().getMethod("requestRouteToHost", int.class, int.class);
    resultBool = (boolean) method.invoke(ConnectivityManager.TYPE_MOBILE_HIPRI, hostAddress);
} catch (Exception e) { 
    //Do something 
}
0
votes

Make sure your library module has compileSdkVersion 25.

0
votes

For Android versions below L, a reflection way of calling the startUsingNetworkFeature, stopUsingNetworkFeature, requestRouteToHost will help.

Google should come up with COMPATIBILITY support.