安卓:定位一次并显示加载对话框

26
我正在编写一个应用程序,它需要用户的当前位置(lastknownlocation 不会非常有用),并从数据库中显示所有最接近他们的“物品”列表。
我已经成功地找到了最接近物品的方法,但目前只使用硬编码的纬度和经度位置,现在是时候实现查找实际位置了。
有人能提供一个示例,演示如何使应用程序在后台查找当前精细位置,并等待应用程序。我只需要一次位置,而不是随着人移动而更新。我已经实现了位置监听器,但我明白获得GPS定位可能很慢。我已看到其他示例使用上次已知位置或实现持续运行和更新的位置监听器,但由于我的Activity需要位置坐标才能显示任何内容,应用程序就会崩溃。我需要在搜索时显示进度对话框。
如何让locationlistener 在后台仅运行一次,然后在找到位置后删除位置更新。我需要其余的应用程序等待GPS位置,或者在20-30秒后超时。如果可能,我希望用户能够取消对话框,如果他们等候这个操作感到烦了,他们可以通过输入区域等来进行搜索。
我一直试图用线程完成这个任务,但是它比我预想的要复杂得多,而且还无法正常工作。在iPhone上,这要简单得多?
有人能提供一种不错的方法吗?我已经为此挣扎了一个星期,它真的使我落后于应用程序的其余部分完成日期。
总之,我想要:
* 找到当前坐标
* 在获取位置时显示进度对话框
* 应用程序可以等待成功的位置或在一定时间后放弃?
* 如果成功:关闭进度对话框,并使用坐标运行我的其他查找附近物品的方法。
* 如果未能获得位置:关闭进度对话框并显示错误消息,并鼓励用户使用菜单进入搜索活动。
* 如果用户从菜单中选择了地图视图,则使用这些坐标显示地图上的当前位置,并使用数据库中所匹配的所有附近项目的坐标来放置大头针。
谢谢您们,我希望我已经解释得足够清楚。如果有任何问题,请随时问我,因为我渴望完成这一部分。 Cheers
编辑 请求代码 locationList activity file
protected void onStart()
{
    super.onStart();

    // ....

    new LocationControl().execute(this);
}


private class LocationControl extends AsyncTask<Context, Void, Void>
{
    private final ProgressDialog dialog = new ProgressDialog(branchAtmList.this);

    protected void onPreExecute()
    {
        this.dialog.setMessage("Determining your location...");
        this.dialog.show();
    }

    protected Void doInBackground(Context... params)
    {
        LocationHelper location = new LocationHelper();

        location.getLocation(params[0], locationResult);

        return null;
    }

    protected void onPostExecute(final Void unused)
    {
        if(this.dialog.isShowing())
        {
            this.dialog.dismiss();
        }

        useLocation(); //does the stuff that requires current location
    }

}

public LocationResult locationResult = new LocationResult()
{
    @Override
    public void gotLocation(final Location location)
    {
        currentLocation = location;
    }
};  

LocationHelper 类

    package org.xxx.xxx.utils;

import java.util.Timer;
/*
imports
*/

public class LocationHelper
{
    LocationManager locationManager;
    private LocationResult locationResult;
    boolean gpsEnabled = false;
    boolean networkEnabled = false;

    public boolean getLocation(Context context, LocationResult result)
    {       
        locationResult = result;

        if(locationManager == null)
        {
            locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
        }
            //exceptions thrown if provider not enabled
            try
            {
                gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
            }
            catch (Exception ex) {}
            try
            {
                networkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
            }
            catch (Exception ex) {}

            //dont start listeners if no provider is enabled
            if(!gpsEnabled && !networkEnabled)
            {
                return false;
            }

            if(gpsEnabled)
            {
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
            }
            if(networkEnabled)
            {
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
            }


            GetLastLocation();
            return true;
    }

    LocationListener locationListenerGps = new LocationListener() {
        public void onLocationChanged(Location location)
        {
            locationResult.gotLocation(location);
            locationManager.removeUpdates(this);
            locationManager.removeUpdates(locationListenerNetwork);

        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extra) {}
    };

    LocationListener locationListenerNetwork = new LocationListener() {
        public void onLocationChanged(Location location)
        {
            locationResult.gotLocation(location);
            locationManager.removeUpdates(this);
            locationManager.removeUpdates(locationListenerGps);

        }
        public void onProviderDisabled(String provider) {}
        public void onProviderEnabled(String provider) {}
        public void onStatusChanged(String provider, int status, Bundle extra) {}

    };

    private void GetLastLocation()
    {
            locationManager.removeUpdates(locationListenerGps);
            locationManager.removeUpdates(locationListenerNetwork);

            Location gpsLocation = null;
            Location networkLocation = null;

            if(gpsEnabled)
            {
                gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
            }
            if(networkEnabled)
            {
                networkLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            }

            //if there are both values use the latest one
            if(gpsLocation != null && networkLocation != null)
            {
                if(gpsLocation.getTime() > networkLocation.getTime())
                {
                    locationResult.gotLocation(gpsLocation);
                }
                else
                {
                    locationResult.gotLocation(networkLocation);
                }

                return;
            }

            if(gpsLocation != null)
            {
                locationResult.gotLocation(gpsLocation);
                return;
            }

            if(networkLocation != null)
            {
                locationResult.gotLocation(networkLocation);
                return;
            }

            locationResult.gotLocation(null);
    }

    public static abstract class LocationResult
    {
        public abstract void gotLocation(Location location);
    }
}

