Android async rest web request design - android

I am new to Android dev and have the following design question when one makes an asnyc rest web request. In my main activity I have a button that does the request using an async task:
public class MainActivity extends ActionBarActivity {
final Context context=this;
.......
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
Button btn = (Button)findViewById(R.id.myshowbutton);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
WebRequest wr=new WebRequest(view.getRootView());
wr.execute("http://www.cheesejedi.com/rest_services/get_big_cheese?level=1");
}
});
}
Th async task class which is in a separate java file:
public class WebRequest extends AsyncTask<String, String, String> {
private View view;
public WebRequest(View v) {
this.view = v;
}
#Override
protected String doInBackground(String... uri) {
HttpClient httpclient = new DefaultHttpClient();
HttpResponse response;
String responseString = null;
try {
response = httpclient.execute(new HttpGet(uri[0]));
StatusLine statusLine = response.getStatusLine();
if(statusLine.getStatusCode() == HttpStatus.SC_OK){
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
out.close();
responseString = out.toString();
} else{
//Closes the connection.
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (ClientProtocolException e) {
//TODO Handle problems..
} catch (IOException e) {
//TODO Handle problems..
}
return responseString;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
EditText et=(EditText)view.findViewById(R.id.editText);
et.setText(result);
}
}
As you can see I am setting the EditText box to the JSON which is returned. I pass a reference of the root view to my WebRequest class so that I can get hold of the EditText box.
Is there a better way of doing this? It does not seem very elegant. Perhaps a nice libary for web requests?

This way works fine, and it is good to know how to write this. There are a couple of things you can do to improve this, and there is also a library you can use to greatly simplify your code.
Option 1: Improve your code
First, you can use a Callback to set the text in the EditText in your Activity, instead:
Change these lines in WebRequest:
private View view;
public WebRequest(View v) {
this.view = v;
}
to
public interface Callback {
public void call(String result);
}
private Callback callback;
public WebRequest(Callback c) {
this.callback = callback;
}
Then in onPostExecute, just call:
callback.call(result);
Now, when you start the web service, your call will look like this:
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
WebRequest wr=new WebRequest(new Callback() {
#Override
public void call(String result) {
((EditText)view.findViewById(R.id.editText)).setText(result);
}
});
wr.execute("http://www.cheesejedi.com/rest_services/get_big_cheese?level=1");
}
});
Option 2: Use a library
If, however, you do want to use a library, I recommend droidQuery. Using this, you can simplify your code from this:
Button btn = (Button)findViewById(R.id.myshowbutton);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
WebRequest wr=new WebRequest(view.getRootView());
wr.execute("http://www.cheesejedi.com/rest_services/get_big_cheese?level=1");
}
});
to:
$.with(this, R.id.myshowbutton).click(new Function() {
#Override
public void invoke($ d, Object... args) {
$.ajax(new AjaxOptions().url("http://www.cheesejedi.com/rest_services/get_big_cheese?level=1").success(new Function() {
#Override
public void invoke($ d, Object... args) {
$.with(MyActivity.this, R.id.editText).text(args[0].toString());
}
}));
}
});
and you no longer need your WebRequest class.

The main problem with handing in a reference to a view into your AsyncTask is what happens when the Activity is destroyed and recreated for example in case of an orientation change (turning the device). In this case you can leak a reference to the Activity, which can get expensive memory-wise.
There are a couple of libraries around that try to help with this kind of problems. Maybe have a look at RoboSpice also.

If you want to decouple the EditText from your WebRequest, consider making a callback interface for your MainActivity to implement.
MainActivity.java
public class MainActivity extends ActionBarActivity implements WebRequestCallback {
...
#Override
public void handleStringResult(String result) {
btn.setText(result);
}
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
WebRequest wr = new WebRequest(this);
wr.execute("http://www.cheesejedi.com/rest_services/get_big_cheese?level=1");
}
});
}
...
}
WebRequestCallback.java
public interface WebRequestCallback {
public void handleStringResult(String result);
}
WebRequest.java
public class WebRequest extends AsyncTask<String, String, String> {
private WebRequestCallback callback;
public WebRequest(WebRequestCallback callback) {
this.callback = callback;
}
#Override
protected String doInBackground(String... uri) {
...
}
#Override
protected void onPostExecute(String result) {
callback.handleStringResult(result);
}
}
Also, while I have not used it myself, I have heard good things about Ion if you are looking for a more robust Android networking library.

Related

Static AsyncTask ProgressBar

