/**
* Copyright 2010-present Facebook.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.facebook;
import android.annotation.TargetApi;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Defines an AsyncTask suitable for executing a Request in the background. May be subclassed
* by applications having unique threading model needs.
*/
public class RequestAsyncTask extends AsyncTask<Void, Void, List<Response>> {
private static final String TAG = RequestAsyncTask.class.getCanonicalName();
private static Method executeOnExecutorMethod;
private final HttpURLConnection connection;
private final RequestBatch requests;
private Exception exception;
static {
for (Method method : AsyncTask.class.getMethods()) {
if ("executeOnExecutor".equals(method.getName())) {
Class<?>[] parameters = method.getParameterTypes();
if ((parameters.length == 2) && (parameters[0] == Executor.class) && parameters[1].isArray()) {
executeOnExecutorMethod = method;
break;
}
}
}
}
/**
* Constructor. Serialization of the requests will be done in the background, so any serialization-
* related errors will be returned via the Response.getException() method.
*
* @param requests the requests to execute
*/
public RequestAsyncTask(Request... requests) {
this(null, new RequestBatch(requests));
}
/**
* Constructor. Serialization of the requests will be done in the background, so any serialization-
* related errors will be returned via the Response.getException() method.
*
* @param requests the requests to execute
*/
public RequestAsyncTask(Collection<Request> requests) {
this(null, new RequestBatch(requests));
}
/**
* Constructor. Serialization of the requests will be done in the background, so any serialization-
* related errors will be returned via the Response.getException() method.
*
* @param requests the requests to execute
*/
public RequestAsyncTask(RequestBatch requests) {
this(null, requests);
}
/**
* Constructor that allows specification of an HTTP connection to use for executing
* the requests. No validation is done that the contents of the connection actually
* reflect the serialized requests, so it is the caller's responsibility to ensure
* that it will correctly generate the desired responses.
*
* @param connection the HTTP connection to use to execute the requests
* @param requests the requests to execute
*/
public RequestAsyncTask(HttpURLConnection connection, Request... requests) {
this(connection, new RequestBatch(requests));
}
/**
* Constructor that allows specification of an HTTP connection to use for executing
* the requests. No validation is done that the contents of the connection actually
* reflect the serialized requests, so it is the caller's responsibility to ensure
* that it will correctly generate the desired responses.
*
* @param connection the HTTP connection to use to execute the requests
* @param requests the requests to execute
*/
public RequestAsyncTask(HttpURLConnection connection, Collection<Request> requests) {
this(connection, new RequestBatch(requests));
}
/**
* Constructor that allows specification of an HTTP connection to use for executing
* the requests. No validation is done that the contents of the connection actually
* reflect the serialized requests, so it is the caller's responsibility to ensure
* that it will correctly generate the desired responses.
*
* @param connection the HTTP connection to use to execute the requests
* @param requests the requests to execute
*/
public RequestAsyncTask(HttpURLConnection connection, RequestBatch requests) {
this.requests = requests;
this.connection = connection;
}
protected final Exception getException() {
return exception;
}
protected final RequestBatch getRequests() {
return requests;
}
@Override
public String toString() {
return new StringBuilder().append("{RequestAsyncTask: ").append(" connection: ").append(connection)
.append(", requests: ").append(requests).append("}").toString();
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if (requests.getCallbackHandler() == null) {
// We want any callbacks to go to a handler on this thread unless a handler has already been specified.
requests.setCallbackHandler(new Handler());
}
}
@Override
protected void onPostExecute(List<Response> result) {
super.onPostExecute(result);
if (exception != null) {
Log.d(TAG, String.format("onPostExecute: exception encountered during request: %s", exception.getMessage()));
}
}
@Override
protected List<Response> doInBackground(Void... params) {
try {
if (connection == null) {
return requests.executeAndWait();
} else {
return Request.executeConnectionAndWait(connection, requests);
}
} catch (Exception e) {
exception = e;
return null;
}
}
RequestAsyncTask executeOnSettingsExecutor() {
if (executeOnExecutorMethod != null) {
try {
executeOnExecutorMethod.invoke(this, Settings.getExecutor(), null);
} catch (InvocationTargetException e) {
// fall-through
} catch (IllegalAccessException e) {
// fall-through
}
} else {
this.execute();
}
return this;
}
}