Just to start this isn't a question I need an answer too. But I think it would be an interesting way of making code cleaner.
In some code I am writing Ive built a module for retrieving HTTP requests on a new thread and then returning the result to the main thread using a callback.
Code example:
public interface MyCallback {
void Callback_done(String Data);
}
public void HTTP_process(String get_vars, List<NameValuePair> post_vars,MyCallback callback){
Task = new GET_Task();
Task.get_vars = get_vars;
Task.post_vars = post_vars;
Task.callback = callback;
Task.execute();
}
public class GET_Task extends AsyncTask<String, Integer, String> {
public MyCallback callback;
public String get_vars="";
public List<NameValuePair> post_vars = null;
#Override
protected void onPreExecute() {
super.onPreExecute();
//displayProgressBar("Downloading...");
}
#Override
protected String doInBackground(String... params) {
//CODE HERE
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
callback.Callback_done(result);
}
}
Then to use the above "library":
Globals.mData_Handler.HTTP_process("",null,new Data_Handler.MyCallback() {
public void Callback_done(String Data) {//Callback code here
}
});
When I say shorthand I want to be able to type this instead:
Globals.mData_Handler.HTTP_process("",null,new ^{//Callback code here
});
I know this is how objective C handles callbacks, is there a way to setup things like this in java? Using custom short hand expressions?
Use Java 8 and you get lambda expressions
Globals.mData_Handler.HTTP_process("",null, (String data) -> {
//Callback code here
});
If you're only doing a one-liner you can even omit the braces:
Globals.mData_Handler.HTTP_process("",null, (String data) -> log.info(data));
Of course, the above can also be written using Java8 method references:
Globals.mData_Handler.HTTP_process("",null, log::info);
Related
I want to know how to access the data and bind it to a component in out of the AsyncTask class body?
I have a class like:
class DownloadData extends AsyncTask<String, Void, String> {....}
and it has a method :
#Override
protected String doInBackground(String... params) {
return ....;//return some data
}
I don't understand doInBackground return data to where?
Because when i want to use my class, i use it like:
DownloadData dd = new DownloadData();
dd.execute(...);
can i use it like this? because i want to fetch returned data out of my main class to bind it to some components
DownloadData dd = new DownloadData();
string temp=dd.doInBackground(...);
After doInBackground() your return will be forwarded to onPostExecute().
To use it in your activity refer this link : How to use Async result in UIThread
I can not catch
#Override
protected String doInBackground(String... params) {
return ....;//return some data
}
result from main UI.
You have to use callbacks. For example, you can use interface to obtain the result.
For example create an interface:
public interface IProgress {
public void onResult(int result);
}
create class:
private class DownloadData extends AsyncTask<String, Void, String> {
private IProgress cb;
DownloadData(IProgress progress) {
this.cb = cb;
}
#Override
protected String doInBackground(String... params) {
for (int i = 0; i < 10; i ++) {
if (cb!=nil)
cb.onResult(i);//calls 10 times
}
....
}
...
}
somewhere in code:
DownloadData dd = new DownloadData( new IProgress() {
public void onResult(int result) {
/// this is your callback
//to update mainUI thread use this:
final res = result;
runOnUiThread(new Runnable() {
#Override
public void run() {
//update UI here
textview.setText("" + res);
}
});
}
});
dd.execute(...);
And, as usual, you can update UI after doInBackground via onPostExecute()
If you just want to return a result from your AsyncTask class so that you can update the UI in your activity according to the result you can do something like this:
In your AsyncTask class declare an interface like this:
private AsyncResponse asyncResponse = null;
public DownloadData(AsyncResponse as) {
this.asyncResponse = as;
}
public interface AsyncResponse {
void onAsyncResponse(String result); // might be any argument length of any type
}
and in onPostExecute() :
asyncResponse.onAsyncResponse(result); // result calculated from doInBackground()
and in the activity class:
DownloadData dd = new DownloadData(new AsyncResponse() {...}); // implement onAsyncResponse here
dd.execute();
private class exampleTask extends AsyncTask<String, Void, SomeResult>{
#Override
protected SomeResult doInBackground(String... urls) {
SomeResult res ;
someMethod(new CallBack<T>(){
#Override
public void onResponse(SomeResult something) {
res = something ;
}
#Override
public void onFailure() {
//
}
});
return res ;
}
#Override
protected void onPostExecute() {
//
}
}
Please I want to assign "res" to "something" witch is inside the callback in the onResponse method. In this code it's impossible to assign res inside the onResponse method.
Please any help is welcome.
Thank you :)
my original code : I'd like to assign "url" ;
private class GetBeaconInfosTask extends AsyncTask<String, Void, Call<Url>> {
Url url ;
#Override
protected Call<Url> doInBackground(String... urls) {
ProxService service = ProxService.Factory.makeProxService(ProxService.ENDPOINT);
return service.getUrlDetails(urls[0]);
}
// onPostExecute return the results of the AsyncTask.
#Override
protected void onPostExecute(Call<Url> call) {
call.enqueue(new Callback<Url>() {
#Override
public void onResponse(Call<Url> call, Response<Url> response) {
url = response.body();
}
#Override
public void onFailure(Call<Url> call, Throwable t) {
//
}
});
if(url == null){
Log.i("url is null", "url is null !!!!! ....");
}
else {
setShopLogo(url.icon) ;
setShopName(url.title);
setDescription(url.description);
setlongUrl(url.longUrl); }
}
}
Assuming the problem is due to the variable having to be declared as final to be accessible from the callback, you could move it out of the doInBackground method and declare it as a member of the exampleTask class.
private class exampleTask extends AsyncTask<String, Void, SomeResult>{
SomeResult res;
#Override
protected SomeResult doInBackground(String... urls) {
someMethod(new CallBack<T>(){
#Override
public void onResponse(SomeResult something) {
res = something ;
}
#Override
public void onFailure() {
//
}
});
return res ;
}
#Override
protected void onPostExecute() {
//
}
}
While this should answer your question, it's hard to tell what you're trying to achieve. Further to #Gabe Sechan's answer - your variable may never get assigned, or it would be assigned after a certain length of time. Ordinarily you would carry out work in the doInBackground and when that has been carried out onPostExecute is called, but in your case you'll likely have onPostExecute called before hitting the onRespons of your callback. I don't think it's good practice to use a Callback in doInBackground the way you're trying to use it.
If you have other work that needs to be carried out asynchronously in doInBackground, do that, but move the someMethod call to onPostExecute and do whatever you need to do with res in there.
private class exampleTask extends AsyncTask<String, Void, SomeResult>{
SomeResult res;
#Override
protected SomeResult doInBackground(String... urls) {
// background work
}
#Override
protected void onPostExecute() {
someMethod(new CallBack<T>(){
#Override
public void onResponse(SomeResult something) {
res = something ;
// DO STUFF WITH RES HERE AFTER IT'S BEEN ASSIGNED OR YOU WON'T HAVE ACCESS TO IT
}
#Override
public void onFailure() {
//
}
});
}
}
EDIT: Now that the question contains real code, I can provide a flow that should work for you. Note in the code I pasted above the comment in onResponse stating this is where you should have the code that uses the variable. Look at onResponse below for example using your code. The value doesn't exist until you get to the onResponse callback method, so you have to wait until it gets there. Your code below is being carried out before onResponse so the variable is inevitably null. The code isn't executed sequentially. I'd suggest studying callbacks to fully understand them :)
private class GetBeaconInfosTask extends AsyncTask<String, Void, Call<Url>> {
Url url ;
#Override
protected Call<Url> doInBackground(String... urls) {
ProxService service = ProxService.Factory.makeProxService(ProxService.ENDPOINT);
return service.getUrlDetails(urls[0]);
}
// onPostExecute return the results of the AsyncTask.
#Override
protected void onPostExecute(Call<Url> call) {
call.enqueue(new Callback<Url>() {
#Override
public void onResponse(Call<Url> call, Response<Url> response) {
url = response.body();
setShopLogo(url.icon) ;
setShopName(url.title);
setDescription(url.description);
setlongUrl(url.longUrl); }
}
#Override
public void onFailure(Call<Url> call, Throwable t) {
//
}
});
}
You seem to not understand the purpose of a callback. A callback is called at some point in the future. Not now. So the function that sets up the callback won't be assured its called by the end of its execution. What you want will never work. Instead, you should execute whatever you wanted to do in onPostExecute with that value in the body of the callback.
I was wondering if I could change the way my method function, similar to Blocks iOS.
So I have this interface create in class API.java
public interface APIListener {
void apiResponseSuccess(String output);
void apiResponseFailed(String output);
}
public APIListener listener = null;
public void myMethod{
listener.apiResponseSuccess("output");
}
In order to call my interface created, i have to implements
API.APIListener. and override the functions
#Override
public void apiResponseSuccess(Object output) {
Log.i("output from api",(String) output);
}
#Override
public void apiResponseFailed(String output) {
}
And to call it, I have to use :
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
API api = new API();
api.listener = this;
api.myMethod();
}
But drawbacks using this, It's hard to maintain if I call many methods inside the API, because all the results will go to apiResponseSuccess in my class, and have to tag which one coming from. Where the iOS comes with Blocks, it becomes easier. so basically, Is there a way to return the interface methods direct when we call it. similar to this
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
API api = new API();
api.listener = this;
api.myMethod(
public void apiResponseSuccess(Object output) {
Log.i("output from api",(String) output);
}
public void apiResponseFailed(String output) {
}); //so the result will go in separately inside the where the function is called.
}
You can achieve it easily.
Your api method should get APIListener as a parameter - so when you'll call it you'll have something like this:
api.myMethod(new APIListener() {
#Override
public void apiResponseSuccess(Object output) {
Log.i("output from api",(String) output);
}
#Override
public void apiResponseFailed(String output) {
}
});
You can also pass more params of course:
api.myMethod(new APIListener() {
#Override
public void apiResponseSuccess(Object output) {
Log.i("output from api",(String) output);
}
#Override
public void apiResponseFailed(String output) {
}
}, "my String", true);
BUT... notice that with your current implementation that the activity is the listener of your API call you'll have a memory leak!
You can solve it in several ways:
Don't make the listener anonymous ("ios block") but an inner static class that takes the activity as a WeakReference
Encapsulate the WeakReference inside your API and manage your listeners there.
I have the following generally happening for an AsyncTask:
private class backgroundTask extends AsyncTask<Object, Object, Object> {
protected void onPreExecute() {
this.dialog.setMessage("Doing stuff...");
this.dialog.show();
}
#Override
protected Object[] doInBackground(Object... params) {
// stuff happens here
ResultsObject results = new ResultsObject(processValue);
Object[] values = new Object[2];
values[0] = "STATUS STRING";
values[1] = results;
return values;
}
protected void onPostExecute(Object... result) {
Log.d("actions", "onPostExecute");
if (this.dialog.isShowing())
this.dialog.dismiss();
}
}
However, onPostExecute does not appear to be getting triggered. I can validate that everything up until doInBackground makes a return call is getting executed. This has to be something obscenely simple that I'm missing here, but I'm stumped. I have other AsyncTasks in this same project, and I have no trouble with them reaching onPostExecute.
You're overloading onPostExecute method and it doesn't get used. Use this:
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
Log.d("actions", "onPostExecute");
if (this.dialog.isShowing())
this.dialog.dismiss();
}
Note the #Override annotation, it comes very useful in times like this. It will show an error if the method you are trying to override doesn't exist.
I'm new in Android. It's been a few weeks since I started working in this project. Reading the code already made I see a lot of private async tasks doing basically the same (call an API) So I wonder if there is a better solution, my idea would be create a public async task called ApiCaller which will return a JSONObject and the responsible for parsing the JSON would be the class calling the ApiCaller:
public class ApiCaller extends AsyncTask<String, String, JSONObject> {
private static final String TAG = "ApiCall";
private final String apiVersion = "v1";
private final String baseURL = "http://my.api.com/";
private String URL = null;
/**
* Generates the URL to call the API.
*
* #param params List with the params to call the API.
*/
public ApiCaller(ArrayList<NameValuePair> params){
String apiURL = this.baseURL + this.apiVersion + "/?";
String paramsList = URLEncodedUtils.format(params, "utf-8");
this.URL = apiURL + paramsList;
}
#Override
protected JSONObject doInBackground(String ... params) {
Log.i(TAG, "API:");
Log.i(TAG, this.URL);
JSONManager jParser = new JSONManager();
JSONObject jsonObject = jParser.getJSONFromUrl(this.URL);
return jsonObject;
}
Is there a way to return that JSONObject outside of the class so I can do something like:
JSONObject js = apiCaller.execute();
Or any other solution to avoid creating new asynctasks every time I need to call the API? With my current code I can't get it but I don't know what is missing? maybe returning it in onPostExecute?
Some time ago I've asked a similar question
One AsyncTask for multiple Activities
and the solution I found was answered in another question:
Common class for AsyncTask in Android?
Basically, what you need is an interface.
I've going to explain the basics, although you should check the original answer by #SirDarius.
You could create an interface like this:
interface AsyncTaskCompleteListener<T> {
public void onTaskComplete(T result);
}
And implements that interface in all classes you need to use the AsynTask, then, in your generic Asynstask you need to have a callback AsyncTaskCompleteListener and call it from your onPostExecute
class B implements AsyncTaskCompleteListener<JSONObject> {
public void onTaskComplete(JSONObject result) {
// do whatever you need
}
public void launchTask(String url) {
ApiCaller a = new ApiCaller(context, ArrayList<NameValuePair> params, this);
ApiCaller.execute(url);
}
}
class ApiCaller extends AsyncTask<String, Void, String> {
private AsyncTaskCompleteListener<String> callback;
public ApiCaller(Context context, ArrayList<NameValuePair> params, AsyncTaskCompleteListener<String> cb) {
this.context = context;
this.callback = cb;
}
protected void onPostExecute(String result) {
finalResult = result;
progressDialog.dismiss();
System.out.println("on Post execute called");
callback.onTaskComplete(result);
}
}
because the asynchronous AsyncTask you can not call a method and obtain the return value;
you could use a AsyncTask on your inline code and get on postExecute your JSON object:
new AsyncTask<String, String, JSONObject>(){
#Override
protected Void doInBackground(String... params) {
//...
}
#Override
protected void onPostExecute(JSONObject result) {
// ...
}
}.execute(...);
You should implement a listener that you will pass to the async task. When the asynctask finishes, it will call the listener's method:
First, make a listener in a new java file:
public interface onTaskDoneListener{
void onTaskDone(JSONObject result);
}
Pass it to your AsyncTask:
private onTaskDoneListner donelistner;
public ApiCaller(ArrayList<NameValuePair> params,onTaskDoneListener donelistener){
String apiURL = this.baseURL + this.apiVersion + "/?";
String paramsList = URLEncodedUtils.format(params, "utf-8");
this.URL = apiURL + paramsList;
this.donelistener = donelistener;
}
Then, inside the onPostExecute method of your AsyncTask
this.donelistener.onTaskDone(result)
Usage:
new ApiCaller(params,new onTaskDoneListener() {
#Override
public void onTaskDone(JSONObject result) {
//This will be called when the asynctask finishes
//Do something with the result
});
Step 1 : Define an Interface.
public interface WebServiceListener {
public void onLoginActionComplete (ArrayList<String> arrayList);
}
Step 2: Implement the WebServiceListener in your activity class.
public class LoginActivity extends Activity implements WebServiceListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
/* Calling the AsyncTask here,you could call in Login Button Click... */
WebServiceClient httpclient = new WebServiceClient(this);
httpclient.execute(ssoURLStr);
}
#Override
public void onLoginActionComplete (ArrayList<String> arrayList) {
// TODO Auto-generated method stub
/* Read the response */
String Response = arrayList.get(0);
}
}
Step 3 : Your AsynTask class code here.
public class WebServiceClient extends
AsyncTask<String, Integer, ArrayList<String>> {
WebServiceListener listener = null;
/* Constructor added WebServiceListener here */
public WebServiceClient ( WebServiceListener listener)
{
this.listener = listener;
}
#Override
protected ArrayList<String> doInBackground(String... params) {
// TODO Auto-generated method stub
ArrayList<String> arrayList = null;
" write your http code here and get the response and update the
arrayList <String> here"
return arrayList;
}
#Override
protected void onPostExecute(ArrayList<String> arrayList) {
// Returns the contents of the HTML page
listener.onLoginActionComplete (arrayList);
}
}
I have also asked this question. Maybe this link will help