이미 백그라운드 스레드가있는 서비스에 있다고 가정하십시오. 콜백이 동 기적으로 발생하도록 동일한 스레드에서 발리를 사용하여 요청을 할 수 있습니까?
여기에는 두 가지 이유가 있습니다.-먼저 다른 스레드가 필요하지 않으며 스레드를 생성하는 데 낭비가됩니다. -둘째, ServiceIntent에 있으면 콜백 전에 스레드 실행이 완료되므로 Volley의 응답이 없습니다. 내가 제어 할 수있는 runloop가있는 스레드가있는 자체 서비스를 만들 수 있다는 것을 알고 있지만이 기능을 발리에서 사용하는 것이 바람직합니다.
감사합니다!
답변
발리의 RequestFuture
수업에서 가능한 것처럼 보입니다 . 예를 들어 동기식 JSON HTTP GET 요청을 작성하려면 다음을 수행하십시오.
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(URL, new JSONObject(), future, future);
requestQueue.add(request);
try {
JSONObject response = future.get(); // this will block
} catch (InterruptedException e) {
// exception handling
} catch (ExecutionException e) {
// exception handling
}
답변
참고 @Matthews 답변은 다른 스레드에 있고 인터넷이 없을 때 발리 콜을 수행하면 오류 콜백이 기본 스레드에서 호출되지만 현재 스레드는 영원히 차단됩니다. 따라서 해당 스레드가 IntentService 인 경우 다른 메시지를 보낼 수 없으며 기본적으로 서비스가 종료됩니다.
get()
타임 아웃이있는 버전을 사용하십시오.future.get(30, TimeUnit.SECONDS)
오류를 잡아서 스레드를 종료하십시오.
@Mathews 답변과 일치 시키려면 :
try {
return future.get(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// exception handling
} catch (ExecutionException e) {
// exception handling
} catch (TimeoutException e) {
// exception handling
}
아래에서는 메소드로 래핑하고 다른 요청을 사용합니다.
/**
* Runs a blocking Volley request
*
* @param method get/put/post etc
* @param url endpoint
* @param errorListener handles errors
* @return the input stream result or exception: NOTE returns null once the onErrorResponse listener has been called
*/
public InputStream runInputStreamRequest(int method, String url, Response.ErrorListener errorListener) {
RequestFuture<InputStream> future = RequestFuture.newFuture();
InputStreamRequest request = new InputStreamRequest(method, url, future, errorListener);
getQueue().add(request);
try {
return future.get(REQUEST_TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Log.e("Retrieve cards api call interrupted.", e);
errorListener.onErrorResponse(new VolleyError(e));
} catch (ExecutionException e) {
Log.e("Retrieve cards api call failed.", e);
errorListener.onErrorResponse(new VolleyError(e));
} catch (TimeoutException e) {
Log.e("Retrieve cards api call timed out.", e);
errorListener.onErrorResponse(new VolleyError(e));
}
return null;
}
답변
선물을 사용하는 것이 좋을 수도 있지만, 어떤 이유로 든 원하지 않는 경우 동기화 된 차단 요리를 요리하는 대신을 사용해야합니다 java.util.concurrent.CountDownLatch
. 이렇게 작동합니다 ..
//I'm running this in an instrumentation test, in real life you'd ofc obtain the context differently...
final Context context = InstrumentationRegistry.getTargetContext();
final RequestQueue queue = Volley.newRequestQueue(context);
final CountDownLatch countDownLatch = new CountDownLatch(1);
final Object[] responseHolder = new Object[1];
final StringRequest stringRequest = new StringRequest(Request.Method.GET, "http://google.com", new Response.Listener<String>() {
@Override
public void onResponse(String response) {
responseHolder[0] = response;
countDownLatch.countDown();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
responseHolder[0] = error;
countDownLatch.countDown();
}
});
queue.add(stringRequest);
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (responseHolder[0] instanceof VolleyError) {
final VolleyError volleyError = (VolleyError) responseHolder[0];
//TODO: Handle error...
} else {
final String response = (String) responseHolder[0];
//TODO: Handle response...
}
사람들이 실제로이 작업을 시도하고 문제를 겪는 것처럼 보였기 때문에 실제로 사용중인 “실제”작업 샘플을 제공하기로 결정했습니다. 여기있어 https://github.com/timolehto/SynchronousVolleySample입니다
이제 솔루션이 작동하더라도 몇 가지 제한이 있습니다. 가장 중요한 것은 메인 UI 스레드에서 호출 할 수 없다는 것입니다. 발리는 백그라운드에서 요청을 실행하지만 기본적으로 발리는Looper
응용 프로그램 을 하여 응답을 발송합니다. 기본 UI 스레드가 응답을 대기하고 있지만 전달을 처리하기 전에 완료 Looper
대기 중이므로 교착 상태가 발생합니다 onCreate
. 당신이 정말 정말 대신 정적 도우미 방법이 당신이 수를하고 싶은 경우에, 당신의 자신의 인스턴스를 RequestQueue
통과 당신의 자신 ExecutorDelivery
에 연결을 Handler
사용하여 Looper
메인 UI 스레드에서 다른 스레드에 연결된다.
답변
@Blundells와 @Mathews 답변 모두에 대한 보완적인 관찰로, 어떤 전화도 어떤 것으로 전달 되는지 확실 하지 않습니다 . 하지만, 발리하여 메인 스레드.
소스
상기보고 갖는 RequestQueue
구현 가 보이는 RequestQueue
A를 사용 NetworkDispatcher
하여 요청을 실행하고이 ResponseDelivery
결과를 전달합니다 (는 ResponseDelivery
주입된다 NetworkDispatcher
). 이 ResponseDelivery
차례로 만들어집니다Handler
메인 쓰레드로부터 스폰 (곳 주위에서 112 라인RequestQueue
구현).
NetworkDispatcher
구현의 135 줄 어딘가에서 성공적인 결과가 ResponseDelivery
모든 오류 와 동일한 방식 으로 전달되는 것처럼 보입니다 . 다시; ㅏResponseDelivery
A의 기반Handler
메인 스레드에서 산란.
이론적 해석
요청이있는 유스 케이스의 경우 IntentService
Volley의 응답이있을 때까지 서비스 스레드가 차단되어야한다고 가정하는 것이 좋습니다 (결과를 처리 할 수있는 살아있는 런타임 범위를 보장하기 위해).
제안 된 솔루션
한 가지 방법은 a RequestQueue
가 생성 되는 기본 방식을 재정의하는 것입니다 . 여기서 대체 생성자가 대신 사용 되며 기본 스레드가 아닌 현재 스레드 ResponseDelivery
에서 생성되는 것을 생성합니다 . 그러나 이것의 의미는 조사하지 않았습니다.
답변
나는 그 효과를 달성하기 위해 자물쇠를 사용한다. 이제 누군가 내 의견이 올바른지 궁금해 하는가?
// as a field of the class where i wan't to do the synchronous `volley` call
Object mLock = new Object();
// need to have the error and success listeners notifyin
final boolean[] finished = {false};
Response.Listener<ArrayList<Integer>> responseListener = new Response.Listener<ArrayList<Integer>>() {
@Override
public void onResponse(ArrayList<Integer> response) {
synchronized (mLock) {
System.out.println();
finished[0] = true;
mLock.notify();
}
}
};
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
synchronized (mLock) {
System.out.println();
finished[0] = true;
System.out.println();
mLock.notify();
}
}
};
// after adding the Request to the volley queue
synchronized (mLock) {
try {
while(!finished[0]) {
mLock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
답변
Matthew의 대답에 무언가를 추가하고 싶습니다. 동안RequestFuture
당신이 그것을 만든 스레드에서 동기 호출을 만들 보일 수도, 그렇지 않습니다. 대신, 백그라운드 스레드에서 호출이 실행됩니다.
라이브러리를 거친 후에 내가 이해 한 것으로부터의 요청 RequestQueue
은 start()
메소드 로 전달됩니다 .
public void start() {
....
mCacheDispatcher = new CacheDispatcher(...);
mCacheDispatcher.start();
....
NetworkDispatcher networkDispatcher = new NetworkDispatcher(...);
networkDispatcher.start();
....
}
이제 모두 CacheDispatcher
와NetworkDispatcher
클래스 스레드를 확장합니다. 따라서 효과적으로 요청 큐를 대기시키기 위해 새로운 작업자 스레드가 생성되고 응답은 내부적으로 구현 된 성공 및 오류 리스너로 리턴됩니다.RequestFuture
.
두 번째 목적은 달성되었지만 실행하는 스레드에 관계없이 항상 새 스레드가 생성되기 때문에 첫 번째 목적은 아닙니다 RequestFuture
.
즉, 기본 Volley 라이브러리 에서는 진정한 동기 요청이 불가능합니다. 내가 틀렸다면 나를 바로 잡으십시오.
답변
발리와 동기화 요청을 할 수는 있지만 다른 스레드에서 메소드를 호출해야합니다. 그렇지 않으면 실행중인 앱이 차단됩니다.
public String syncCall(){
String URL = "http://192.168.1.35:8092/rest";
String response = new String();
RequestQueue requestQueue = Volley.newRequestQueue(this.getContext());
RequestFuture<JSONObject> future = RequestFuture.newFuture();
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, URL, new JSONObject(), future, future);
requestQueue.add(request);
try {
response = future.get().toString();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return response;
}
그런 다음 thread에서 메소드를 호출 할 수 있습니다.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String response = syncCall();
}
});
thread.start();