Android位置监听器:onStatusChanged和onProviderDisabled出现AbstractMethodError错误

28
我有一个简单的活动,里面有一个按钮,使用LocationManager尝试获取当前位置:
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());
        }
    });
}

使用API级别22在Android Studio中创建了一个模拟器,在授予权限后并开启模拟器的GPS后,当单击按钮时,应用程序崩溃并显示以下错误:

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

如果我在关闭GPS的情况下按下按钮,则会出现以下错误:

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

如果我在我的小米Mi A1上尝试这个应用程序,只有当GPS关闭并且点击按钮时它才会崩溃,在GPS打开并按下按钮时不会崩溃。

根据文档,这些方法被标记为默认,所以我不需要实现它们。

这种行为有什么原因吗?

6个回答

46

只需在代码末尾添加这三个函数:

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

}

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

}

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

并编写您希望在状态更改时执行的代码


2
不太确定,但我猜这是由于位置监听器类的更改所致。旧版Android需要定义这些函数以使它们不是抽象的。最近,Android将所有三个函数都设置为非抽象的,并弃用了onStatusChanged()。请参见https://developer.android.com/sdk/api_diff/30/changes/android.location.LocationListener。 - mohit48
2
我之前没有实现onProviderEnabled方法,导致出现了同样的错误(AbstractMethodError),现在我已经实现了它并清除了super方法,不再出现崩溃。 - Ahmet B.
1
@ahmet locationListner类是一个接口。因此,我认为您不应该在其函数中使用super。 - mohit48
@mohit48,我现在所做的是将我的“locationListener”变量更改为“this”。但这还不够。我想让我在“onProviderDisabled”上写点什么,否则即使它被覆盖了,它也会崩溃!问题是当它到达“locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,2000,2,this);”时,它只会触发“onProviderDisabled”,然后就没了... - Panagiss
2
我已更新我的帖子,以帮助其他人解决问题。https://dev59.com/X7_pa4cB1Zd3GeqP-gRu#65807111 感谢@mohit48 - Panagiss
显示剩余12条评论

24

我对以上答案并不完全满意,所以决定详细说明一下。

正如@sunlover3所说,有问题的方法的实现已经改变。在API 30之前,它们是抽象的,这意味着如果compileSdk不是30,那么OP的代码会出现编译错误(这些方法必须被实现)。但在API 30中,他们添加了空的默认方法实现,这意味着如果compileSdk为30,则编译器将检查这些方法并且不会报错,因为它们已经被实现了。

但是,如果在低于30版本的旧操作系统上运行使用compileSdk 30的应用程序,则这些方法的实现仍然是旧的,也就是说没有实现,它们是抽象的!因此,会出现错误。

老实说,这是一个明显的SDK开发人员的错误,这种事情不应该发生。最坏的情况是应该有一个编译时警告,提醒用户仍然需要重写这些方法以兼容较老的版本。我希望有人能够比我更勤奋地将此问题报告给谷歌跟踪器 :)


6

这是我的情况。在Android 9(API28)和Android 10 (API 29)上的android.location.LocationManager类中,有这个方法:

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

这似乎被称为。此外,Android 29和Android 30之间的LocationListener实现差异如下:

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) {}
}

显然,“default”这个单词是关键。因为我和你一样遇到了同样的问题,所以现在需要找到一个解决方法。或者使用mohit48的回答。祝你好运!

2

在您的代码中重写方法。

override fun onProviderDisabled(provider: String) {}
override fun onProviderEnabled(provider: String) {}
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
...

2

我曾遇到同样的问题,按照以上建议覆盖onStatusChanged()方法后成功解决了。

不过,Android团队可能正在开发一些新功能,我发现有这个接口可以解决问题,经过我的测试,无需覆盖方法即可成功。

您可以在这里查看androidx.core.location包的摘要。

要在项目中使用这些新类,您应该使用最新的beta版本androidx.core。请在此处阅读相关内容。


1
只需添加实现LocationListener即可。
@Override
public void onProviderEnabled(@NonNull String provider) {

}

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

}

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

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community
你的回答与 https://dev59.com/6lIG5IYBdhLWcg3whwic#64643361 有何不同?请勿发布重复内容或添加更多细节/解释。 - Antoine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接