Sorry if this seems a basic question. I've updated Android Studio and notice some memory leak warnings on my AsyncTasks saying I should make them static. I have made them static but can't seem to make anything like List, ProgressBar, ImageView work without getting the same memory leak warning. It seems I can't win no matter which way I try it. I guess my questions are:
Are AsyncTasks supposed to be static? The official documentation doesn't make it static but my IDE fires warnings saying they should.
If they are meant to be static, how can I start and stop a ProgressBar within the static AsyncTask.
EDIT
This still throws "This AsyncTask class should be static or leaks might occur"
private class DownloadCategoryTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... params) {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
String url = Config.API_URL +
"/Index.aspx?" +
"type=3&" +
"site_id=" + SITE_ID;
String method = "GET";
String array_name = "categories";
Downloaded_category_array = Config.getJSONNew(url, method, array_name, context);
return "";
}
protected void onPostExecute(String result) {
if(isCancelled()){
return;
}
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
//Update your UI here
//showProgressBar();
}
});
Populate_category_list();
}
}
Try this solution which I found:
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progress_bar);
}
public void startAsyncTask(View v) {
ExampleAsyncTask task = new ExampleAsyncTask(this);
task.execute(10);
}
private static class ExampleAsyncTask extends AsyncTask<Integer, Integer, String> {
private WeakReference<MainActivity> activityWeakReference;
ExampleAsyncTask(MainActivity activity) {
activityWeakReference = new WeakReference<MainActivity>(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
MainActivity activity = activityWeakReference.get();
if (activity == null || activity.isFinishing()) {
return;
}
activity.progressBar.setVisibility(View.VISIBLE);
}
#Override
protected String doInBackground(Integer... integers) {
for (int i = 0; i < integers[0]; i++) {
publishProgress((i * 100) / integers[0]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Finished!";
}
}
}
Reference: here
No, No need to make AsyncTasks as static.
If non static methods are trying to modify static members then IDE throws warning to make it static.
If you want to update your UI from AsyncTask use 'runOnUiThread'.
runOnUiThread(new Runnable() {
#Override
public void run() {
//Update your UI here
showProgressBar();
}
});
Looks like you are using anonymous inner class.
Here is the solution,
private class LoadData extends AsyncTask<Void, Void, String> {
LoadData() {
}
#Override
protected String doInBackground(Void... params) {
return "task finished";
}
#Override
protected void onPostExecute(String result) {
runOnUiThread(new Runnable() {
#Override
public void run() {
//Update your UI here
//showProgressBar();
}
});
}
}
//Execute your task
new LoadData().execute();

AsyncTask not calling doInBackground

I have a button in my application which on click should run AsyncTask but the doInBackground method is not being called, I am trying to connect to my server using okHTTP inside AsyncTask, here is my code,
public class Home extends Fragment implements View.OnClickListener {
Button button;
public static final String TAG = "aj";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.home, container, false);
button = (Button) v.findViewById(R.id.button);
button.setOnClickListener(this);
FirebaseMessaging.getInstance().subscribeToTopic("test");
FirebaseInstanceId.getInstance().getToken();
return v;
}
#Override
public void onClick(View v) {
sendRequest myTask = new sendRequest();
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.HONEYCOMB)
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
myTask.execute();
}
private class sendRequest extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... params) {
Log.v(TAG, "doInBackground");
String token = FirebaseInstanceId.getInstance().getToken();
OkHttpClient client = new OkHttpClient();
RequestBody body = null;
if (token != null) {
body = new FormBody.Builder()
.add("Token", token)
.build();
}
Request request = new Request.Builder()
.url("https://www.example.com/aj20010319")
.post(body)
.build();
try {
client.newCall(request).execute();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
I don't know why the doInBackground method is not being called, the logger doesn't log anything regarding it. And if I am correct, it is setup correctly. I tried the solutions in other threads but none is working.
checked your code in my machine removing the complexities its working fine i pasted the code bellow.
Two thing it can happen
problem with build version . just call the myTask.execute();remove all the if else
It might happen with the logcat . if possible make toast from the doInBackground
Toast.makeText(getApplicationContext(), "text", Toast.LENGTH_LONG).show();
If you are searching in the logcat it always searches by message not by tag so search by doInBackground not by aj
public class MainActivity extends Activity implements View.OnClickListener{
Button button;
#Override
public void onClick(View arg0) {
sendRequest myTask = new sendRequest();
if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.HONEYCOMB)
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
myTask.execute();
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button)findViewById(R.id.button1);
button.setOnClickListener(this);
}
private class sendRequest extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... params)
{
int i=0;
while(++i<100)
{
Log.i("www", "www");
}
return null;
}
}
}
You need to instantiate the class
new myTask().execute();
Inside the onClickListener

