在Android活动中,我发送由用户按下UI上的按钮驱动的http请求。 我不希望同时运行多个请求(OutlookClient崩溃)。
我的问题是:是否有可能因为回调而导致竞争条件,结果是在发送新请求之前写入相同的布尔值(使用runOnUiTread)?
由于
// Should this be either "volatile" or atomic ??
private boolean isThereAPendingRequest = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
genericClient = clientInitializer.create(this);
// ...
isThereAPendingRequest = true; // still have to login
Futures.addCallback(genericClient.logon(this, scopes), new FutureCallback<Boolean>() {
@Override
public void onSuccess(Boolean result) {
// ...
isThereAPendingRequest = false;
}
@Override
public void onFailure(@NonNull Throwable t) {
// ...
isThereAPendingRequest = false;
}
});
// ...
}
// ...
public void getBookings(View view){
if(isThereAPendingRequest){
Toast.makeText(getApplicationContext(), "There's already a pending request. Try in a few seconds.", Toast.LENGTH_LONG).show();
return;
}
isThereAPendingRequest = true;
Futures.addCallback( genericClient.getCalendarEvents(), new FutureCallback<List<List>>(){
@Override
public void onSuccess(final List<List> resultCalendars) {
Log.d("APP", "Success. Result: "+resultCalendars);
runOnUiThread(new Runnable() {
@Override
public void run() {
// ..
isThereAPendingRequest = false;
}
}
}
// ..
}
public void sendBooking(View view){
if(isThereAPendingRequest){
Toast.makeText(getApplicationContext(), "There's already a pending request. Try in a few seconds.", Toast.LENGTH_LONG).show();
return;
}
isThereAPendingRequest = true;
Futures.addCallback( genericClient.sendBooking( booker, title), new FutureCallback<List<String>>(){
@Override
public void onSuccess(final List<String> resultBooking) {
Log.d("APP", "Success. Result: "+resultBooking);
runOnUiThread(new Runnable() {
@Override
public void run() {
// ...
isThereAPendingRequest = false;
}
});
}
@Override
public void onFailure(Throwable t) {
Log.e( "APP", "Delete error. Cause: "+t.getLocalizedMessage() );
// ...
Toast.makeText(getApplicationContext(), "Fail!", Toast.LENGTH_LONG).show();
isThereAPendingRequest = false;
}
});
}catch(Exception ex){
// logger
isThereAPendingRequest = false;
}
}
更新:这是Futures中调用的函数之一..
public ListenableFuture<List<List>> getCalendarEvents(){
// logger
final SettableFuture<List<List>> future = SettableFuture.create();
DateTime now = new DateTime(DateTimeZone.UTC);
DateTime workDayEnd = new DateTime( now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 23, 59, 0 );
Futures.addCallback(
mClient .getMe()
.getCalendarView()
.addParameter("startDateTime", now)
.addParameter("endDateTime", workDayEnd)
.read(),
new FutureCallback<OrcList<Event>>() {
@Override
public void onSuccess(final OrcList<Event> result) {
// ...
future.set(myList);
}
@Override
public void onFailure(@NonNull Throwable t) {
// ...
future.setException(t);
}
}
);
return future;
}
答案 0 :(得分:1)
如果始终在UI线程上调用getBookings
和setBookings
,那么你应该没问题。您知道,当isThereAPendingRequest
设置为false时,请求必须已经完成,因此您可以放心使用。顺便说一下,Futures.addCallback
有一个替代签名,允许您明确传入Executor
,因此如果您使用它,则不需要调用runOnUiThread
来减少某些代码嵌套。
但是,如果您打算同时调用这些方法,我会看到至少一个需要锁定以阻止的竞争条件。如果您有兴趣,请详细了解。
编辑完整性:
问题表明您的目标是防止两个请求同时运行。有两种情况可能发生:
isThereAPendingRequest==false
,但实际上有待处理的请求。到目前为止,您的代码是安全的,因为您只在请求完成后将其设置为false。你甚至不需要volatile
。getBookings
和/或setBookings
。如果他们同时达到if(isThereAPendingRequest)
会怎样?他们可以同时(并且正确地)看到它是假的,将其设置为true,然后两者都独立发送请求并导致您崩溃。您不必担心(1),并且(2)不应该是一个问题,只要您始终在同一个线程上调用这些方法即可。