0

非ブロッキング FusedLocationProvider API をブロッキング API として使用しようとしています。これが私がやっていることです(大まかに):

  1. AsyncTask を開始する
  2. バックグラウンド スレッドで、PlayServices/FusedProvider API に接続します。これに使用できるタイムアウト付きのブロック方法があります。
  3. 上記の API を使用して、場所の更新をリクエストします。API は、デバイスを使用して位置を計算し、呼び出し元のスレッドで実行されるコールバックをトリガーします。
  4. を使用してコールバックを待ちますcountDownLatch.await(timeOut,timeUnit)
  5. Fused API がコールバックをトリガーし、場所を取得して を実行すると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