Android - progressdialog not displaying in AsyncTask

I have an android app that I am having trouble with.
Basically the ProgressDialog is not showing at all. I believe this to be a threading issue of some sort but I don't know how to fix it.
I am using ActionBarSherlock with some Fragments. I am also using the new Android DrawerLayout where I have my options on the drawer, which replace a fragment when clicked.
On first load of my app, I want to check the database to see if the inital data has been downloaded. If not, then I go off and begin an AsyncTask to download the data. This SHOULD have a ProgressDialog display during this, but it doesnt.
Can someone see where I am going wrong? Thanks.
MainScreen - The default landing page/fragment when the app opens
public class MainScreen extends SherlockFragment {
public static final String TAG = "MainScreen";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_main, container, false);
setHasOptionsMenu(false);
ImageView imgLogo = (ImageView) rootView.findViewById(R.id.imgMainScreen);
imgLogo.setOnClickListener(new ButtonHandler(getActivity()));
checkDatabase();
return rootView;
}
private void checkDatabase() {
//Ensure there is data in the database
DBHelper db = new DBHelper(this.getSherlockActivity());
db.checkDatabase();
}
...
}
DBHelper.checkDatabase() - The method that initiates the download
public void checkDatabase() {
if (isEmpty()) {
//Connect to net and download data
NetworkManager nm = new NetworkManager(activity);
if (!nm.downloadData()) {
Toast.makeText(activity, R.string.internetCheck, Toast.LENGTH_SHORT).show();
}
}
}
and finally
NetworkManager.downloadData() - The method that kicks off the AsyncTask:
public boolean downloadData() {
try {
return new HttpConnection(activity).execute().get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return false;
}
public class HttpConnection extends AsyncTask<Void, Void, Boolean> {
private ProgressDialog progressDialog;
private Activity m_activity;
protected HttpConnection(Activity activity) {
m_activity = activity;
}
#Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(m_activity);
progressDialog.setMessage("Wait ...");
progressDialog.setCancelable(false);
progressDialog.setMax(100);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.show();
super.onPreExecute();
}
#Override
protected Boolean doInBackground(Void... params) {
String[] types = new String[]{"type1", "type2", "type3", "type4", };
StringBuilder sb = new StringBuilder();
for(String type : types) {
sb = new StringBuilder();
if(DBHelper.TYPE4_TABLE.equals(type)) {
InputStream is = activity.getResources().openRawResource(R.raw.dbdata);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
try {
sb.append(reader.readLine());
} catch (IOException e) {
Toast.makeText(activity.getApplicationContext(), "Error retriveving data", Toast.LENGTH_SHORT).show();
Log.e(Constants.TAG, "Error reading data");
e.printStackTrace();
}
} else {
sb = fetchURLData(Constants.ALL_URL+type);
}
cleanDataAndStore(sb, type);
}
return true;
}
#Override
protected void onPostExecute(Boolean result){
progressDialog.hide();
}
}
Using the above code, all I get is a white screen as the app tries to load, and sometimes an ANR. When the download is done, the fragment loads. So it works fine except for the missing ProgressDialog.
PS, Notice I'm setting the activity in each constructor.
Thanks.
Remove .get() from return new HttpConnection(activity).execute().get(); You are basically locking your UI thread. Once removed it should work as AsyncTasks are expected to work.
The purpose is to be Asynchronous so boolean downloadData() should have a return type of void. If you need to do something with the data then you should implement an interface "listener" and pass it to the AsyncTask.
Example Listener:
class TaskConnect extends AsyncTask<Void, Void, ConnectionResponse> {
private final AsyncTaskListener mListener;
/**
*
*/
public TaskConnect(AsyncTaskListener listener) {
...
mListener = listener;
}
#Override
protected void onPreExecute() {
if (mListener != null) {
mListener.onPreExecute(mId);
}
}
#Override
protected ConnectionResponse doInBackground(Void... cData) {
...
return responseData;
}
#Override
protected void onPostExecute(ConnectionResponse response) {
if (mListener != null) {
mListener.onComplete(response);
} else {
LOG.w("No AsyncTaskListener!", new Throwable());
}
}
}
public interface AsyncTaskListener {
public abstract void onPreExecute(int id);
public abstract void onComplete(ConnectionResponse response);
}
My issue was not the common issue of others where they were calling get() method after execute() method. My issue was the Context I was passing to my AsyncTask method. I have a settingsActivity and I have a ReadMeActivity that calls the asynctask task. Instead of using the context in which is was being called (ReadMeActivity.this) I used the settingsActivity which prevented it from being seen. Once I switched it and passed it the context in which the activity was being called it worked.
Hope it helps someone else.

