I'm trying to use setUseSynchronousMode on loopj to wait for results of http call before continuing in one case. I tried:
AsyncHttpResponseHandler responseHandler = new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
Log.d("TEST", "Got results");
}
};
AsyncHttpClient client = new AsyncHttpClient();
responseHandler.setUseSynchronousMode(true);
client.get("http://www.google.com", responseHandler);
Log.d("TEST", "Don't want to get here until after getting results");
But the result is:
07-11 19:48:05.631 D/TEST﹕ Don't want to get here until after getting results
07-11 19:48:05.814 D/TEST﹕ Got results
Am I misunderstanding what setUseSynchronousMode should do?
You should have used SyncHttpClient instead of AsyncHttpClient. setUseSynchronousMode doesn't have the desired effect for AsyncHttpClient.
To have synchronous version of AsyncHttpClient with an ability to cancel it, I do everything on the main thread. Previously I was running it in AsyncTask and as soon as AsyncHttpClient.post() was called, the AsyncTask would finish and I was unable to keep track the AsyncHttpClient instance.
SyncHttpClient didn't allow me to cancel the uploading so I knew I had to use AsyncHttpClient and make appropriate changes.
Following is my class to upload a file which uses AsyncHttpClient and allows cancellation:
public class AsyncUploader {
private String mTitle;
private String mPath;
private Callback mCallback;
public void AsyncUploader(String title, String filePath, MyCallback callback) {
mTitle = title;
mPath = filePath;
mCallback = callback;
}
public void startTransfer() {
mClient = new AsyncHttpClient();
RequestParams params = new RequestParams();
File file = new File(mPath);
try {
params.put("title", mTitle);
params.put("video", file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
mClient.setTimeout(50000);
mClient.post(mContext, mUrl, params, new ResponseHandlerInterface() {
#Override
public void sendResponseMessage(HttpResponse response) throws IOException {
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
// TODO convert instream to JSONObject and do whatever you need to
mCallback.uploadComplete();
}
}
#Override
public void sendProgressMessage(int bytesWritten, int bytesTotal) {
mCallback.progressUpdate(bytesWritten, bytesTotal);
}
#Override
public void sendFailureMessage(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
mCallback.failedWithError(error.getMessage());
}
});
}
/**
* Cancel upload by calling this method
*/
public void cancel() {
mClient.cancelAllRequests(true);
}
}
This is how you can run it:
AsyncUploader uploader = new AsyncUploader(myTitle, myFilePath, myCallback);
uploader.startTransfer();
/* Transfer started */
/* Upon completion, myCallback.uploadComplete() will be called */
To cancel the upload, just call cancel() like:
uploader.cancel();
Related
SOLVED:
I have gone ahead and used the recomendation of Retrofit library and it has reduced my code allot. Thank you for the help.
I have an android application, on the page I have code displayed below when someone clicks on a switch it will use PUT to send the data as a JSON to the server. However im not sure how I can reduce this code in a way that I can use it again. I dont want to have to keep copy and pasting the same code and changing the requestMethod or the type of bytes im writing.
This is the code in my oncreate, when the switch is turned on it will do what is in Figure B. I want to make it DELETE when the switch is off without having to copy the same code from figure B and changing it and then executing it.:
test.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
boolean on = ((Switch) v).isChecked();
if(on)
{
//Do something when switch is on/checked
new RetrieveFeedTask().execute();
new newtask().execute();
}
else
{
//Do something when switch is off/unchecked
}
}
});
Figure B
class RetrieveFeedTask extends AsyncTask<String, String, String> {
private Exception exception;
protected String doInBackground(String... urls) {
URL url = null;
try {
String strUrl = "http://192.168.0.104:5053/TEST";
url = new URL(strUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json; charset");
connection.setDoInput(true);
connection.setDoOutput(true);
OutputStream stream = connection.getOutputStream();
DataOutputStream writer = new DataOutputStream(stream);
connection.connect();
//Log.d("WARN", "TEST");
// The LogCat prints out data like:
// ID:test,Email:test#gmail.com,Pwd:test
String suuid = UUID.randomUUID().toString();
writer.writeBytes("{\"id\" : \""+suuid+ "\", \"sensorType\" : \"sound\", \"threshold\" : \"50\", \"operator\" : \">\", \"actuator\" : \"1\", \"actuatorAction\" : \"4\"}");
writer.flush();
writer.close();
InputStream response = connection.getInputStream();
StringBuilder sb = new StringBuilder();
BufferedReader bf = new BufferedReader(new InputStreamReader(response, "utf-8"));
String responseLine = bf.readLine();
while(responseLine != null) {
sb.append(responseLine);
responseLine = bf.readLine();
}
stream.close();
System.out.println("executed");
connection.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
return "fff";
}
protected String onPostExecute() {
// TODO: check this.exception
// TODO: do something with the feed
return "ASDASD";
}
}
If any more information is needed on making this clearer please let me know.
Use Android Asynchronous Http Client
Sample Code:
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("key", "value");
params.put("more", "data");
client.post("https://www.google.com", params, new AsyncHttpResponseHandler() {
#Override
public void onStart() {
// called before request is started
}
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
// called when response HTTP status is "200 OK"
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
}
#Override
public void onRetry(int retryNo) {
// called when request is retried
}
});
Gradle:
dependencies {
compile 'com.loopj.android:android-async-http:1.4.9'
}
Android-Volley(deprecated):
https://github.com/mcxiaoke/android-volley
or
Retrofit:
Type-safe HTTP client
Retrofit is definitely the better alternative to volley in terms of ease of use, performance,Catching, extensibility, Multipart uploads and other things. It is a type-safe REST client
(http://square.github.io/retrofit)
You can use a very lightweight Ion library for this. Usage examples are on Github: https://github.com/koush/ion
First of all, i would recommend using a networking library (Retrofit, Volly, etc...) since dealing with the http connection directly is a bit more complicated and prone to errors which the libraries have probably addressed already.
But if you still wish to use an http connection directly, You could just make an httpConnection Task that extends async task and gets the necessary params on creation.
for example -
public class HttpConnection extends AsyncTask<String, String, String> {
private final String mBytes;
private final String mMethod;
public HttpConnection(String method, String bytes) {
mMethod = method;
mBytes = bytes;
}
#Override
protected String doInBackground(String... strings) {
// ... open connection
writer.writeBytes(mBytes);
return null;
}
Obviously, you can change this up to use whatever params you want/need
Trying to upload a file with params using loopj.
im trying to get file from Request.Files and params from Request.Form["create"]
but it is not uploading to the server.
Android Post method
try {
String createTeamURL = "http://url";
RequestParams params = new RequestParams();
params.put("file", new File(pathoffile));
params.add("create", regString);
AsyncHttpClient client = new AsyncHttpClient();
client.post(createTeamURL, params, new AsyncHttpResponseHandler() {
#Override
public void onStart() {
// called before request is started
}
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] response) {
// called when response HTTP status is "200 OK"
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
// called when response HTTP status is "4XX" (eg. 401, 403, 404)
}
#Override
public void onRetry(int retryNo) {
// called when request is retried
}
});
} catch (Exception e) {
Log.e("createTeamPreStep", e.getMessage());
}
My Web Api c# method
[HttpPost]
public async Task<string> CreateUHS()
{
var resultString = "";
foreach(HttpPostedFileBase s in Request.Files)
{
var a=s;
}
String sdf = Request.Form["create"];
}
You need to use put for string args.
please find the below both server and client methods.
and one more thing im really worried about your naming variable. its bad. please change it. Happy coding.
String createTeamURL = "http://url";
RequestParams params = new RequestParams();
params.put("file", new File(pathoffile));
params.put("create", regString);
Server (Web api)
[HttpPost]
public async Task<string> CreateUHS()
{
var file=Request.Files[0];
String otherArg = Request.Form["create"];
}
I'd like to do some work in AsyncTask including some server requests for downloading small files. When download is done continue logic inside AsyncTask and when all stuff is done I get the result in activity. Everything is working good but my AsyncTask is not waiting for callback method:
public class AsyncOperation extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... params) {
String linkUrl = params[0];
functionDoStuff(linkUrl);
return "Executed";
}
public void functionDoStuff(String urlLink) {
... code ...
String str = getFile(urlLink);
!!! is not waiting for result !!!
... use 'str' ...
}
private String getFile(String urlLink) {
String savedFileDestination = null;
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url(urlLink")
.build();
client.newCall(request).enqueue(new com.squareup.okhttp.Callback() {
#Override
public void onFailure(Request request, IOException e) {
//something goes wrong
}
#Override
public void onResponse(com.squareup.okhttp.Response response) throws IOException {
//get stream
InputStream inputStream = response.body().byteStream();
//this method save file and return file path
savedFileDestination = saveFileMethod(inputStream);
}
});
return savedFileDestination;
}
}
How can I wait for this callback to continue logic in functiobDoStuff() ?
put all the stuff inside onResponse method.Because onResponse method work asyncronusly
#Selvin is right, I have to make my request synchronous and my 'waiting problem' is gone!
The only change is into getFile() method, it should be smth like:
private void getFile(String urlLink) {
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url(urlLink)
.build();
try {
Response response = client.newCall(request).execute();
if (response!=null){
InputStream inputStream = response.body().byteStream();
saveFile(inputStream);
}
} catch (IOException e) {
e.printStackTrace();
}
}
I have a Android IntentService that connects to a web service to download some jobs in json format. These are then parsed and put into a SQLite3 DB.
The following code (with the sensitive bits added back in) worked on the emulator but never worked on the actual device.
public class FetchJobsService extends IntentService {
private final static String LOG_TAG = "FetchJobsService";
public FetchJobsService() {
super("FetchJobsService");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i(LOG_TAG, "Fetch Jobs Service Started");
AsyncHttpClient client = new AsyncHttpClient();
Map<String, Object> params = new HashMap<String, Object>();
// Add params to send in the request!
JSONObject jsonData = new JSONObject(params);
RequestParams requestParams = new RequestParams();
requestParams.put(Constants.DATA, jsonData.toString());
client.post(Constants.FETCH_JOBS, requestParams,
new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int i, Header[] headers, byte[] bytes) {
try {
JSONObject returnObj = new JSONObject(new String(bytes));
Log.i(LOG_TAG, "Success! Jobs downloaded");
JSONObject dataObj = returnObj.getJSONObject(Constants.DATA);
JSONArray jobs = dataObj.getJSONArray(Constants.JOBS);
// Do something with the data downloaded!
} catch (JSONException e) {
Log.e(LOG_TAG, "Failure! Jobs not successfully downloaded", e);
}
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] bytes,
Throwable throwable) {
Log.i(LOG_TAG, "Failure! Status Code: " + statusCode);
}
}
);
Log.i(LOG_TAG, "Fetch Jobs Service Finished");
}
}
It complained about posting to a Dead Thread. After some reading around, I added a Looper to keep the IntentService around until the data had been downloaded and parsed, as such:
public class FetchJobsService extends IntentService {
private final static String LOG_TAG = "FetchJobsService";
public FetchJobsService() {
super("FetchJobsService");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i(LOG_TAG, "Fetch Jobs Service Started");
if (Looper.myLooper() == null) {
Looper.prepare();
}
AsyncHttpClient client = new AsyncHttpClient();
Map<String, Object> params = new HashMap<String, Object>();
// Add params to send in the request!
JSONObject jsonData = new JSONObject(params);
RequestParams requestParams = new RequestParams();
requestParams.put(Constants.DATA, jsonData.toString());
client.post(Constants.FETCH_JOBS, requestParams,
new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int i, Header[] headers, byte[] bytes) {
try {
JSONObject returnObj = new JSONObject(new String(bytes));
Log.i(LOG_TAG, "Success! Jobs downloaded");
JSONObject dataObj = returnObj.getJSONObject(Constants.DATA);
JSONArray jobs = dataObj.getJSONArray(Constants.JOBS);
// Do something with the data downloaded!
} catch (JSONException e) {
Log.e(LOG_TAG, "Failure! Jobs not successfully downloaded", e);
} finally {
if (Looper.myLooper() != null) {
Looper.myLooper().quit();
}
}
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] bytes,
Throwable throwable) {
Log.i(LOG_TAG, "Failure! Status Code: " + statusCode);
if (Looper.myLooper() != null) {
Looper.myLooper().quit();
}
}
}
);
Looper.loop();
Log.i(LOG_TAG, "Fetch Jobs Service Finished");
}
}
The jobs are now downloaded successfully however the call to quit() or quitSafely() causes a crash Fatal signal 11 (SIGSEGV) at 0x0000000c (code=1).
If I remove the calls to quit() then there is no crash but obviously the service is then not going to stop and release resources until the application quits.
What am I doing wrong?
An IntentService is meant to handle its own lifecycle. You just override the onHandleIntent callback and do what you need to do - once this method exits the IntentService will shut itself down.
The IntentService is already running in a seperate thread, so it doesn't make sense to make the request asynchronously. You should execute your network call synchronously within the onHandleIntent callback. There are some examples in this article.
You could try something along these lines:
HttpEntity requestEntity = new UrlEncodedFormEntity(params);
//set up post request
final HttpPost post = new HttpPost(url);
post.addHeader(requestEntity.getContentType());
post.setEntity(requestEntity);
//get http client
DefaultHttpClient mHttpClient = new DefaultHttpClient();
final HttpParams httpParams = mHttpClient.getParams();
HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT);
HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT);
ConnManagerParams.setTimeout(httpParams, TIMEOUT);
// get response -- this line will block until it is complete -- which is completely fine in an IntentService!
HttpResponse response = getHttpClient().execute(post);
int statusCode = response.getStatusLine().getStatusCode();
String responseEntity = EntityUtils.toString(response.getEntity());
if (statusCode != HttpStatus.SC_OK) {
// something bad happened, request failed!
} else{
// response was good, and "responseEntity" can be used for whatever you need
}
I currently am using AsynchttpClient to make a POST request to a web server. When I debug, I can clearly see the JSON that is being returned. However, when I set the variable to a static method in the ECUser class, I always get that the ECUser.getCurrentUser() is null which shouldn't be correct. Since all the methods in ECUser is static, I don't see what my problem is.
The same thing happens if I try to assign a jsonobject to the responseBody from the asynchttpclient call. After the anonymous class terminates, the jsonobject is always null for some reason.
private void attemptLogin(){
RequestParams params = new RequestParams();
params.put("email", userEmail.getText().toString());
params.put("password", userPass.getText().toString());
ECApiManager.post(Constants.loginAPI, params, new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject responseBody) {
//called when response code 200
try{
ECUser.setCurrentUser(new ECUser(responseBody));
Log.d("login", responseBody.toString());
}catch(JSONException e){
e.printStackTrace();
}
}
});
if (ECUser.getCurrentUser() == null) {
((OnLoginListener) getActivity()).loginSuccessful(false);
}
This is my ECUser Class.
public class ECUser {
private static ECUser currentUser;
private static String userToken;
private String firstName;
private String lastName;
private static String userID;
private JSONObject jObject;
private boolean loginSuccess;
public ECUser(JSONObject data) throws JSONException {
this.jObject = data;
try {
this.loginSuccess = Boolean.parseBoolean(this.jObject.getString("success"));
if (this.loginSuccess) {
this.userToken = this.jObject.getString("token");
this.firstName = this.jObject.getJSONObject("user").getString("firstname");
this.lastName = this.jObject.getJSONObject("user").getString("lastname");
this.userID = this.jObject.getJSONObject("user").getString("id");
}
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* Method that refreshes the state of the current user by calling the API again.
*/
public static void refreshCurrentUser() {
RequestParams params = new RequestParams();
params.put("token", userToken);
//Gotta put in user ID too.
params.put("user_id", userID);
// TODO: This should only call https://edu.chat/api/user, #JACOB
ECApiManager.get(Constants.refreshUserAPI, params, new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject responseBody) {
try {
ECUser.setCurrentUser(new ECUser(responseBody));
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
// Static
public static ECUser getCurrentUser() {
return ECUser.currentUser;
}
public static void setCurrentUser(ECUser user) {ECUser.currentUser = user;}
public static String getUserToken() {
return ECUser.userToken;
}
// Dynamic
public String getLastName() {
return this.lastName;
}
public String getFirstName() {
return this.firstName;
}
public boolean getLoginSuccessful() {
return this.loginSuccess;
}
}
You treat the login attempt in ECApiManager#post as a synchronous method, e.g. one that returns once the attempt is completed. And so, right after calling post you check the current user.
But, clearly from the name of the http client (AsynchttpClient) you can understand that the post is an asynchronous call, which means that the HTTP request will be performed in the background, and once completed successfully, it will call your JsonHttpResponseHandler#onSuccess method. This means that although the ECApiManager#post will return immediately, the current user is not set yet.
When programming in asynchronous mode, you need to handle events as they occur, and not just call everything in sequence. If you do want to make this synchronous, you will need either to use a synchronous http client (such as HttpClient or HttpUrlConnection), or add a Semaphore to wait for your HTTP request to be completed.
For example:
private void attemptLogin(){
Semaphore sema = Semaphore(1);
sema.acquire();
// ....
ECApiManager.post(Constants.loginAPI, params, new JsonHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject responseBody) {
//called when response code 200
try{
ECUser.setCurrentUser(new ECUser(responseBody));
Log.d("login", responseBody.toString());
}catch(JSONException e){
e.printStackTrace();
}
finally {
sema.release();
}
}
});
sema.acquire();
if (ECUser.getCurrentUser() == null) {
((OnLoginListener) getActivity()).loginSuccessful(false);
}
Note, that if you use a semaphore you will need to release it upon failure as well.