I have a custom adapter that buttons and TextView, the TextView is changed on a button click within listview and it after sending and receiving feedback from http Post via Json response, I tried using runnable and assynctask but no success. In the runnable, I can not return a value from the method.
What I wanted is to send http request to the server and return json results, based on the returned results, the TextView will change.
What is the best approach to use to achieve this.
If any one can help point me to a resource that will help me to achieve this will be highly welcome.
Thanks in advance!
Here is my code..
public String getStatus(final String id, final String user){
final String[] resp = {"0/0/0"};
new Thread(new Runnable() {
#Override
public void run() {
// Building Parameters
final List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username", user));
params.add(new BasicNameValuePair("id", id));
Log.d("Nay Params", "" + params);
// getting product details by making HTTP request
JSONObject json = jsonParser.makeHttpRequest(NAY_URL, "POST",
params);
// check your log for json response
// json success tag
try {
Log.d("Nay Response", ""+ json);
success = json.getBoolean(TAG_SUCCESS);
yeaSt = json.getBoolean(TAG_E_STATUS);
naySt = json.getBoolean(TAG_A_STATUS);
yeaC = json.getInt(TAG_YEA);
nayC = json.getInt(TAG_NAY);
if (success){
resp[0] = yeaS + "/" + naySt + "/" + yeaC + "/" + nayC;
return resp[0];
//Can not return return value within void method that is; Class void run()
}
} catch (JSONException e) {
e.printStackTrace();
//return resp[0];
}
}
}).start();
//Can not acces json result outside the try catch block
Log.d("sux", ""+success);
// Log.d("Rest1", resp[0]);
return resp[0];
}
You could use Callable interface in order to run a thread and get the thread's result.
Here's the Callable documentation.
And Jakob explains how to use it here.
Hope that helped, if you need a concrete example I recently used this approach in my project, and I'll be happy to provide you with a sample.
Edit:
Here is a simple example of using callable, I changed a little bit my code so it is more clear:
public class Test {
ExecutorService executor;
public Test() {
/* I used an executor that creates a new thread every time creating a server requests object
* you might need to create a thread pool instead, depending on your application
* */
executor = Executors.newSingleThreadExecutor();
}
private JSONObject doTheWork() {
// init
Callable<JSONObject> callable;
Future<JSONObject> future;
JSONObject jsonResult = null;
try {
// create callable object with desired job
callable = new Callable<JSONObject>() {
#Override
public JSONObject call() throws Exception {
JSONObject jsonResult = new JSONObject();
// connect to the server
// insert desired data into json object
// and return the json object
return jsonResult;
}
};
future = executor.submit(callable);
jsonResult = future.get();
} catch(InterruptedException ex) {
// Log exception at first so you could know if something went wrong and needs to be fixed
} catch(ExecutionException ex) {
// Log exception at first so you could know if something went wrong and needs to be fixed
}
return jsonResult;
}
}
Another approach would be creating a class that implements Callable. Whatever of these approaches you choose depends on your application, and maybe person's taste :).
Happy that I helped.
Related
I have 10 health check URLs which are simply get service
I am hitting them in a loop like below
for(int i=0;i<10;i++){
Response response = given().when().relaxedHttpsValidation().get(url[i]);
list.add(response);
}
return list;
Now the problem is it hits API in series and waiting for a response for all, I just want to hit all API in parallel but combine the result, I tried using threads but unable to get an idea on how to combine the response in case of multi-threading
If I am reading your question right I believe you want to make parallel calls and combine the results, and in that case I would suggest you to make use of TestNG. I had a similar requirement in the past and this link helped me out
Here's a sample code
public class Parallel {
#DataProvider(parallel = true)
public Object[] getURL() {
return new Object[] { "https://reqres.in/api/users/1", "https://reqres.in/api/users/2",
"https://reqres.in/api/users/3", "https://reqres.in/api/users/4", "https://reqres.in/api/users/5",
"https://reqres.in/api/users/6" };
}
ArrayList<String> original = new ArrayList<String>();
#Test(dataProvider = "getURL")
public void stack(String url) {
Response response = given().when().get(url);
JsonPath js = response.jsonPath();
String email = js.getString("data.email");
original.add(js.getString("data.email"));
}
#AfterTest
public void simple() {
System.out.println("List : " + original);
}
}
Just remove (parallel = true) to see how it works sequentially. I have extracted the email field from the response using JSONPath and added to the list
Don't forget to update the POM
Thank you for your quick response i just want to share now how i achieved it
List responseList = new ArrayList();
ExecutorService exec = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
exec.submit(new Runnable() {
public void run() {
String response = executeServiceCall(urlArray[i]);
responseList.add(response);
}
});
} exec.shutdown();
try {
exec.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {
LOGGER.error(e.toString());
}
LOGGER.info("response list is " + responseList)
I am using the Facebook graph api to find out what pages a user is apart of. When the query comes back with a json object it has what I need but, for some reason it doesn't want to add to my array list. The correct value is printed in log.d it seems to skip my arraylist for some reason. Any ideas?
Find page function
private ArrayList<String> foundPages;
private JSONObject jsonObject;
public ArrayList<String> findPages()
{
accessToken = AccessToken.getCurrentAccessToken();
foundPages = new ArrayList<>();
GraphRequest request = GraphRequest.newGraphPathRequest(
accessToken,
"/me/accounts",
new GraphRequest.Callback() {
#Override
public void onCompleted(GraphResponse response) {
try {
jsonObject = response.getJSONObject();
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
request.executeAsync();
return foundPages;
}
There is a common way to solve this problem, which is to define a callback method which will return these values to you, AFTER they have been populated by the call, which goes something like this (my java is rusty, bear with me...)
define an interface :
interface Callback{
void apiResponseCallback(ArrayList<Page> result);//whatever your model is, make the array of that type
}
then, in your normal findPages method, change it to this:
public void findPages(Callback callback) {
//
//
........
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
callback.apiResponseCallback(foundPages);//here we are returning the data when it is done
}
then, when you call findPages
findPages(new Callback() {
#Override
public void apiResponseCallback(ArrayList<Page> result) {
here, this result parameter that comes through is your api call result to use, so result will be your populated pages to use.
}
});
}
sake of completeness:
public void findPages(Callback callback)
{
accessToken = AccessToken.getCurrentAccessToken();
foundPages = new ArrayList<>();
GraphRequest request = GraphRequest.newGraphPathRequest(
accessToken,
"/me/accounts",
new GraphRequest.Callback() {
#Override
public void onCompleted(GraphResponse response) {
try {
jsonObject = response.getJSONObject();
for(int i=0; i < jsonObject.getJSONArray("data").length(); i++)
{
page = response.getJSONObject().getJSONArray("data").getJSONObject(i).getString("name");
Log.d("viewmodel",page);
foundPages.add(page);
}
callback.apiResponseCallback(foundPages);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
request.executeAsync();
}
Yep. This here:
request.executeAsync();
triggers an asynchronous request. But your "current" thread simply continues to do:
return foundPages;
and it returns an empty list.
That list gets later filled, but at the moment in time when that method returns, that list is still empty. Or just gets filled. Who knows, as it gets filled asynchronously, at some unknown point in the future.
A solution could be to have some other variable/field that tells you the data has arrived and pushed into the list.
Alternatively, that method could just make a synchronous request, simply block the caller from progressing until the data has arrived.
You see, you can't have it both ways: when you don't wait for your results to arrive, you shouldn't expect them to be available immediately.
i m new android and find the best approach to update the data on UI with thread-safety for getting data from server.
here is my code to get data from server api and update list view,
but i was think this approach may cause to degrade the performance of application.
i m using okhttp for calling server api.
public String ActionAsyncGetTeamData(final String URL, final String token,final String expected_closure_date, final HTTPVerb verb, Activity activity) {
final String[] ResponseData = new String[1];
loading_complete=false;
_context = activity;
if (networkDetector.isNetworkAvailable()) {
new Thread(new Runnable() {
public void run() {
ResponseData[0] = GET(URL, token, expected_closure_date);
leadList.clear();
meetingList.clear();
taskList.clear();
TeamSyncModel teamSyncModel=json.fromJson(ResponseData[0],TeamSyncModel.class);
for (LeadModel leadModel: teamSyncModel.lead) {
leadList.add(leadModel);
}
for (MeetingModel meetingModel: teamSyncModel.meeting) {
meetingList.add(meetingModel);
}
for (TaskModel taskModel: teamSyncModel.task) {
taskList.add(taskModel);
}
loading_complete=true;
_context.runOnUiThread(new Runnable() {
#Override
public void run() {
Planner planner=new Planner();
planner.progress.setVisibility(View.GONE);
planner.list.setVisibility(View.VISIBLE);
planner.adapterTeam(_context);
planner.closure_header.requestFocus();
}
});
}
}).start();
return ResponseData[0];
}
else {
Planner planner=new Planner();
planner.progress.setVisibility(View.GONE);
planner.list.setVisibility(View.VISIBLE);
planner.adapterTeam(_context);
leadList.clear();
meetingList.clear();
taskList.clear();
loading_complete=true;
Toast.makeText(_context.getApplicationContext(), "Internet connection not available", Toast.LENGTH_SHORT).show();
}
return null;
}
Get data from Server
private String GET(String url, String token, String lastSync) {
client.setConnectTimeout(15, TimeUnit.MINUTES);
client.setReadTimeout(15, TimeUnit.MINUTES);
try {
Request request = new Request.Builder()
.url(url + lastSync)
.addHeader("x-access-token", token)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
} catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
is it right way to do this tasks in android
you try the following steps
1 in UI create refer to listview, adapter and asyntask
2 from the ui send reference to adapter at the asyntask in this getData from server and in the method onPostExecute of the asynk call this method adapter.notifyDatasetChange();
3 in the adapter you manage the info what you give of the server in the asyntask
AsyncTask question
I've followed some tutorials but it still isn't clear to me. Here's the code I currently have with some questions below the code. MainActivity calls SomeClassWithHTTPNeeds, which then calls the JSONParser (AsyncTask<>)
MainActivity:
String station = SomeClassWithHTTPNeeds.getInstance().getStation(123);
SomeClassWithHTTPNeeds:
getStation {
JSONParser = new JSONParser();
JSONObject station = parser.getJSONFromUrl("https://api....");
return JSONObject.getString("station");
}
JSONParser (AsyncTask< String, Void, String >)
protected String doInBackground(); ==> Seperate thread
protected void onPostExecute(); ==> On GUI thread
I was thinking:
--- Put the HTTPRequest in doInBackground();
Problem is I'm not sure how to:
get the JSONParser to return the JSONObject to the getStation method?
What I need to know
=> Where should I return the JSONObject: in background or execute?
=> How do I use the JSONParser once it's an AsyncTask? Will the execute() function return the value?
=> AsyncTask< String, Void, String > ==> How does this work? It's the return type?
Thanks a lot!
FAQs and general explaination of the usage of AsyncTask
=> Where should I do network operations? Where should I return my aquired values?
In general, you should do Network Operations in a Seperate Thread -> doInBackground(); since you do not want your UI to freeze when a Network Operation takes its time. So you should connect to your Service or .php script or wherever you get the Data from inside the doInBackground() method. Then you could also parse the data there and return the parsed data from the doInBackground() method by specifying the return type of doInBackground() to your desires, more about that down there. The onPostExecute() method will then receive your returned values from doInBackground() and represent them using the UI.
=> AsyncTask< String, Integer, Long> ==> How does this work?
In general, the AsyncTask class looks like this, which is nothing more than a generic class with 3 different generic types:
AsyncTask<Params, Progress, Result>
You can specify the type of Parameter the AsyncTask takes, the Type of the Progress indicator and the type of the result (the return type
of doInBackGround()).
Here is an Example of an AsyncTask looking like this:
AsyncTask<String, Integer, Long>
We have type String for the Parameters, Type Integer for the Progress and Type Long for the Result (return type of doInBackground()). You can use any type you want for Params, Progress and Result.
private class DownloadFilesTask extends AsyncTask<String, Integer, Long> {
// these Strings / or String are / is the parameters of the task, that can be handed over via the excecute(params) method of AsyncTask
protected Long doInBackground(String... params) {
String param1 = params[0];
String param2 = params[1];
// and so on...
// do something with the parameters...
// be careful, this can easily result in a ArrayIndexOutOfBounds exception
// if you try to access more parameters than you handed over
long someLong;
int someInt;
// do something here with params
// the params could for example contain an url and you could download stuff using this url here
// the Integer variable is used for progress
publishProgress(someInt);
// once the data is downloaded (for example JSON data)
// parse the data and return it to the onPostExecute() method
// in this example the return data is simply a long value
// this could also be a list of your custom-objects, ...
return someLong;
}
// this is called whenever you call puhlishProgress(Integer), for example when updating a progressbar when downloading stuff
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
// the onPostexecute method receives the return type of doInBackGround()
protected void onPostExecute(Long result) {
// do something with the result, for example display the received Data in a ListView
// in this case, "result" would contain the "someLong" variable returned by doInBackground();
}
}
=> How to use AsyncTask? How can I "call" it? How can I "execute" it?
In this case, the AsyncTask takes a String or String Array as a Parameter which will look like this once the AsyncTask is called: (The specified parameter is used in the execute(param) method of AsyncTask).
new DownloadFilesTask().execute("Somestring"); // some String as param
Be aware, that this call does not have a return value, the only return value you should use is the one returned from doInBackground(). Use the onPostExecute() method do make use of the returned value.
Also be careful with this line of code: (this execution will actually have a return value)
long myLong = new DownloadFilesTask().execute("somestring").get();
The .get() call causes the UI thread to be blocked (so the UI freezes if the operation takes longer than a few millisecons) while the AsyncTask is executing, because the execution does not take place in a separate thread. If you remove the call to .get() it will perform asynchronously.
=> What does this notation "execute(String... params)" mean?
This is a method with a so called "varargs" (variable arguments) parameter. To keep it simple, I will just say that it means that the actual number of values you can pass on to the method via this parameter is not specified, and any amount of values you hand to the method will be treated as an array inside the method. So this call could for example look like this:
execute("param1");
but it could however also look like this:
execute("param1", "param2");
or even more parameters. Assuming we are still talking about AsyncTask, the parameters can be accessed in this way in the doInBackground(String... params) method:
protected Long doInBackground(String... params) {
String str1 = params[0];
String str2 = params[1]; // be careful here, you can easily get an ArrayOutOfBoundsException
// do other stuff
}
You can read more about AsyncTask here: http://developer.android.com/reference/android/os/AsyncTask.html
Also take a look at this AsyncTask example: https://stackoverflow.com/a/9671602/1590502
package com.example.jsontest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONObject;
import android.util.Log;
public class HttpClient {
private static final String TAG = "HttpClient";
public static JSONObject SendHttpPost(String URL, JSONObject jsonObjSend) {
try {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost httpPostRequest = new HttpPost(URL);
StringEntity se;
se = new StringEntity(jsonObjSend.toString());
httpPostRequest.setEntity(se);
httpPostRequest.setHeader("Accept", "application/json");
httpPostRequest.setHeader("Content-type", "application/json");
httpPostRequest.setHeader("Accept-Encoding", "gzip");
long t = System.currentTimeMillis();
HttpResponse response = (HttpResponse) httpclient.execute(httpPostRequest);
Log.i(TAG, "HTTPResponse received in [" + (System.currentTimeMillis()-t) + "ms]");
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
instream = new GZIPInputStream(instream);
}
String resultString= convertStreamToString(instream);
instream.close();
resultString = resultString.substring(0,resultString.length()-1);
JSONObject jsonObjRecv = new JSONObject(resultString);
Log.i(TAG,"<JSONObject>\n"+jsonObjRecv.toString()+"\n</JSONObject>");
return jsonObjRecv;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
private static String convertStreamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
Log.e("JSON", ""+line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
}
Asynctask:
public class callCarWeb extends AsyncTask {
#Override
protected void onPreExecute() {
mDialog = new ProgressDialog(MainActivity.this);
mDialog.setMessage("Please wait...");
mDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mDialog.setIndeterminate(true);
mDialog.setCancelable(false);
mDialog.show();
}
#Override
protected Void doInBackground(Void... params) {
try {
JSONObject jsonObjSend = new JSONObject();
jsonObjSend.put("username", username);
jsonObjSend.put("password", passwd);
Log.e("SEND", jsonObjSend.toString());
JSONObject json = HttpClient.SendHttpPost("http://10.0.2.2/json/login.php", jsonObjSend);
String status = json.getString("status");
if(status.equalsIgnoreCase("pass")){
String id = json.getString("user_id");
Log.e("id", id);
String name = json.getString("name");
Log.e("name", name);
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
mDialog.cancel();
}
}
## Heading ##
I think you can execute your HTTPRequest in your doInBackground of the Async task. And JSONParser at onPostExecute.
do read some generics.
now, when you write your async task JSONParser here params is of type String, progress is of type Void and result is of type String. Read this.
generally people overrides two methods doInBackground() and onPostExecute(), the first one takes params and returns a result and second one takes that result. These are protected methods you can't call em directly. Then you might ask how to send param to doInBackground(), look at execute() API.
doInBackground() runs on background thread, its not a blocking call!!
don't do this,
JSONParser = new JSONParser();
JSONObject station = parser.getJSONFromUrl("https://api....");
return JSONObject.getString("station");
instead write on interface in JSONParser or somewhere else like,
public interface OnParseCompleteListener {
void onParseComplete(JSONObject obj);
}
now your JSONParser class will something like,
public class JSONParser extends AsyncTask<String, Void, String> {
private OnParseCompleteListener mOnParseCompleteListener;
public void setOnParseCompleteListener(OnParseCompleteListener listener) {
mOnParseCompleteListener = listener;
}
protected String doInBackground(String... params) {
/*
* do http request and return a result
*/
}
protected void onPostExecute(String... result) {
/*
* parse the resulting json string or you can parse same string in
* doInBackground and can send JSONObject as a result directly.
* at this stage say you have a JSONObject obj, follow
*/
if (mOnParseCompleteListener != null) {
mOnParseCompleteListener.onParseComplete(obj);
}
}
}
when you create an object of JSONParser set OnParseCompleteListener.
JSONParser parser = new JSONParser();
parser.setOnParseCompleteListener(listener);
parse.execute("may be an url");
now you decide from where to pass or create your own listener.
I am creating an android application which has to execute web requests in the background and then handle the received data and modify the user interface according to the server response.
The goal of posting requests and handling data in the background is to avoid the freezing of user interface. Currently however I notice that the user interface is freezing so I am not sure the logic is working as it is supposed to.
Here is the part of code which is supposed to post requests and handle responses in its own thread and then pass the data to GUI:
public class ServerConnection {
Queue<String> requests;
...
DefaultHttpClient httpClient;
HttpHost targetHost;
Handler handler;
ServerResponseHandler responseHandler;
Activity activity;
public ServerConnection(Activity activity){
this.activity = activity;
this.responseHandler = (ServerResponseHandler) activity;
httpClient = new DefaultHttpClient();
targetHost = new HttpHost(TARGET_DOMAIN, 80, "http");
requests = new LinkedList<String>();
}
private Runnable requestSender = new Runnable(){
#Override
public void run() {
if(!requests.isEmpty()){
String requestString = requests.remove();
HttpGet httpGet = new HttpGet(requestString);
httpGet.addHeader("Accept", "text/xml");
String encodingString = "testuser:testpass";
String sEncodedString = Base64Coder.encodeString(encodingString);
try{
String sContent = fetchURL(requestString, sEncodedString);
XMLParser xmlParser = new XMLParser();
List <Product> products = xmlParser.getProducts(sContent);
responseHandler.onProductsResponse(products);
}
catch(Exception ex){
Log.e(TAG, ex.getMessage());
}
}
}
};
public void sendRequest(String requestString){
requests.add(requestString);
handler = new Handler();
handler.post(requestSender);
}
The method sendRequest() is called from the main activity which implements ServerResponseHandler. I guess the request is executed in its own thread and by calling
responseHandler.onProductsResponse(products);
the list of products (data from the web) is passed to main activity. Anyway due to poor performance I would appreciate if anyone could correct any possible issue in the logic above or suggest any other (better) option.
I'd suggest you take a look at ASyncTask class (available since Android 1.5).
It simplifies the process of creating a background Thread that synchronizes with the GUI thread once it's complete.
You should be able to achieve what you're trying using code something list this
private class DownloadFilesTask extends AsyncTask<String, List<Product>, Integer> {
protected List<Products> doInBackground(String... requestStrings) {
int count = requestStrings.length;
int results = 0;
for (int i = 0; i < count; i++) {
String requestString = requestStrings[i];
HttpGet httpGet = new HttpGet(requestString);
httpGet.addHeader("Accept", "text/xml");
String encodingString = "testuser:testpass";
String sEncodedString = Base64Coder.encodeString(encodingString);
try{
String sContent = fetchURL(requestString, sEncodedString);
XMLParser xmlParser = new XMLParser();
List <Product> products = xmlParser.getProducts(sContent);
results++;
publishProgress(products);
}
catch(Exception ex){
Log.e(TAG, ex.getMessage());
}
}
return results;
}
protected void onProgressUpdate(Integer... progress) {
// TODO You are on the GUI thread, and the first element in
// the progress parameter contains the last progress
// published from doInBackground, so update your GUI
}
protected void onPostExecute(int result) {
// Processing is complete, result contains the number of
// results you processed
}
}
And execute by calling
new DownloadFilesTask().execute(url1, url2, url3);
According to the handler javadoc, I don't think the post() method create any threads. If I'm right it execute the Runnable on the thread to which the handler is attached. So in this case this is the activity thread so the UI thread ! That's why you have poor performance.
You have to implement a Thread which execute your Runnable. But by doing that, you won't be able to update your activity like you currently do by calling :
responseHandler.onProductsResponse(products);
This is because you are not any more in the UI thread, and only the UI thread is authorized to interact with the UI (so the activity).
So you have to replace this call by accessing your Handler.
Message msg = handler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putSerializable( "products", products ); //not sure it will pass here
msg.setData( bundle );
handler.sendMessage( msg );
And implementing the handleMessage() method for your Handler :
#Override
public void handleMessage( Message msg )
{
List <Product> products = msg.getData().getSerializable( "products" );
responseHandler.onProductsResponse(products);
}
Last but not least : the Handler has to still be created in the activity thread.