Crash when calling Looper.quit() - android

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
}

Related

Separate Class for OkHttp Requests

I use OkHttp for requests to my raspberry. I am thinking about putting the requests in a separate class.
Currently I have one method to send requests. The code is as follows:
private void sendRequest(String url, JSONObject json) {
Log.d(TAG, "sendRequest: Das Json: " + json);
// Authentication for the request to raspberry
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic("username", "password");
return response.request().newBuilder()
.header("Authorization", credential)
.build();
}
});
// Sending out the request to the raspberry
OkHttpClient okHttpClient = client.build();
RequestBody body = RequestBody.create(null, new byte[]{});
if( json != null) {
body = RequestBody.create(MediaType.parse(
"application/json"),
json.toString()
);
}
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.d(LOG, "Big Fail");
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try {
ResponseBody responseBody = response.body();
if( !response.isSuccessful() ) {
Log.d(TAG, "onResponse: We are in !response.successful()");
throw new IOException("Response not successful: " + response );
}
Log.d(LOG, "onResponse: Response is: " + responseBody.string());
} catch (Exception e) {
e.printStackTrace();
Log.d(LOG, "onResponse: failed!" + e);
}
}
});
}
Here is an example how the sendRequest() function is called:
private void makePremixCall(Premix premix) {
JSONArray jsonArray = new JSONArray();
ArrayList<Premixable> usedPremixables = premix.getUsedPremixables();
for(Premixable usedPremixable: usedPremixables) {
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("Silo", usedPremixable.getmSilo());
jsonObject.put("Gramm", usedPremixable.getmKgPerCow() * mFeeding.getmNumberOfCows());
jsonArray.put(jsonObject);
} catch (JSONException e) {
e.printStackTrace();
}
}
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("Components", jsonArray);
sendRequest("http://192.168.178.49:5000/evaluatePost", jsonObject);
} catch (JSONException e) {
e.printStackTrace();
Log.d(TAG, "makePremixCall: " + e);
}
}
My problem with this: I would like to have a separate class, which offers the function makePremix(Premix premix) and other functions that I need.
The only solution that comes to my mind is implementing the requests synchronously in the separate class and call that separate class in an AsyncTask in the class I am working in.
Do I oversee something? Is there a way to create a separate class and still use the OkHttp enqueue method?
You could extract makePremix(Premix premix) in a separate class and make sendRequest() public (or maybe package-private depending on your use case).
public void sendRequest(String url, JSONObject json)
However since sendRequest is generic and can be used by any other makeAnotherCall() in some other class you would need to get back result of every requests. Hence you can extract the Callback out of sendRequest()
public void sendRequest(String url, JSONObject json, Callback callback)
Now your sendRequest will look like
private void sendRequest(String url, JSONObject json) {
Log.d(TAG, "sendRequest: Das Json: " + json);
// Authentication for the request to raspberry
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic("username", "password");
return response.request().newBuilder()
.header("Authorization", credential)
.build();
}
});
// Sending out the request to the raspberry
OkHttpClient okHttpClient = client.build();
RequestBody body = RequestBody.create(null, new byte[]{});
if( json != null) {
body = RequestBody.create(MediaType.parse(
"application/json"),
json.toString()
);
}
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
okHttpClient.newCall(request).enqueue(callback);
}
Hope it makes sense!
Also as a side note, see that you are creating a new OkHttp Client every time you call sendRequest. You could probably optimise memory here by caching the client and reusing it.

How to send JSON data to a server in android?

