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.
Related
I'm trying to create couple of Java class to perform certain work. Let's say I want to get the task done by calling my classes like this:
FirebaseAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
FirebaseUser user = task.getResult().getUser();
// ...
} else {
// Sign in failed, display a message and update the UI
Log.w(TAG, "signInWithCredential:failure", task.getException());
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
// The verification code entered was invalid
}
}
}
});
I could understand up to signInWithCredential(). I can't figure out how to implement addOnCompleteListener() and have a interface as argument.
I've currently create my top class like FirebaseAuth with methods like getInstance () and signInWithCredential(). Also, I tried creating an interface but I am getting error that result of the interface is never used. How can I implement the style of addOnCompleteListener(parameter 1, interface 2).
Here, addOnCompleteListener is getting parameters of activity and interface and in my case, I will be using the activity parameter for some work.
P.S: I found out this is called interface callback. If it's right, any guidance to it's structure will be great
You can do it like this:
Create an interface:
public interface onCompleteListener {
void onComplete(MyTask object);
}
Define your MyTask class:
public abstract class MyTask {
public abstract boolean someFunc1();
public abstract String someFunc2();
public abstract String someFunc3();
}
In your main class:
public class MainClass{
public static MainClass instance;
private static Activity mActivity;
public onCompleteListener onCompleteListener;
private MainClass(Activity activity) {
mActivity = activity;
}
public static synchronized MainClass getInstance(Activity activity) {
if (instance == null) {
instance = new MainClass(activity);
}
return instance;
}
public void addOnCompleteListener(#NonNull onCompleteListener var2) {
onCompleteListener = var2;
//Call your task function
doTask();
}
public void doTask(){
MyTask o = new MyTask() {
#Override
public boolean someFunc1() {
return true;
}
#Override
public String someFunc2() {
return "";
}
#Override
public String someFunc3 {
return "";
}
};
//Once done, pass your Task object to the interface.
onCompleteListener.onComplete(o);
}
}
Usage:
MainClass.getInstance(MainActivity.this).addOnCompleteListener(new onCompleteListener() {
#Override
public void onComplete(MyTask object) {
doYourWork(object);
}
});
I'm implementing LoaderManager.LoaderCallbacks on my MainActivity and I'm overriding onCreateLoader. In the onCreateLoader I simply return a AsyncTaskLoader object, where I override the onStartLoading method, in which I check if the query is null. For this code works, I need to call forceLoad(). Here is a snippet of the code:
#Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
return new AsyncTaskLoader<String>(this) {
#Override
protected void onStartLoading() {
// No need to peform a query if no arguments were passed
if (args == null) {
return;
}
// This needs to be called!!
forceLoad();
}
#Override
public String loadInBackground() {
/* some code */
}
};
}
The problem is that I don't know why I need to call forceLoad(), because its implementation is a "empty" method. In the source code of the Loader Class, the implementation of forceLoad is:
public void forceLoad() {
onForceLoad();
}
and the implementation of onForceLoad() is:
protected void onForceLoad() {
}
I tried to find some methods that override forceLoad() or onForceLoad in the other parts of the code (I use (getSupportLoaderManager().initLoader(arg1, arg2, arg3)), but until this moment have not succeeded. Why do I have to call forceLoad() and why does it work?
The reason Loader class is having empty implementation of onForceLoad() is that Loader is a base class. Their child classes are supposed to be implementing onForceLoad().
If we will see your code, you are using AsyncTaskLoader which basically a child of Loader so AsyncTaskLoader will have the onForceLoad() implementation which is actually this:
#Override
protected void onForceLoad() {
super.onForceLoad();
cancelLoad();
mTask = new LoadTask();
if (DEBUG) Slog.v(TAG, "Preparing load: mTask=" + mTask);
executePendingTask();
}
Your onCreateLoader()basically should be like this:
public Loader<String> onCreateLoader(int id, final Bundle args) {
AsyncTaskLoader<String> loader = new AsyncTaskLoader<String>(this) {
#Override
protected void onStartLoading() {
// No need to peform a query if no arguments were passed
if (args == null) {
return;
}
}
#Override
public String loadInBackground() {
/* some code */
}
};
loader.forceLoad(); // This basically gonna run the loader.
return loader;
}
We can also override the onStartLoading() method to call forceLoad() which is a required step to actually trigger the loadInBackground() method to execute.
public class Loader extends AsyncTaskLoader<List<data>> {
// Tag for Log messages
private static final String LOG_TAG = Loader.class.getName();
// Query URL
private String mUrl;
/**
* Constructs a new {#link data}
*
* #param context of the activity
* #param url to load the data from
*/
public Loader (Context context, String url) {
super(context);
mUrl = url;
}
#Override
protected void onStartLoading() {
forceLoad();
}
/**
* This is on the background thread
*/
#Override
public List<data> loadInBackground() {
if(mUrl == null) {
return null;
}
// Perform the network request, parse the response and extract a list of data.
List<data> data= QueryUtils.fetchData(mUrl);
return data;
}
}
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 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");
}
}
how can I trigger AsyncTasks that are contained in another activity from my main activity?
public class DatabaseActivity extends Activity {
private class DbReader extends AsyncTask<..> {
#Override
protected List<MyData> doInBackground(..) {
//execute query etc
}
}
private class DbSaver extends AsyncTask<..> {
#Override
protected void doInBackground(MyData data) {
//save to dn
}
}
private class DbRemover extends AsyncTask<..> {
#Override
protected void doInBackground(MyData data) {
//remove in db
}
}
}
How can I trigger from MyApplication extends Actitivy?
i think you should use seperate class where AsyncTask is alone.
when your app needs more than one AsyncTask then you should use seperate AsyncTask and call it.
private class CommonTask extends AsyncTask<..> {
public CommonTask(Foo foo){
}
#Override
protected void doInBackground(MyData data) {
//remove in db
}
}
No you can pass diffrent value for constructor and check what you want from Activity either data save or remove or anything else...