我正在尝试使用非阻塞 FusedLocationProvider API 作为阻塞 API。这是我正在做的(大致):
- 启动异步任务
- 在后台线程中,连接到 PlayServices/FusedProvider API。有一个可用的超时阻塞方法。
- 使用上述 API,请求位置更新。API 将使用设备计算位置并触发在调用者线程上执行的回调。
- 等待回调使用
countDownLatch.await(timeOut,timeUnit)
- 当 Fused API 触发回调时,检索位置并执行 a
countDownLatch.countdown()
,这将释放闩锁。 - 返回位置。
我需要一种阻止方法,因为我可能需要在任何时间计算位置并使用它。上述方法有效,但我正在寻找更好的方法,因为 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);
}
}
在这种情况下,除了锁定无限循环和标志之外,还有什么策略可以阻止吗?谢谢。