Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android LocationListener, AbstractMethodError on onStatusChanged and onProviderDisabled

I have a simple activity with a button, that uses the LocationManager to try to get the current location:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Button buttonGps = findViewById(R.id.buttonGps);

    final Context activity = this;

    buttonGps.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (!checkForPhonePermission()) {
                return;
            }

            LocationManager locationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);

            locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, new LocationListener() {
                @Override
                public void onLocationChanged(@NonNull Location location) {
                    Log.d(TAG, location.getLatitude() + "");
                }
            }, Looper.myLooper());
        }
    });
}

I created an emulator in Android Studio with the API level 22, and after giving the permission, and with the gps of emulator on, the app crashes with this error when clicking the button:

java.lang.AbstractMethodError: abstract method "void android.location.LocationListener.onStatusChanged(java.lang.String, int, android.os.Bundle)"

If I press the button with the gps turned off, I get this error instead:

java.lang.AbstractMethodError: abstract method "void android.location.LocationListener.onProviderDisabled(java.lang.String)"

If I try the app on my Xiaomi Mi A1, it only crashes when the gps is off and the button is clicked, it does not crash when the gps is on and the button is pressed.

From the documentation, those methods are marked with default, so I should not need to implement them.

Is there any reason for this behavior?

like image 733
Bsa0 Avatar asked Nov 02 '20 00:11

Bsa0


5 Answers

just add these three function at the end of your code:

@Override
public void onProviderEnabled(@NonNull String provider) {

}

@Override
public void onProviderDisabled(@NonNull String provider) {

}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
    
}

and write your code that you would like to execute when status changes

like image 168
mohit48 Avatar answered Oct 16 '22 16:10

mohit48


I was not completely satisfied with the answers above and decided to elaborate a little.

As @sunlover3 rightfully stated, the problematic methods' implementation was changed. Before API 30 they were abstract, which means that if the compileSdk would be anything but 30 the OP's code would give compile error (these methods must have been implemented). But in the API 30 they added empty default methods' implementation, which means that if the compileSdk is 30, the compiler checks these methods and gives no error, they are already implemented.

BUT, if one runs an app with compileSdk 30 on an older OS with the version below 30, these methods' implementation is still old, i.o. there is none, they are abstract! Hence, the error.

Honestly, that's an obvious SDK's devs error, such things must not happen. At worst there should've been a compile-time warning that one should still override these methods for the older versions compatibility. I hope someone less lazy than I am on this topic will report this to google tracker :)

like image 42
Kirill Starostin Avatar answered Oct 16 '22 17:10

Kirill Starostin


Here's my situation. On Android 9 (API 28) and Android 10 (API 29), in the android.location.LocationManager class there is this method:

private void _handleMessage(Message msg) {
    switch (msg.what) {
        case TYPE_LOCATION_CHANGED:
            Location location = new Location((Location) msg.obj);
            mListener.onLocationChanged(location);
            break;
        case TYPE_STATUS_CHANGED:
            Bundle b = (Bundle) msg.obj;
            String provider = b.getString("provider");
            int status = b.getInt("status");
            Bundle extras = b.getBundle("extras");
            mListener.onStatusChanged(provider, status, extras);
            break;
        case TYPE_PROVIDER_ENABLED:
            mListener.onProviderEnabled((String) msg.obj);
            break;
        case TYPE_PROVIDER_DISABLED:
            mListener.onProviderDisabled((String) msg.obj);
            break;
    }
    locationCallbackFinished();
}

which seems to be called. Also, the difference of implementation for LocationListener between Android 29 and Android 30 is this:

Android 29:
public interface LocationListener {
    void onLocationChanged(Location location);
    
    @Deprecated
    void onStatusChanged(String provider, int status, Bundle extras);

    void onProviderEnabled(String provider);

    void onProviderDisabled(String provider);
}

Android 30:
public interface LocationListener {
    void onLocationChanged(@NonNull Location location);

    @Deprecated
    default void onStatusChanged(String provider, int status, Bundle extras) {}

    default void onProviderEnabled(@NonNull String provider) {}

    default void onProviderDisabled(@NonNull String provider) {}
}

It's obvious that the word "default" is the difference. Given that I have the same problem as you, I need to find a workaround now. Or use the response of mohit48. Good luck!

like image 4
sunlover3 Avatar answered Oct 16 '22 17:10

sunlover3


I faced the same issue and i managed to solve it by overriding the onStatusChanged() as suggested above.

However, the Android team might be working on something and i found that there is this interface that solves the problem without overriding, as far as i have tested.

You can see here the summary for the androidx.core.location package.

In order to use this new classes in your project you should use the latest beta version of the androidx.core. Read here.

like image 2
jvoyatz Avatar answered Oct 16 '22 16:10

jvoyatz


Override the methods in your code. ...

override fun onProviderDisabled(provider: String) {}
override fun onProviderEnabled(provider: String) {}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
...
like image 1
DALI RICHARD HILLARY Avatar answered Oct 16 '22 17:10

DALI RICHARD HILLARY