I have a set of APIs which are implemented using AsyncTask. Some of them have different signature( Some have progress reporting, some others have different datatype being sent as Params). But, all of these APIs return a boolean Result. On success, app Logic for successful calling of API is done. On failure, a generic error popup with error message is shown. Now I want to derive a class from AsyncTask in such a way that it implements a function onSuccessResult as well as overrides a function onFailureResult.
//I get error Params, Progress not recognized.
public class ServerAPIAsyncTask extends AsyncTask<Params, Progress, Boolean>{
abstract public void onSuccessResult();
public void onFailureResult() {
int err = getErrorCode();
showPopup(err);
}
#override
protected void onPostExecute(final Boolean success) {
if (success)
onSuccessResult();
else
onFailureResult();
}
}
Please note that I have to do all of this with two generic datatypes Params and Progress. How can I achieve this? I want to achieve this for two reasons. First I want to derive from this new class like this:
public class getCarDetailAPITask extends ServerAPIAsyncTask<Garage, void, Boolean> {
#Override
protected Boolean doInBackground(Void... params) {
//call my api
}
#Override
protected void onPostExecute(final Boolean success) {
super.onPostExecute(success);
}
#Override
public void onFailureResult() {
super.onFailureResult();
}
#Override
public void onSuccessResult() {
//Do app logic
}
}
Secondly, it helps me to keep the onFailureResult logic at one place thus, not repeating it over and again.
For Params, Progress and Result you need to pass in actual classes when you extend AsyncTask. Since you don't have classes in your class-path that match the names Params and Progress you get these errors.
public class ServerAPIAsyncTask extends AsyncTask<Void, Void, Boolean>{
abstract public void onSuccessResult();
public void onFailureResult() {
int err = getErrorCode();
showPopup(err);
}
protected void onPostExecute(final Boolean success) {
if (success)
onSuccessResult();
else
onFailureResult();
}
}
For your second AsyncTask you should extend AsyncTask, not your own derived ServerAPIAsyncTask. Also, the first Parameter Garage needs to match the parameter you pass into doInBackground, see below:
public class GetCarDetailAPITask extends AsyncTask<Garage, Void, Boolean> {
#Override
protected Boolean doInBackground(Garage... params) {
//call my api
}
protected void onPostExecute(final Boolean success) {
super.onPostExecute(success);
}
#Override
public void onFailureResult() {
super.onFailureResult();
}
#Override
public void onSuccessResult() {
//Do app logic
}
}
According to your comment, there you have generic AsyncTask example:
public class MyAsyncTask<A,B> extends AsyncTask<A, Void, B> {
#Override
protected B doInBackground(A... params) {
return null;
}
// Other methods
}
You said this
//I get error Params, Progress not recognized.
public class ServerAPIAsyncTask extends AsyncTask<Params, Progress, Boolean>
Params and Progress are not real classes. They need to be real classes present in your package.
Some of them have different signature( Some have progress reporting, some others have different datatype being sent as Params).
Different datatype being sent as param? So set params to Object type. It is the superclass of all classes.
See this example, taken from AsyncTask documentation itself:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Related
I'm new to Room and i'm trying to query my database to get a row from it. I attempted doing so by querying it with the primary key which is id but the problem is i don't know how to return the target object from the repository.
This is the Dao
#Query("SELECT * FROM targets WHERE id = :id LIMIT 1")
Targets findTargetById(int id);
THis is the Repository class
public Targets findTarget (int id) {
new findTargetByIDAsyncTask(mTargetsDao).execute(id);
}
private static class findTargetByIDAsyncTask extends AsyncTask<Integer, Void, Targets> {
private TargetsDao mAsyncTaskDao;
findTargetByIDAsyncTask(TargetsDao dao) {
mAsyncTaskDao = dao;
}
#Override
protected Targets doInBackground(Integer... integers) {
return mAsyncTaskDao.findTargetById(integers[0]);
}
#Override
protected void onPostExecute(Targets targets) {
super.onPostExecute(targets);
}
}
You have two ways to return a result.
The first way is to call AsyncTask.get() method, but it will still hold a MainThread what leads to ANR if a task will longer than 5 seconds:
public Targets findTarget (int id) {
return new findTargetByIDAsyncTask(mTargetsDao).execute(id).get();
}
The second way is more complicated but it will not hold the MainThread. You should add a Callback class:
public interface Callback {
void onSuccess(Targets targets);
}
Each method of your repository will look like that:
public void findTarget (Callback callback, int id) {
new findTargetByIDAsyncTask(mTargetsDao, callback).execute(id);
}
And AsynTask will look like that:
private static class FindTargetByIDAsyncTask extends AsyncTask<Integer, Void, Targets> {
private final TargetsDao mAsyncTaskDao;
private final Callback callback;
FindTargetByIDAsyncTask(TargetsDao dao, Callback callback) {
mAsyncTaskDao = dao;
this.callback = callback;
}
#Override
protected Targets doInBackground(Integer... integers) {
return mAsyncTaskDao.findTargetById(integers[0]);
}
#Override
protected void onPostExecute(Targets targets) {
callback.onSuccess(targets);
}
}
The point is to get the data/object from a background thread. You can use Android's AsyncTask or a ExecutorService. A simple example is if you want to get a String of a user name the method will be:
private String getName() {
String name = null;
try {
name = Executors.newSingleThreadExecutor().submit(() ->
userDao.fetchUserName()).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return name;
}
I successfully created a multiple photo uploader. It works as planned. Except I would like the ability to cancel the upload if I need to.
I know I can call cancel() on an AsyncTask if I need to cancel it.
What I would like to do is keep a reference to the AsyncTask in my photo upload object, so it can be referenced to cancel if necessary.
Doing the below works, but I can't get a reference to the async task.
UploadItem item = new UploadItem();
item.setItemFilePath(imageLocation);
item.setTaskIdentifier(UUID.randomUUID().toString());
new UploadTask().execute(item);
But I want to keep a reference to the task should I need to cancel it. So I did something like this but it crashes the app.
//Create new upload item
UploadItem item = new UploadItem();
item.setItemFilePath(imageLocation);
item.setTaskIdentifier(UUID.randomUUID().toString());
item.setAsyncTask(new UploadTask());
//Start the upload
item.getAsyncTask().execute(this);
How do you keep a reference to an Async Task in a custom object?
My custom upload object.
public class UploadItem implements Serializable {
public AsyncTask mAsyncTask;
public String itemFilePath = "";
public String taskIdentifier = "";
public boolean isUploading = false;
public AsyncTask getAsyncTask() {
Log.d("Upload Item", "Getting upload task");
return mAsyncTask;
}
public void setAsyncTask(AsyncTask asyncTask) {
Log.d("Upload Item", "Setting upload task");
this.mAsyncTask = asyncTask;
}
public String getItemFilePath() {
return itemFilePath;
}
public void setItemFilePath(String itemFilePath) {
this.itemFilePath = itemFilePath;
}
public boolean isUploading() {
return isUploading;
}
public void setUploading(boolean uploading) {
isUploading = uploading;
}
public String getTaskIdentifier() {
return taskIdentifier;
}
public void setTaskIdentifier(String taskIdentifier) {
this.taskIdentifier = taskIdentifier;
}
}
The background task:
public class UploadTask extends AsyncTask<UploadItem, Integer, HashMap<String, String>> {
#Override
protected void onPreExecute() { }
#Override
protected void onProgressUpdate(Integer... values) { }
#Override
protected void onPostExecute(HashMap<String, String> r) { }
}
I would like to use interface to implement the communicate of passing data from fragments to activity's button which contains onClick event. I can see HashMap can write the data which is valid on editText field, but those value cannot be sent to activity. it shows error and stopped once I trigger the onClick event on activity.
I was confused on the usage of the interface. The errors as below appears after trying on debugging, waste around 3 days to handle on it and still cannot be resolve. Can anyone recommend or discuss how to solve it, thank you.
The errors:
Error:(77, 5) error: method does not override or implement a method from a supertype
Error:(39, 8) error: Fragment_step_2 is not abstract and does not override abstract method onPassValueStep2() in onPassValue2
Error:(231, 32) error: method onPassValueStep1 in class Fragment_step_1 cannot be applied to given types;
required: HashMap
found: no arguments
reason: actual and formal argument lists differ in length
Error:(232, 32) error: method onPassValueStep2 in class Fragment_step_2 cannot be applied to given types;
required: HashMap
found: no arguments
reason: actual and formal argument lists differ in length
Error:(78, 5) error: method does not override or implement a method from a supertype
Error:(36, 8) error: Fragment_step_1 is not abstract and does not override abstract method onPassValueStep1() in onPassValue
Main activity:
public interface onPassValue{
Map<Object, String> onPassValueStep1();
}
public interface onPassValue2{
Map<Object, String> onPassValueStep2();
}
protected void onCreate(Bundle savedInstanceState) {
......
btn_sendInsureInfo.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view){
//CALL here
//Fragment_step_1.onPassValueStep1();
//Fragment_step_2.onPassValueStep2();
......
}
}
......
Fragment_step_1: (xxx is activity's name)
public class Fragment_step_1 extends Fragment implements xxx.onPassValue {
......
HashMap insureApplicant = new HashMap<>(4);
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public void onAttach(Context xxx){
super.onAttach(xxx);
/*try {
passValue = (onPassValue) xxx;
} catch (ClassCastException e) {
throw new ClassCastException(pingan_insure_info.toString()
+ " didn't implement onPassValue");
}*/
}
#Override
public Map<Object, String> onPassValueStep1(HashMap insureResult) {
for (Object key : insureResult.entrySet()) {
//System.out.println(key + " fragment_1 : " + insureResult.get(key));
System.out.println(" fragment_1 : " + key);
Log.e("Hashmap", String.valueOf(insureResult));
}
return insureResult;
}
......
Fragment_step_2: (xxx is activity's name)
public class Fragment_step_2 extends Fragment implements xxx.onPassValue2{
......
RelativeLayout correspondence;
HashMap insureApplicant2 = new HashMap<>(3);
#Override
public void onAttach(Context pingan_insure_info){
super.onAttach(pingan_insure_info);
/*try {
passValueStep2 = (onPassValueStep2) xxx;
} catch (ClassCastException e) {
throw new ClassCastException(xxx.toString()
+ " didn't implement onPassValue");
}*/
}
#Override
public Map<Object, String> onPassValueStep2(HashMap insureApplicantStep2){
for (Object key : insureApplicantStep2.entrySet()) {
System.out.println("fragment_2 : " + key);
Log.e("Hashmap2", String.valueOf(insureApplicantStep2));
}
return insureApplicant2;
}
All fragments' editText will be filled after the editText is valid and typing by user and send to the function and stored in HashMap.
For example: (AddTextChangedListener with TextWatcher)
residentAddress.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
#Override
public void afterTextChanged(Editable editable) {
residentAddress.setOnFocusChangeListener(new View.OnFocusChangeListener(){
#Override
public void onFocusChange(View view, boolean isFocus){
if(!isFocus){
if("".trim().equals(residentAddress.getText().toString())){
rAddress.setError("Resident Address is required.");
strAddress = "";
insureApplicant2.put(2, strAddress);
} else {
rAddress.setErrorEnabled(false);
rAddress.setError(null);
strAddress = residentAddress.getText().toString().trim();
insureApplicant2.put(2, strAddress);
onPassValueStep2(insureApplicant2);
}
}
}
});
}
});
The signature in the Fragment of your interface's methods are wrong. You declared the interface like:
public interface onPassValue{
Map<Object, String> onPassValueStep1();
}
public interface onPassValue2{
Map<Object, String> onPassValueStep2();
}
and in the Fragment you have public
public Map<Object, String> onPassValueStep1(HashMap insureResult) {
Map<Object, String> onPassValueStep2(HashMap insureApplicantStep2){
as you can notice, the methods declared in your interfaces have no parameters.
You can either change your the method declaration in your interface, adding the missing parameter, or change the methods in the Fragment
I have these two methods declared:
private Result mResult;
private void setResult(Result result){
this.mResult = result;
}
private Result getResult(){
new Executor(new OnResultListener() {
#Override
public void onResult(Result result) {
setResult(result);
}
}).execute();
return mResult;
}
Im using an interface while an AsyncTask is executing. What my problem is, is that I want to return the Result object of the onResult method as an object to the getResult() method.
As shown above, I tried to set it through a setter, but it seems that this is not working.
How can I succeed that?
Thanks in advance!
You have two options here. The bad one is to wait until the new thread will finish. let's don't do that). the better way is to use a callback for:
public static interface OnResultCallback {
void onResult(Result result);
}
private void getResult(final OnResultCallback callback){
new Executor(new OnResultListener() {
#Override
public void onResult(Result result) {
setResult(result);
callback.onResult(result);
}
}).execute();
}
You could provide an instance of OnResultListener as part of the constructor of your AsyncTask, which the caller has to implement. E.g.
private Result mResult;
private OnResultListener mListener;
private void setResult(Result result, OnResultListener listener){
this.mResult = result;
mListener = listener;
}
private Result getResult(){
new Executor(new OnResultListener() {
#Override
public void onResult(Result result) {
if (mListener != null) {
mListener.onResult(result);
}
setResult(result);
}
}).execute();
return mResult;
}
or you could directly provide mListener to new Executor
public class CustomOnResultListener extends OnResultListener{
Callback callback ;
public CustomOnResultListener(Callback callback){}
this.callback =callback; // use this callback to send result
}
public interface Callback{public void onCallback(Result result);};
I have requests to web service implemented using AsyncTask. Normally callback to Activity i do using interface:
public interface AsyncTaskComplete<T> {
public void onTaskComplete(T result);
public void onTaskFailed(T result);
}
In AsyncTask:
private AsyncTaskComplete<String> callback;
public AsyncTaskAbout(AsyncTaskComplete<String> cb) {
this.callback = cb;
}
protected void onPostExecute(String result) {
if(result == null){
callback.onTaskFailed(result);
}else{
callback.onTaskComplete(result);
}
}
But if i want to give callback FragmentActivity compiler suggest me to change AsyncTask constructor to:
public AsyncTaskAbout(FragmentActivity faActivity) {
this.callback = (AsyncTaskComplete<String>) faActivity;
}
Where is warning:
Type safety: Unchecked cast from FragmentActivity to AsyncTaskComplete<String>
So what i need to change ?
Or how to make callback in this case ?
Thanks.