0

我正在尝试使用非阻塞 FusedLocationProvider API 作为阻塞 API。这是我正在做的(大致):

  1. 启动异步任务
  2. 在后台线程中,连接到 PlayServices/FusedProvider API。有一个可用的超时阻塞方法。
  3. 使用上述 API,请求位置更新。API 将使用设备计算位置并触发在调用者线程上执行的回调。
  4. 等待回调使用countDownLatch.await(timeOut,timeUnit)
  5. 当 Fused API 触发回调时,检索位置并执行 a countDownLatch.countdown(),这将释放闩锁。
  6. 返回位置。

我需要一种阻止方法,因为我可能需要在任何时间计算位置并使用它。上述方法有效,但我正在寻找更好的方法,因为 CountDownLatch 是从它阻塞的同一线程触发的。我不知道我这样做是否是在亵渎神灵/纯粹的愚蠢。

这是我的代码:

public class BlockingLocationProvider implements ConnectionCallbacks,
        OnConnectionFailedListener, LocationListener {

    private static final String TAG = "BlockingLocationProvider";

    private LocationInfo mLocInfoRow;
    private long mCallTime;
    private Double mDel;
    private GoogleApiClient mGoogleApiClient;
    private Location mLocation;
    private LocationRequest locationRequest;
    private boolean mLocationCalculated = false;
    private CountDownLatch latch;

    private Context context;

    private ConnectionResult gApiConnResult;

    @Override
    public void onConnectionFailed(ConnectionResult arg0) {
        Logger.error(TAG, "Google play services error while getting location.");
        mLocInfoRow = new LocationInfo().timestamp(System.currentTimeMillis())
                .status(LocStatus.ERR_G_PLAY).statusCode(arg0.getErrorCode());
        mLocation = null;
        mLocationCalculated = true;
        latch.countDown();
    }

    @Override
    public void onConnected(Bundle arg0) {
        locationRequest = new LocationRequest();
        locationRequest.setInterval(0).setNumUpdates(1)
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                .setExpirationDuration(2000);
        LocationServices.FusedLocationApi.requestLocationUpdates(
                mGoogleApiClient, locationRequest, this);
    }

    @Override
    public void onConnectionSuspended(int arg0) {
        Logger.warning(TAG, "Can't handle suspension.");
        mLocInfoRow = new LocationInfo().timestamp(System.currentTimeMillis())
                .status(LocStatus.ERR_UNKNOWN).statusCode(-200);
        latch.countDown();
    }

    @Override
    public void onLocationChanged(Location arg0) {
        mLocation = arg0;
        if (mLocation == null) {
            Logger.debug(TAG, "Fused provider returned null");
            mLocInfoRow = new LocationInfo()
                    .timestamp(System.currentTimeMillis())
                    .status(LocStatus.ERR_FUSED_NULL).statusCode(-3);
        } else {
            Logger.debug(TAG, "Got a location from fused provider.");
            mLocInfoRow = new LocationInfo().timestamp(mLocation.getTime())
                    .lat(mLocation.getLatitude()).lng(mLocation.getLongitude())
                    .travelSpeed(mLocation.getSpeed())
                    .altitude(mLocation.getAltitude())
                    .acc(mLocation.getAccuracy()).provider("fused")
                    .status("OK").statusCode(0);

        }
        mLocationCalculated = true;
        latch.countDown();
    }

    public LocationInfo getLocationInfo(Context ctx) {
        this.context = ctx;
        calcLocation();
        return mLocInfoRow;
    }

    public Location getLocation(Context ctx) {
        this.context = ctx;
        calcLocation();
        return mLocation;
    }

    // This should block
    public void calcLocation() {

        if (Thread.currentThread().getName().equals("main")) {
            throw new RuntimeException("Cannot run " + TAG + " on UI thread.");
        }

        latch = new CountDownLatch(1);

        /* To figure how long it takes to calc location */
        mCallTime = System.currentTimeMillis();

        Logger.verbose(TAG, "Checking play services.");
        // First check play services
        int playServicesAvailable = GooglePlayServicesUtil
                .isGooglePlayServicesAvailable(context);
        // whoopsie!
        if (playServicesAvailable != ConnectionResult.SUCCESS) {
            Logger.error(TAG,
                    "Google play services error while getting location.");
            mLocInfoRow = new LocationInfo()
                    .timestamp(System.currentTimeMillis())
                    .status(LocStatus.ERR_G_PLAY)
                    .statusCode(playServicesAvailable);
            mLocation = null;
            return;
        }

        Logger.verbose(TAG, "Checking GPS.");
        // Then check GPS enabled or not
        if (!isGpsEnabled(context)) {
            Logger.error(TAG,
                    "User has disabled GPS. Unable to provide location updates.");
            mLocInfoRow = new LocationInfo()
                    .timestamp(System.currentTimeMillis())
                    .status(LocStatus.ERR_NO_GPS).statusCode(-2);
            mLocation = null;
            return;
        }

        Logger.verbose(TAG, "Connecting to play services.");
        // Then connect to play services client
        mGoogleApiClient = new GoogleApiClient.Builder(context)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API).build();

        if (!(mGoogleApiClient.isConnected() || mGoogleApiClient.isConnecting())) {
            gApiConnResult = mGoogleApiClient.blockingConnect(5,
                    TimeUnit.SECONDS);

        }

        boolean timeout = false;

        if (gApiConnResult.isSuccess()) {
            try {
                Logger.warning(TAG, "Waiting latch on location request...");
                timeout = !latch.await(5, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (timeout) {
                Logger.warning(TAG, "Latch timeout!");
                mLocInfoRow = new LocationInfo()
                        .timestamp(System.currentTimeMillis())
                        .status(LocStatus.ERR_TIMEOUT).statusCode(-100);
            } else {
                Logger.debug(TAG, "Latch triggered!");
            }

        } else {
            Logger.warning(TAG, "gApi connect timeout!");
            mLocInfoRow = new LocationInfo()
                    .timestamp(System.currentTimeMillis())
                    .status(LocStatus.ERR_TIMEOUT).statusCode(-300);
        }

        mDel = (Double.valueOf(System.currentTimeMillis() - mCallTime) / 1000);
        Logger.debug(TAG, "Took " + mDel + " seconds for location update.");
        LocationServices.FusedLocationApi.removeLocationUpdates(
                mGoogleApiClient, this);
        mGoogleApiClient.disconnect();
    }

    private static boolean isGpsEnabled(final Context ctx) {
        LocationManager lm = (LocationManager) ctx
                .getSystemService(Context.LOCATION_SERVICE);
        return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    }

}

在这种情况下,除了锁定无限循环和标志之外,还有什么策略可以阻止吗?谢谢。

4

0 回答 0