这个似乎为了只获取当前位置而付出了很大的努力?我不需要更新或其他任何东西?有没有更简单的方法让应用程序等待结果?我已经卡在这里很长时间了。


https://dev59.com/QnA75IYBdhLWcg3wuLjD#9811633 - Rajal
3个回答

16
使用一个AsyncTask,同时使用network_provider和gps_provider,这意味着需要两个监听器。gps获取定位的时间较长,有时可能需要一分钟,而且只能在户外使用,而network则可以很快地获取位置。
一个好的代码示例在这里:What is the simplest and most robust way to get the user's current location on Android? 关于AsyncTask,可以参考http://developer.android.com/reference/android/os/AsyncTask.html
这里也有许多相关的代码示例,例如:Android Show a dialog until a thread is done and then continue with the program 编辑: 代码概念:
添加类变量
boolean hasLocation = false;

在onCreate中调用(而不是AsyncTask中):

locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

然后在locationListener.onLocationChanged中,一旦找到位置,就更改该值:

boolean hasLocation = false;

异步任务(AsyncTask):保持原样,但不要调用

LocationHelper location = new LocationHelper();
location.getLocation(params[0], locationResult);

那里,改为执行

Long t = Calendar.getInstance().getTimeInMillies(); 
while (!hasLocation && Calendar.getInstance().getTimeInMillies()-t<30000)) {
    Thread.Sleep(1000); 
};    

在两者之间加上适当的延迟可能就足够了。


谢谢,这看起来很有帮助。我认为GPS API的东西本来就是异步的,不是吗?还是说我仍然需要实现它以便应用程序等待结果?基于这些示例:那么我是否应该从doInBackground(final String... args)中调用myLocation.getLocation(this, locationResult)?我真的不明白onPostExecute(final Void unused)何时被调用,因为MyLocation的最终结果是gotLocation(final Location location)。如果计时器到期会发生什么? - Daniel Bowden
是的,GPS相关的内容是异步的,但显示进度不是。因此,您需要将其包装在AsyncTask中,因为进度对您的GPS状态/结果有依赖。 onPostExecute和onPreExecute在UI主线程上运行,因此您可以在其中更新UI,例如一旦您获得了GPS定位,还可以在其中关闭进度对话框。 - Mathias Conradt
好的,谢谢。我会尝试一下。onPostExecute如何知道我何时获得了GPS定位或执行完成?即它如何知道getlocation()被调用或计时器运行结束的情况? - Daniel Bowden
1
今天尝试使用AsyncTask遇到了问题。我在doInBackground中调用LocationHelper location = new LocationHelper();和location.getLocation(params[0], locationResult);当执行到下面这行代码时: locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);就会抛出异常: ERROR/AndroidRuntime(6534): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()请帮忙解决一下? - Daniel Bowden
1
到目前为止,对我来说工作得很好,谢谢!不太确定超时时间要设置多长,因为在我的设备上获取GPS或网络位置似乎很棘手,但我会继续测试值,直到满意为止。标记为已接受。 :) - Daniel Bowden
显示剩余8条评论

12

我仍然不喜欢这个工作方式,它似乎比应该的更加复杂,但在有人提供更好的方法之前,我暂时让它工作...

如果有人发现了一个更好、更简洁的方法来获取用户当前位置的坐标,那就不用再寻找了,非常感谢!

private LocationControl locationControlTask;
private boolean hasLocation = false;
LocationHelper locHelper;

从 onCreate() 调用

locHelper = new LocationHelper();
locHelper.getLocation(context, locationResult);
locationControlTask = new LocationControl();
locationControlTask.execute(this);

我随后创建了一个名为 LocationControl 的私有类。
private class LocationControl extends AsyncTask<Context, Void, Void>
    {
        private final ProgressDialog dialog = new ProgressDialog(activityname.this);

        protected void onPreExecute()
        {
            this.dialog.setMessage("Searching");
            this.dialog.show();
        }

        protected Void doInBackground(Context... params)
        {
            //Wait 10 seconds to see if we can get a location from either network or GPS, otherwise stop
            Long t = Calendar.getInstance().getTimeInMillis();
            while (!hasLocation && Calendar.getInstance().getTimeInMillis() - t < 15000) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            return null;
        }

        protected void onPostExecute(final Void unused)
        {
            if(this.dialog.isShowing())
            {
                this.dialog.dismiss();
            }

            if (currentLocation != null)
            {
                useLocation();
            }
            else
            {
                //Couldn't find location, do something like show an alert dialog
            }
        }
    }

接着是一个位置结果...

public LocationResult locationResult = new LocationResult()
    {
        @Override
        public void gotLocation(final Location location)
        {
            currentLocation = new Location(location);
            hasLocation = true;
        }
    };

此外,为了避免你像我一样长时间地抓狂,请记得在某人提前离开活动时停止locationcontrol任务,并停止位置更新以防止用户离开后GPS继续运行。在onStop()中,可以这样做:

locHelper.stopLocationUpdates();
locationControlTask.cancel(true);

Location Helper类中的stopLocationUpdates函数执行以下操作:

public void stopLocationUpdates()
{
locationManager.removeUpdates(locationListenerGps);
locationManager.removeUpdates(locationListenerNetwork);
}

祝你好运,我需要它...

(注:原文已是中文)

7
分享完整的可运行代码(例如在 GitHub 上)对你和我们都非常有帮助,这很棒! - ant

0

为什么不创建一个ProgressDialog框并在public void gotLocation(final Location location){}中dismiss它,而不是创建一个AsyncTask?您还可以向ProgressDialog框添加onCancelListener,并取消任何当前位置请求。


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