How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?

I have this two classes. My main Activity and the one that extends the AsyncTask, Now in my main Activity I need to get the result from the OnPostExecute() in the AsyncTask. How can I pass or get the result to my main Activity?
Here is the sample codes.
My main Activity.
public class MainActivity extends Activity{
AasyncTask asyncTask = new AasyncTask();
#Override
public void onCreate(Bundle aBundle) {
super.onCreate(aBundle);
//Calling the AsyncTask class to start to execute.
asyncTask.execute(a.targetServer);
//Creating a TextView.
TextView displayUI = asyncTask.dataDisplay;
displayUI = new TextView(this);
this.setContentView(tTextView);
}
}
This is the AsyncTask class
public class AasyncTask extends AsyncTask<String, Void, String> {
TextView dataDisplay; //store the data
String soapAction = "http://sample.com"; //SOAPAction header line.
String targetServer = "https://sampletargeturl.com"; //Target Server.
//SOAP Request.
String soapRequest = "<sample XML request>";
#Override
protected String doInBackground(String... string) {
String responseStorage = null; //storage of the response
try {
//Uses URL and HttpURLConnection for server connection.
URL targetURL = new URL(targetServer);
HttpURLConnection httpCon = (HttpURLConnection) targetURL.openConnection();
httpCon.setDoOutput(true);
httpCon.setDoInput(true);
httpCon.setUseCaches(false);
httpCon.setChunkedStreamingMode(0);
//properties of SOAPAction header
httpCon.addRequestProperty("SOAPAction", soapAction);
httpCon.addRequestProperty("Content-Type", "text/xml; charset=utf-8");
httpCon.addRequestProperty("Content-Length", "" + soapRequest.length());
httpCon.setRequestMethod(HttpPost.METHOD_NAME);
//sending request to the server.
OutputStream outputStream = httpCon.getOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
writer.write(soapRequest);
writer.flush();
writer.close();
//getting the response from the server
InputStream inputStream = httpCon.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(50);
int intResponse = httpCon.getResponseCode();
while ((intResponse = bufferedReader.read()) != -1) {
byteArrayBuffer.append(intResponse);
}
responseStorage = new String(byteArrayBuffer.toByteArray());
} catch (Exception aException) {
responseStorage = aException.getMessage();
}
return responseStorage;
}
protected void onPostExecute(String result) {
aTextView.setText(result);
}
}
Easy:
Create interface class, where String output is optional, or can be whatever variables you want to return.
public interface AsyncResponse {
void processFinish(String output);
}
Go to your AsyncTask class, and declare interface AsyncResponse as a field :
public class MyAsyncTask extends AsyncTask<Void, Void, String> {
public AsyncResponse delegate = null;
#Override
protected void onPostExecute(String result) {
delegate.processFinish(result);
}
}
In your main Activity you need to implements interface AsyncResponse.
public class MainActivity implements AsyncResponse{
MyAsyncTask asyncTask =new MyAsyncTask();
#Override
public void onCreate(Bundle savedInstanceState) {
//this to set delegate/listener back to this class
asyncTask.delegate = this;
//execute the async task
asyncTask.execute();
}
//this override the implemented method from asyncTask
#Override
void processFinish(String output){
//Here you will receive the result fired from async class
//of onPostExecute(result) method.
}
}
UPDATE
I didn't know this is such a favourite to many of you. So here's the simple and convenience way to use interface.
still using same interface. FYI, you may combine this into AsyncTask class.
in AsyncTask class :
public class MyAsyncTask extends AsyncTask<Void, Void, String> {
// you may separate this or combined to caller class.
public interface AsyncResponse {
void processFinish(String output);
}
public AsyncResponse delegate = null;
public MyAsyncTask(AsyncResponse delegate){
this.delegate = delegate;
}
#Override
protected void onPostExecute(String result) {
delegate.processFinish(result);
}
}
do this in your Activity class
public class MainActivity extends Activity {
MyAsyncTask asyncTask = new MyAsyncTask(new AsyncResponse(){
#Override
void processFinish(String output){
//Here you will receive the result fired from async class
//of onPostExecute(result) method.
}
}).execute();
}
Or, implementing the interface on the Activity again
public class MainActivity extends Activity
implements AsyncResponse{
#Override
public void onCreate(Bundle savedInstanceState) {
//execute the async task
new MyAsyncTask(this).execute();
}
//this override the implemented method from AsyncResponse
#Override
void processFinish(String output){
//Here you will receive the result fired from async class
//of onPostExecute(result) method.
}
}
As you can see 2 solutions above, the first and third one, it needs to create method processFinish, the other one, the method is inside the caller parameter. The third is more neat because there is no nested anonymous class.
Tip: Change String output, String response, and String result to different matching types in order to get different objects.
There are a few options:
Nest the AsyncTask class within your Activity class. Assuming you don't use the same task in multiple activities, this is the easiest way. All your code stays the same, you just move the existing task class to be a nested class inside your activity's class.
public class MyActivity extends Activity {
// existing Activity code
...
private class MyAsyncTask extends AsyncTask<String, Void, String> {
// existing AsyncTask code
...
}
}
Create a custom constructor for your AsyncTask that takes a reference to your Activity. You would instantiate the task with something like new MyAsyncTask(this).execute(param1, param2).
public class MyAsyncTask extends AsyncTask<String, Void, String> {
private Activity activity;
public MyAsyncTask(Activity activity) {
this.activity = activity;
}
// existing AsyncTask code
...
}
You can try this code in your Main class.
That worked for me, but i have implemented methods in other way
try {
String receivedData = new AsyncTask().execute("http://yourdomain.com/yourscript.php").get();
}
catch (ExecutionException | InterruptedException ei) {
ei.printStackTrace();
}
I felt the below approach is very easy.
I have declared an interface for callback
public interface AsyncResponse {
void processFinish(Object output);
}
Then created asynchronous Task for responding all type of parallel requests
public class MyAsyncTask extends AsyncTask<Object, Object, Object> {
public AsyncResponse delegate = null;//Call back interface
public MyAsyncTask(AsyncResponse asyncResponse) {
delegate = asyncResponse;//Assigning call back interfacethrough constructor
}
#Override
protected Object doInBackground(Object... params) {
//My Background tasks are written here
return {resutl Object}
}
#Override
protected void onPostExecute(Object result) {
delegate.processFinish(result);
}
}
Then Called the asynchronous task when clicking a button in activity Class.
public class MainActivity extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
Button mbtnPress = (Button) findViewById(R.id.btnPress);
mbtnPress.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MyAsyncTask asyncTask =new MyAsyncTask(new AsyncResponse() {
#Override
public void processFinish(Object output) {
Log.d("Response From Asynchronous task:", (String) output);
mbtnPress.setText((String) output);
}
});
asyncTask.execute(new Object[] { "Your request to aynchronous task class is giving here.." });
}
});
}
}
Thanks
This answer might be late but I would like to mention few things when your Activity dependent on AsyncTask. That would help you in prevent crashes and memory management. As already mentioned in above answers go with interface, we also say them callbacks. They will work as an informer, but never ever send strong reference of Activity or interface always use weak reference in those cases.
Please refer to below screenshot to findout how that can cause issues.
As you can see if we started AsyncTask with a strong reference then there is no guarantee that our Activity/Fragment will be alive till we get data, so it would be better to use WeakReference in those cases and that will also help in memory management as we will never hold the strong reference of our Activity then it will be eligible for garbage collection after its distortion.
Check below code snippet to find out how to use awesome WeakReference -
MyTaskInformer.java Interface which will work as an informer.
public interface MyTaskInformer {
void onTaskDone(String output);
}
MySmallAsyncTask.java AsyncTask to do long running task, which will use WeakReference.
public class MySmallAsyncTask extends AsyncTask<String, Void, String> {
// ***** Hold weak reference *****
private WeakReference<MyTaskInformer> mCallBack;
public MySmallAsyncTask(MyTaskInformer callback) {
this.mCallBack = new WeakReference<>(callback);
}
#Override
protected String doInBackground(String... params) {
// Here do whatever your task is like reading/writing file
// or read data from your server or any other heavy task
// Let us suppose here you get response, just return it
final String output = "Any out, mine is just demo output";
// Return it from here to post execute
return output;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// Here you can't guarantee that Activity/Fragment is alive who started this AsyncTask
// Make sure your caller is active
final MyTaskInformer callBack = mCallBack.get();
if(callBack != null) {
callBack.onTaskDone(s);
}
}
}
MainActivity.java This class is used to start my AsyncTask implement interface on this class and override this mandatory method.
public class MainActivity extends Activity implements MyTaskInformer {
private TextView mMyTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyTextView = (TextView) findViewById(R.id.tv_text_view);
// Start your AsyncTask and pass reference of MyTaskInformer in constructor
new MySmallAsyncTask(this).execute();
}
#Override
public void onTaskDone(String output) {
// Here you will receive output only if your Activity is alive.
// no need to add checks like if(!isFinishing())
mMyTextView.setText(output);
}
}
You can do it in a few lines, just override onPostExecute when you call your AsyncTask. Here is an example for you:
new AasyncTask()
{
#Override public void onPostExecute(String result)
{
// do whatever you want with result
}
}.execute(a.targetServer);
I hope it helped you, happy codding :)
in your Oncreate():
`
myTask.execute("url");
String result = "";
try {
result = myTask.get().toString();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}`
Why do people make it so hard.
This should be sufficient.
Do not implement the onPostExecute on the async task, rather implement it on the Activity:
public class MainActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState) {
//execute the async task
MyAsyncTask task = new MyAsyncTask(){
protected void onPostExecute(String result) {
//Do your thing
}
}
task.execute("Param");
}
}
You can call the get() method of AsyncTask (or the overloaded get(long, TimeUnit)). This method will block until the AsyncTask has completed its work, at which point it will return you the Result.
It would be wise to be doing other work between the creation/start of your async task and calling the get method, otherwise you aren't utilizing the async task very efficiently.
You can write your own listener. It's same as HelmiB's answer but looks more natural:
Create listener interface:
public interface myAsyncTaskCompletedListener {
void onMyAsynTaskCompleted(int responseCode, String result);
}
Then write your asynchronous task:
public class myAsyncTask extends AsyncTask<String, Void, String> {
private myAsyncTaskCompletedListener listener;
private int responseCode = 0;
public myAsyncTask() {
}
public myAsyncTask(myAsyncTaskCompletedListener listener, int responseCode) {
this.listener = listener;
this.responseCode = responseCode;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
String result;
String param = (params.length == 0) ? null : params[0];
if (param != null) {
// Do some background jobs, like httprequest...
return result;
}
return null;
}
#Override
protected void onPostExecute(String finalResult) {
super.onPostExecute(finalResult);
if (!isCancelled()) {
if (listener != null) {
listener.onMyAsynTaskCompleted(responseCode, finalResult);
}
}
}
}
Finally implement listener in activity:
public class MainActivity extends AppCompatActivity implements myAsyncTaskCompletedListener {
#Override
public void onMyAsynTaskCompleted(int responseCode, String result) {
switch (responseCode) {
case TASK_CODE_ONE:
// Do something for CODE_ONE
break;
case TASK_CODE_TWO:
// Do something for CODE_TWO
break;
default:
// Show some error code
}
}
And this is how you can call asyncTask:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Some other codes...
new myAsyncTask(this,TASK_CODE_ONE).execute("Data for background job");
// And some another codes...
}
Hi you can make something like this:
Create class which implements AsyncTask
// TASK
public class SomeClass extends AsyncTask<Void, Void, String>>
{
private OnTaskExecutionFinished _task_finished_event;
public interface OnTaskExecutionFinished
{
public void OnTaskFihishedEvent(String Reslut);
}
public void setOnTaskFinishedEvent(OnTaskExecutionFinished _event)
{
if(_event != null)
{
this._task_finished_event = _event;
}
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
#Override
protected String doInBackground(Void... params)
{
// do your background task here ...
return "Done!";
}
#Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
if(this._task_finished_event != null)
{
this._task_finished_event.OnTaskFihishedEvent(result);
}
else
{
Log.d("SomeClass", "task_finished even is null");
}
}
}
Add in Main Activity
// MAIN ACTIVITY
public class MyActivity extends ListActivity
{
...
SomeClass _some_class = new SomeClass();
_someclass.setOnTaskFinishedEvent(new _some_class.OnTaskExecutionFinished()
{
#Override
public void OnTaskFihishedEvent(String result)
{
Toast.makeText(getApplicationContext(),
"Phony thread finished: " + result,
Toast.LENGTH_SHORT).show();
}
});
_some_class.execute();
...
}
Create a static member in your Activity class. Then assign the value during the onPostExecute
For example, if the result of your AsyncTask is a String, create a public static string in your Activity
public static String dataFromAsyncTask;
Then, in the onPostExecute of the AsyncTask, simply make a static call to your main class and set the value.
MainActivity.dataFromAsyncTask = "result blah";
I make it work by using threading and handler/message.
Steps as follow:
Declare a progress Dialog
ProgressDialog loadingdialog;
Create a function to close dialog when operation is finished.
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
loadingdialog.dismiss();
}
};
Code your Execution details:
public void startUpload(String filepath) {
loadingdialog = ProgressDialog.show(MainActivity.this, "Uploading", "Uploading Please Wait", true);
final String _path = filepath;
new Thread() {
public void run() {
try {
UploadFile(_path, getHostName(), getPortNo());
handler.sendEmptyMessage(0);
} catch (Exception e) {
Log.e("threadmessage", e.getMessage());
}
}
}.start();
}
You need to use "protocols" to delegate or provide data to the AsynTask.
Delegates and Data Sources
A delegate is an object that acts on behalf of, or in coordination with, another object when that object encounters an event in a program. (Apple definition)
protocols are interfaces that define some methods to delegate some behaviors.
Here is a complete example!!!
try this:
public class SomAsyncTask extends AsyncTask<String, Integer, JSONObject> {
private CallBack callBack;
public interface CallBack {
void async( JSONObject jsonResult );
void sync( JSONObject jsonResult );
void progress( Integer... status );
void cancel();
}
public SomAsyncTask(CallBack callBack) {
this.callBack = callBack;
}
#Override
protected JSONObject doInBackground(String... strings) {
JSONObject dataJson = null;
//TODO query, get some dataJson
if(this.callBack != null)
this.callBack.async( dataJson );// asynchronize with MAIN LOOP THREAD
return dataJson;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if(this.callBack != null)
this.callBack.progress(values);// synchronize with MAIN LOOP THREAD
}
#Override
protected void onPostExecute(JSONObject jsonObject) {
super.onPostExecute(jsonObject);
if(this.callBack != null)
this.callBack.sync(jsonObject);// synchronize with MAIN LOOP THREAD
}
#Override
protected void onCancelled() {
super.onCancelled();
if(this.callBack != null)
this.callBack.cancel();
}
}
And usage example:
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Context _localContext = getContext();
SomeAsyncTask.CallBack someCallBack = new SomeAsyncTask.CallBack() {
#Override
public void async(JSONObject jsonResult) {//async thread
//some async process, e.g. send data to server...
}
#Override
public void sync(JSONObject jsonResult) {//sync thread
//get result...
//get some resource of Activity variable...
Resources resources = _localContext.getResources();
}
#Override
public void progress(Integer... status) {//sync thread
//e.g. change status progress bar...
}
#Override
public void cancel() {
}
};
new SomeAsyncTask( someCallBack )
.execute("someParams0", "someParams1", "someParams2");
}
Probably going overboard a bit but i provided call backs for both the execution code and the results. obviously for thread safety you want to be careful what you access in your execution callback.
The AsyncTask implementation:
public class AsyncDbCall<ExecuteType,ResultType> extends AsyncTask<ExecuteType, Void,
ResultType>
{
public interface ExecuteCallback<E, R>
{
public R execute(E executeInput);
}
public interface PostExecuteCallback<R>
{
public void finish(R result);
}
private PostExecuteCallback<ResultType> _resultCallback = null;
private ExecuteCallback<ExecuteType,ResultType> _executeCallback = null;
AsyncDbCall(ExecuteCallback<ExecuteType,ResultType> executeCallback, PostExecuteCallback<ResultType> postExecuteCallback)
{
_resultCallback = postExecuteCallback;
_executeCallback = executeCallback;
}
AsyncDbCall(ExecuteCallback<ExecuteType,ResultType> executeCallback)
{
_executeCallback = executeCallback;
}
#Override
protected ResultType doInBackground(final ExecuteType... params)
{
return _executeCallback.execute(params[0]);
}
#Override
protected void onPostExecute(ResultType result)
{
if(_resultCallback != null)
_resultCallback.finish(result);
}
}
A callback:
AsyncDbCall.ExecuteCallback<Device, Device> updateDeviceCallback = new
AsyncDbCall.ExecuteCallback<Device, Device>()
{
#Override
public Device execute(Device device)
{
deviceDao.updateDevice(device);
return device;
}
};
And finally execution of the async task:
new AsyncDbCall<>(addDeviceCallback, resultCallback).execute(device);
Hope you been through this , if not please read.
https://developer.android.com/reference/android/os/AsyncTask
Depending on the nature of result data, you should choose best possible option you can think of.
It is a great choice to use an Interface
some other options would be..
If the AsyncTask class is defined inside the very class you want to
use the result in.Use a static global variable or get() , use it from
outer class (volatile variable if necessary). but should be aware of the AsyncTask progress or should at least make sure that it have finished the task and result is
available through global variable / get() method. you may use
polling, onProgressUpdate(Progress...), synchronization or interfaces (Which ever suits best for you)
If the Result is compatible to be a sharedPreference entry or it is okay to be saved as a file in the memory you could save it even from
the background task itself and could use the onPostExecute() method
to get notified when the result is available in the memory.
If the string is small enough, and is to be used with start of an
activity. it is possible to use intents (putExtra()) within
onPostExecute() , but remember that static contexts aren't that safe
to deal with.
If possible, you can call a static method from the
onPostExecute() method, with the result being your parameter