With all the old methods been deprecated such as http post , response ,http client, string entity etc, i want to know how can i post json data to a server in android in 2017. My app is supposed to register or send JSON data such as email,contact number and password to a server using POST method and in turn server will give JSON response such as status , message and an array named data. Data is an array of only 2 objects (ie token and email). Please Help.
I think you need to try Loopj library for sending Json Data
you can try this link
and also it is quite easy to undestand
You can try another link
try{
AsyncHttpClient client = new AsyncHttpClient();
JSONObject obj = new JSONObject();
obj.put("email",email);
obj.put("contact_number",contact_number);
obj.put("password",password);
entity = new StringEntity(obj.toString());
client.post(getApplicationContext(), "Your_URL", entity, "application/json", new TextHttpResponseHandler() {
#Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
Log.d("LoginActivity","Failed");
Log.d("LoginActivity","body " + responseString);
}
#Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
Log.d("LoginActivity","data " + responseString);
try {
JSONObject respObj = new JSONObject(responseString);
String data = respObj.toString();
Log.d("LoginActivity","Data : " + data);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}catch (Exception ex){
Log.d("LoginActivity","Getting Exception "+ex.toString());
}
Try like this
private void registerUser(){
final String username = editTextUsername.getText().toString().trim();
final String password = editTextPassword.getText().toString().trim();
final String email = editTextEmail.getText().toString().trim();
StringRequest stringRequest = new StringRequest(Request.Method.POST, REGISTER_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(MainActivity.this,response,Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MainActivity.this,error.toString(),Toast.LENGTH_LONG).show();
}
}){
#Override
protected Map<String,String> getParams(){
Map<String,String> params = new HashMap<String, String>();
params.put(KEY_USERNAME,username);
params.put(KEY_PASSWORD,password);
params.put(KEY_EMAIL, email);
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}

Loopj - Uploading Files with RequestParams c# .net

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"];
}

Make a call synchronously with loopj Android Asynchronous Http Client

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();

POSTing JSON/XML using android-async-http (loopj)

I am using android-async-http and really liking it. I've run into a problem with POSTing data. I have to post data to the API in the following format: -
<request>
<notes>Test api support</notes>
<hours>3</hours>
<project_id type="integer">3</project_id>
<task_id type="integer">14</task_id>
<spent_at type="date">Tue, 17 Oct 2006</spent_at>
</request>
As per the documentation, I tried doing it using RequestParams, but it is failing. Is this any other way to do it? I can POST equivalent JSON too. Any ideas?
Loopj POST examples - extended from their Twitter example:
private static AsyncHttpClient client = new AsyncHttpClient();
To post normally via RequestParams:
RequestParams params = new RequestParams();
params.put("notes", "Test api support");
client.post(restApiUrl, params, responseHandler);
To post JSON:
JSONObject jsonParams = new JSONObject();
jsonParams.put("notes", "Test api support");
StringEntity entity = new StringEntity(jsonParams.toString());
client.post(context, restApiUrl, entity, "application/json",
responseHandler);
#Timothy answer did not work for me.
I defined the Content-Type of the StringEntity to make it work:
JSONObject jsonParams = new JSONObject();
jsonParams.put("notes", "Test api support");
StringEntity entity = new StringEntity(jsonParams.toString());
entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
client.post(context, restApiUrl, entity, "application/json", responseHandler);
Good Luck :)
a better way to post json
RequestParams params = new RequestParams();
params.put("id", propertyID);
params.put("lt", newPoint.latitude);
params.put("lg", newPoint.longitude);
params.setUseJsonStreamer(true);
ScaanRestClient restClient = new ScaanRestClient(getApplicationContext());
restClient.post("/api-builtin/properties/v1.0/edit/location/", params, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
}
});
To post XML
protected void makePost() {
AsyncHttpClient client = new AsyncHttpClient();
Context context = this.getApplicationContext();
String url = URL_String;
String xml = XML-String;
HttpEntity entity;
try {
entity = new StringEntity(xml, "UTF-8");
} catch (IllegalArgumentException e) {
Log.d("HTTP", "StringEntity: IllegalArgumentException");
return;
} catch (UnsupportedEncodingException e) {
Log.d("HTTP", "StringEntity: UnsupportedEncodingException");
return;
}
String contentType = "string/xml;UTF-8";
Log.d("HTTP", "Post...");
client.post( context, url, entity, contentType, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(String response) {
Log.d("HTTP", "onSuccess: " + response);
}
... other handlers
});
}
just write your xml or json to a string and send to server, with proper headers or without. and yes set "Content-Type" to "application/json"
If someone have a problem that httpclient send as Content-Type: text/plain, please refer this link: https://stackoverflow.com/a/26425401/361100
The loopj httpclient is somewhat changed (or has problem) which cannot override StringEntity native Content-Type to application/json.
You can add the JSON string as an InputStream of some kind - I've used the ByteArrayStream, then passing it to the RequestParams you should set the correctMimeType
InputStream stream = new ByteArrayInputStream(jsonParams.toString().getBytes(Charset.forName("UTF-8")));
multiPartEntity.put("model", stream, "parameters", Constants.MIME_TYPE_JSON);
Just make JSONObject and then convert it to String "someData" and simply send with "ByteArrayEntity"
private static AsyncHttpClient client = new AsyncHttpClient();
String someData;
ByteArrayEntity be = new ByteArrayEntity(someData.toString().getBytes());
client.post(context, url, be, "application/json", responseHandler);
It is working fine for me.
To post xml file to a php server :
public class MainActivity extends AppCompatActivity {
/**
* Send xml file to server via asynchttpclient lib
*/
Button button;
String url = "http://xxx/index.php";
String filePath = Environment.getExternalStorageDirectory()+"/Download/testUpload.xml";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
postFile();
}
});
}
public void postFile(){
Log.i("xml","Sending... ");
RequestParams params = new RequestParams();
try {
params.put("key",new File(filePath));
}catch (FileNotFoundException e){
e.printStackTrace();
}
AsyncHttpClient client = new AsyncHttpClient();
client.post(url, params, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes) {
Log.i("xml","StatusCode : "+i);
}
#Override
public void onFailure(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes, Throwable throwable) {
Log.i("xml","Sending failed");
}
#Override
public void onProgress(long bytesWritten, long totalSize) {
Log.i("xml","Progress : "+bytesWritten);
}
});
}
}
After adding android-async-http-1.4.9.jar to android studio,
go to build.gradle and add :
compile 'com.loopj.android:android-async-http:1.4.9' under dependencies
And on AndroidManifest.xml add:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Categories

Resources