Java 在Android中创建一个在获取位置后返回地址的类

Java 在Android中创建一个在获取位置后返回地址的类,java,android,asynchronous,Java,Android,Asynchronous,我正在尝试在Android中创建一个位置获取器类,该类将获取位置并返回地址实例 此类包括向用户请求位置权限、显示权限理由、显示无法获取位置时的错误消息以及带有进度条的AlertDialog,该进度条将显示何时获取位置,并在找到位置或发生错误时隐藏自身 这是我目前的工作: public class LocationFetcher { static Handler handler = new Handler(Looper.getMainLooper()); AlertDialog

我正在尝试在Android中创建一个位置获取器类,该类将获取位置并返回
地址
实例

此类包括向用户请求位置权限、显示权限理由、显示无法获取位置时的错误消息以及带有进度条的AlertDialog,该进度条将显示何时获取位置,并在找到位置或发生错误时隐藏自身

这是我目前的工作:

public class LocationFetcher {

    static Handler handler = new Handler(Looper.getMainLooper());
    AlertDialog alertDialog;
    Context context;
    View retry;

    LocationFetcher(Context context, View retry) {
        this.context = context;
        this.retry = retry;
        handler.post(() -> {
            alertDialog = new AlertDialog.Builder(context)
                    .setTitle("Trying to fetch location...")
                    .setCancelable(false)
                    .create();
            alertDialog.setView(LayoutInflater.from(context).inflate(R.layout.progress_dialog, null));
        });
    }

    void showErrorMessage() {
        handler.post(() -> {
            alertDialog.dismiss();
            new Builder(context)
                    .setTitle("Error getting location data")
                    .setMessage("Please check the location settings in the Settings app and try again")
                    .setPositiveButton("Retry", (dialog, which) -> {
                        retry.performClick();
                        alertDialog.dismiss();
                    })
                    .setNegativeButton("Cancel", (dialog, which) -> {
                        // do nothing
                    }).show();
        });
    }

    Address getLocation() {
        final int LOCATION_PERMISSION_REQUEST_CODE = 1;
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            Task<Location> locationTask = LocationServices.getFusedLocationProviderClient(context).getLastLocation();
            handler.post(alertDialog::show);
            try {
                return Executors.newSingleThreadExecutor().submit(() -> {
                    boolean flag = true;
                    while (flag) {
                        if (locationTask.isComplete()) {
                            if (locationTask.isSuccessful()) {
                                Location location = locationTask.getResult();
                                if (location != null) {
                                    List<Address> addresses = null;
                                    try {
                                        addresses = new Geocoder(context, Locale.getDefault()).getFromLocation(location.getLatitude(), location.getLongitude(), 1);
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                    if (addresses != null) {
                                        alertDialog.dismiss();
                                        return addresses.get(0);
                                    }
                                    else showErrorMessage();
                                } else showErrorMessage();
                            } else if (locationTask.getException() != null)
                                showErrorMessage();
                            flag = false;
                        }
                    }
                    return null;
                }).get(5, TimeUnit.SECONDS);
            } catch (ExecutionException | InterruptedException | TimeoutException e) {
                e.printStackTrace();
                showErrorMessage();
            }
        } else // if permission is not there, show permission rationale
            if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.ACCESS_FINE_LOCATION))
                handler.post(() -> {
                    new AlertDialog.Builder(context)
                            .setTitle("Permission rationale")
                            .setMessage("This permission is required to access your location. Without it, the app cannot get your location to serve you better.")
                            .setPositiveButton("Ok", (dialog, which) -> {
                                ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
                            }).setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss())
                            .show();
                });
            else // if rationale not needed, ask for permission again
                ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
        return null;
    }
}
并在实际的GetLocation方法中显示对话框:

handler.post(alertDialog::show)

这里发生的事情基本上是因为构造函数中的
handler.post
部分作为一个异步操作工作,有些是
handler.post(alertDialog::show)行在创建
警报对话框
之前执行,这导致
GetLocation
方法抛出一个
NullPointerException

在使用
alertDialog
之前,如何等待构造函数和异步任务(或将异步转换为同步)完成

编辑:

编辑1:
注:
我想异步调用GetLocation方法,即调用如下函数:

Executors.newSingleThreadExecutor().execute(() -> {
                    LocationFetcher locationFetcher = new LocationFetcher(this, tvCurrentLocation);
                    Address address = locationFetcher.getLocation();
                    });  

同步创建alertDialog并在新线程中调用方法
getLocation

构造函数

LocationFetcher(Context context, View retry) {
    this.context = context;
    this.retry = retry;
    alertDialog = new AlertDialog.Builder(context)
            .setTitle("Trying to fetch location...")
            .setCancelable(false)
            .create();
    alertDialog.setView(LayoutInflater.from(context).inflate(R.layout.progress_dialog, null));
}
和方法调用

LocationFetcher locationFetcher = new LocationFetcher(this, tvCurrentLocation);
Executors.newSingleThreadExecutor().execute(() -> {    
    Address address = locationFetcher.getLocation();
}); 

为什么要异步创建alertDialog?只要在构造函数中同步创建它就可以了。@Yoleth我忘了提到什么。我已经在编辑中添加了它,请检查。仍然是相同的答案,您已经在主线程之外的另一个线程中,因此同步创建alertdialog:)@Yoleth您不能在非UI线程中创建UI元素。这是一个错误。我真不敢相信我竟然如此愚蠢。非常感谢。顺便说一句,您忘记删除答案的构造函数部分中的
处理程序。post
。是的,我看到了,我刚刚编辑:)
LocationFetcher locationFetcher = new LocationFetcher(this, tvCurrentLocation);
Executors.newSingleThreadExecutor().execute(() -> {    
    Address address = locationFetcher.getLocation();
});