AsyncTask as Inner class and static field issue

I have a method searchPlace() that updates a static Arrays of custom Place Object in a class A (FindItOnMap) with a google map, and a method updateMap() that updates the various geopoints .
I invoke these methods Button.onClick and all works properly.
Since these methods use internet data this operation could take a while, I have been looking for the implementation of an inner class B(YourCustomAsyncTask) inside the class A that extends AsyncTask to show a waiting dialog during the processing of these two methods
An user suggested a solution in this form (that apparently seems valid):
public class FindItOnMap extends MapActivity {
static Place[] foundResults;
private ProgressDialog dialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ricerca_condominio);
mapView = (MapView)findViewById(R.id.mapView);
...........
((ImageButton) findViewById(R.id.btSearch)).setOnClickListener(mSearchListenerListener);
}
OnClickListener mSearchListener = new OnClickListener() {
public void onClick(View v) {
String location=editorLocation.getText().toString();
String name=editorName.getText().toString();
//Call the AsyncTask here
new YourCustomAsyncTask().execute(new String[] {name, location});
}
};
private class YourCustomAsyncTask extends AsyncTask <String, Void, Void> {
#Override
protected void onPreExecute() {
dialog = new ProgressDialog(Main.this);
dialog.setMessage("Loading....");
dialog.setIndeterminate(true);
dialog.setCancelable(true);
dialog.show(); //Maybe you should call it in ruinOnUIThread in doInBackGround as suggested from a previous answer
}
#Override
protected Void doInBackground(String... strings) {
try {
search(strings[0], string[1]);
return null;
} catch(Exception e) {
}
}
#Override
protected void onPostExecute(Void params) {
updateMapWithResult();
dialog.dismiss();
//result
}
.....
}
The waiting dialog is showed and the methods are invoked in background,
However for some strange reason the static list foundResults results filled with various null items...
How is this possible?
If I invoke the method search(location, name) outside the inner class all works properly and updateMapWithResult(); updates all geopoint, so these two methods are ok. Only if I try to invoke this in the inner class the json calls seem to be working but the static variable foundResults is filled with null elements and the program doesn't work properly.
Any suggestion?
I have understand where is the problem.
You have to run the search method on the UI thread.
So change this code block:
#Override
protected Void doInBackground(String... strings) {
try {
search(strings[0], string[1]);
return null;
} catch(Exception e) {
}
}
with this
#Override
protected Void doInBackground(final String... strings) {
try {
runOnUiThread(new Runnable() {
public void run() {
search(strings[0], string[1]);
return null;
}
});
} catch(Exception e) {
e.printStackTrace();
}
}
And all should works correctly.
Here is one problem:
OnClickListener mSearchListener = new OnClickListener() {
public void onClick(View v) {
String Location=editorLocation.getText().toString();
String name=editorName.getText().toString();
//Call the AsyncTask here
new YourCustomAsyncTask().execute(new String[] {name, location});
}
Your Location should be location.
Also here:
#Override
protected Void doInBackground(String... strings) {
try {
search(strings[0], string[1]);
} catch(Exception e) {
}
}
#Override
protected void onPostExecute(Void params) {
updateMapWithResult();
dialog.dismiss();
//result
}
In doInBackground you don't assign a value after you search. You might try this:
#Override
protected Void doInBackground(String... strings) {
try {
search(strings[0], string[1]);
String name = string[0];
String location = string[1]
} catch(Exception e) {
}
}
Or something else that will assign value while it runs. As it is, it appears that you just search, and then nothing else.
The reason foundResults is null is because you don't ever assign it a value.
There is nothing wrong with your AsyncTask. Please include the search() method.

Categories

Resources