I have the following AsyncTask class which I want to handle multiple api calls and return a List back to my activity. I am having a problems trying to make this work.
How can I return an object back to my activity when onPostExecute() method does not return anything?
public class NetworkCall extends AsyncTask<Call, Void, List<Student>> {
#Override
protected List<Students> doInBackground(Call... calls) {
try {
Call<Students> call = calls[0];
Response<Students> response = call.execute();
return response.body().getStudents();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(List<Students> students) {
}
}
One of the options is to create interface and use as callback.
In async task class :
public class NetworkCall extends AsyncTask<Call, Void, List<Student>> {
public interface NetworkCallback{
void onResponse(List<Students> students);
}
private NetworkCallback callback;
public void setCallback(NetworkCallback callback){
this.callback = callback;
}
#Override
protected List<Students> doInBackground(Call... calls) {
try {
Call<Students> call = calls[0];
Response<Students> response = call.execute();
return response.body().getStudents();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(List<Students> students) {
callback.onResponse(students)
}
}
and now in your activity implement the interface and provide to the async task via setter.
public class StudentsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//.... you setup for activity
NetworkCall networkCall = new NetworkCall();
networkCall.setCallback(new NetworkCall.NetworkCallback() {
#Override
public void onResponse(List<Students> students) {
//update your UI here
}
});
}
}
Based on the Docs onPostExecute runs on the main UI thread so no need to the runOnUiThread and Runnable
you can implement like this:
Your interface:
public interface OnTaskCompleted{
void onTaskCompleted(Object o);
}
Your Activity:
public class YourActivity implements OnTaskCompleted{
// your Activity
#Override
void onTaskCompleted(Object o){
}
And your AsyncTask:
public class YourTask extends AsyncTask<Object,Object,Object>{ //change Object
to required type
private OnTaskCompleted listener;
public YourTask(OnTaskCompleted listener){
this.listener=listener;
}
// required methods
protected void onPostExecute(Object o){
// your stuff
listener.onTaskCompleted(o);
}
}
Related
I'm creating a library in Android and I want to pass some values (strings) from the library to the app that is using the library after some events. The app that uses the library has only one screen to display the strings sent by the library.
My app has a MainActivity that will populate an listView with the events received by the library.
It also have an MyApp that extends Application.
Here I'm doing this:
public class MyApp extends Application{
private static MyApp sMyApp;
public MyApp() {
}
#Override
public void onCreate() {
super.onCreate();
sMyApp= this;
MyLibrary.getInstance().setApplication(sMyApp);
}
public static MyApp getInstance() {
return sMyApp;
}
}
In my library:
public class MyLibrary {
private static MyLibrary sInstance = new MyLibrary();
private Application mMyApp;
public static MyLibrary getInstance() {
return sInstance;
}
private MyLibrary() {
}
public void setApplication(Application myApp) {
mMyApp = myApp;
}
public void sendEventMessage(String message) {
mMyApp.setEvent(message);
}
}
I've tried to implement an interface in MainActivity so that mMyApp.setEvent(message); could send a message that MainActivity could receive but with no success.
How can I achieve what I pretend?
You could try callback as this sample:
Firstly, declare your callback inside library
public interface ILibrary {
public void onStart(String msg);
public void onProcess(String msg);
public void onFish(String msg);}
public class SDK {
private static final String START = "I'm started";
private static final String PROCESSING = "I'm running";
private static final String STOP = "I'm done";
private ILibrary mCallback;
// Make it easy for demo
public SDK(ILibrary callback) {
mCallback = callback;
}
public void start() {
mCallback.onStart(START);
}
public void process() {
mCallback.onProcess(PROCESSING);
}
public void stop() {
mCallback.onFish(STOP);
}}
And then do something like that:
SDK sdk = new SDK(new ILibrary() {
#Override
public void onStart(String msg) {
Log.d("TAG", msg);
}
#Override
public void onProcess(String msg) {
Log.d("TAG", msg);
}
#Override
public void onFish(String msg) {
Log.d("TAG", msg);
}
});
AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
sdk.process();
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
sdk.stop();
}
#Override
protected void onPreExecute() {
super.onPreExecute();
sdk.start();
}
};
asyncTask.execute();
I am new in Android development. Is there any way to get result from an AsyncTask in a non-activity class? I know about the standard procedure of using an interface and getting the parsed result from the onPostExecute. But this is not working, using context instead of activityname.this in the non activity class(where I sent the context as a parameter). I am building a library and it is required for that. Any help would be greatly appreciated.
in MainActivity.java --->
Library l1 = new Library();
l1.init(portalHitter, "security certificate file name");
if(l1.getLoginStatus(MainActivity.this)){
start intent to go to another activity
}
in Library.java --->
CommonMethods commonMethods;
public void init(String portalHitter, String certName){
.....
}
public boolean getLoginStatus(Context context){
if(clint is initialized){
commonMethods = new CommonMethods(context, CommonValues.LOGIN_REQUEST);
}else{
/* error */
}
if(CommonValues.LOGIN_STATUS)
return true;
else
return false;
}
in CommonMethods.java --->
public CommonMethods(Context context, int reqest_code){
this.context = context;
this.request_for_which_service = reqest_code;
executeService();
}
public void executeService(){
switch(request_for_which_service){
case CommonValues.LOGIN_REQUEST:
loginAsyncTask = new LoginAsyncTask(context, params ...);
loginAsyncTask.execute();
loginAsyncTask.delegate = context;
break;
}
}
in LoginAsyncTask.java --->
public class LoginAsyncTask extends AsyncTask<String, String, String>{
...
public LoginCompleteInterface delegate = null;
public LoginAsyncTask(Context context, params...){
...
}
doInBacground(){
..do work..
return response;
}
protected void onPostExecute(String result) {
super.onPostExecute(result);
if(exception_identifier == 0 || exception_identifier == 1 || exception_identifier == 2){
/* display dialog for exception on timeout, socket exceptions etc */
}else{
String tempLoginStatus = loginStatus(result);
delegate.loginCompleted(tempLoginStatus);
loginProgress.dismiss();
}
}
loginStatus(String result){
// parse and return success and failure //
}
...
}
and in the LoginCompleteInterface.java --->
public interface LoginCompleteInterface {
void loginCompleted(String output);
}
Now, if commonmethods was an activity, then it did not create a problem, but now it is creating problem, even after passing the context from MainActivity.java
loginAsyncTask.delegate = context; line.
You can use Event Bus for same .
You can set a bus and subscribe to a class where you want to have the callback.
Some common Event Bus are
TinyBus
Otto
You can get with the help of Interface.
Create an Interface which has method getResponse(String data).
implements Interface to your Activity.
when you call AsyncTask from Activity pass then reference of Interface.
In AsyncTask onPostExecute() call then Interface method and pass the data as parameter.
you will get the data in override method of interface in Activity.
Code
Interface
public interface MyInterface {
public void getResponse(String data);
}
Activity Class:
public class MainActivity extends AppCompatActivity implements MyInterface {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// CAll Async Task
new AsyncTaskClass(this).execute();
}
#Override
public void getResponse(String data) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
}
AsyncTask Class
public class AsyncTaskClass extends AsyncTask<String, String, String> {
private MyInterface mInterface;
public AsyncTaskClass(MyInterface reference) {
mInterface = reference;
}
#Override
protected String doInBackground(String... params) {
return "this data";
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// call getResponse(_) of interface.
mInterface.getResponse(s);
}
}
----------------------For non-Activity Class--------------
public class SimpleClass implements MyInterface
{
public void someMethod()
{
// CAll Async Task
new AsyncTaskClass(this).execute();
}
}
Interface and AsyncTask class will be the same.
I use Retrofit. Sometime I want integrate with AsyncTask for some purpose like Dialog Loading...
Here is my way:
public class RetrofitAsyncTask<T extends ResponseModel> {
public interface OnCompleteListener<T extends ResponseModel> {
public void onComplete(T t);
public void onError(Exception e);
}
public interface JsonConverter<T extends ResponseModel> {
public T getModel();
}
protected OnCompleteListener<T> onCompleteListener = null;
protected JsonConverter<T> jsonConverter;
protected ProgressDialog progressDialog;
protected int requestCode = Integer.MIN_VALUE;
public static RetrofitAsyncTask newInstance() {
return new RetrofitAsyncTask();
}
public RetrofitAsyncTask setOnCompleteListener(OnCompleteListener<T> l) {
onCompleteListener = l;
return this;
}
public RetrofitAsyncTask setJsonConverter(JsonConverter<T> converter) {
jsonConverter = converter;
return this;
}
public RetrofitAsyncTask setProgressDialog(ProgressDialog dlg) {
progressDialog = dlg;
return this;
}
public RetrofitAsyncTask setRequestCode(int reqCode) {
requestCode = reqCode;
return this;
}
public void execute() {
new AsyncTask<Void, Void, Exception>() {
private T result;
#Override
protected ProgressDialog getDialogRunOnPreStart() {
/** Retrun dialog will display. Will process on onPre & onPost. But current I will ignore it.**/
return progressDialog;
}
#Override
protected Exception doInBackground(Void... params) {
if (jsonConverter == null) {
return null;
}
try {
result = jsonConverter.getModel();
result.setRequestCode(requestCode);
} catch (Exception e) {
return e;
}
return null;
}
#Override
protected void onPostExecute(Exception e) {
if (onCompleteListener != null) {
if (e != null || result == null) {
onCompleteListener.onError(e);
} else {
onCompleteListener.onComplete(result);
}
}
}
}.execute();
}
}
In this case class ResponseModel is Empty class.
public class ResponseModel {/** You will custom some filed default here **/}
Next you will define some Service Api.... look like:
public interface MyService {
#GET("/path")
public User getUserInfo(....);
}
(Notes class User Must be extend from ResponseModel).
Then you will custom some RetrofitAdapter...
Finally to use RetrofitAsyncTask you will do something like:
RetrofitAsyncTask.newInstance()
.setJsonConverter(
new RetrofitAsyncTask.JsonConverter<User>() {
#Override
public User getModel() {
return MyService.getUser(...);
}
}
)
.setOnCompleteListener(this)
.setRequestCode(requestCode)
.execute();
That's way I done. But current I feel it not good.
(setJsonConverter get much line :|, If you have any idea to make it better & shorter please comment ! Thanks so so much !)
I would like to ask assistance for my error. I used robospice-retrofit for my api and I want to get the cache. In my other sample program the I could get the cache and the value but when i used it in the fragment, I always have a null value of my cache. Btw, I created another class that would handle all my request, is there a problem with this?
Please check my codes: This is created from separate class.
public void roboretroGetTempString(final DomainResponseCallback callback,SpiceManager spiceManager){
boolean isHasCache = false;
TestStringRequest graphRequest = new TestStringRequest();
try {
if(spiceManager.isDataInCache(String.class,"graph",DurationInMillis.ONE_MINUTE).get()){
Log.e(TAG,"onRefresh:Has Cache"+spiceManager.getDataFromCache(String.class,"graph"));
}else{
/*Execute if cache has expired*/
Log.e(TAG,"onRefresh:No Cache");
spiceManager.execute(graphRequest, "graph", DurationInMillis.ONE_MINUTE, new RequestListener<String>() {
#Override
public void onRequestFailure(SpiceException spiceException) {
spiceException.printStackTrace();
callback.onApiResponse("Error", null);
}
#Override
public void onRequestSuccess(String str) {
Log.e(TAG,"onRequestSuccess:result-"+str);
}
});
}
} catch (CacheCreationException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (CacheLoadingException e) {
e.printStackTrace();
}
}
This is the code in my Fragment: DomainResponseCallback is my callback interface. I also passed the SpiceManager as a parameter that I used in the code above.
private void roboLoadTest(){
GraphController graphController = new GraphController();
graphController.roboretroGetTempString(new DomainResponseCallback() {
#Override
public void onApiResponse(String result, Object obj) {
progressBar.setVisibility(View.GONE);
Log.e(TAG,"onApiResponse-"+result);
}
#Override
public void onApiError(String error) {
Log.e(TAG,"onApiResponse-"+error);
}
},getSpiceManager());
}
This is How I set up my Fragment based on the Sample Code and extends my Fragment to this.
public class RoboFragment extends Fragment{
private String TAG = RoboFragment.class.getSimpleName();
/***
* With {#link com.octo.android.robospice.UncachedSpiceService} there is no cache management. Remember to declare it in
* AndroidManifest.xml
*/
private SpiceManager spiceManager = new SpiceManager(RoboSpiceService.class);
#Override
public void onStart() {
super.onStart();
spiceManager.start(getActivity());
}
#Override
public void onStop() {
// Please review https://github.com/octo-online/robospice/issues/96 for the reason of that
// ugly if statement.
if (spiceManager.isStarted()) {
spiceManager.shouldStop();
}
super.onStop();
}
protected SpiceManager getSpiceManager() {
return spiceManager;
}
}
This is my TestRequest`
public class TestStringRequest extends RetrofitSpiceRequest<String,RetrofitApi> {
public TestStringRequest() {
super(String.class, RetrofitApi.class);
}
#Override
public String loadDataFromNetwork() throws Exception {
return getService().getTestRetrofit();
}
}
My RetrofitApi
#GET(api_reciever+"mytestretrofit")
public String getTestRetrofit();
I don't have any idea on whats missing on my code.
Looking forward for your assistance.
A new spice manager is created/started every time a new fragment is open and that spice manager also will close. That is why you will have an empty/null cache. Check this [post].1
In my scenario, I just created a singleton extends with application and created that spicemanager in my main activity(the parent of the fragments). And It works. I don't know if this is the best solution, still searching for more better approach.
public class AppSingleton extends Application{
private static AppSingleton ourInstance = new AppSingleton();
private SpiceManager spiceManager= new SpiceManager(RoboSpiceService.class);
public static AppSingleton getInstance() {
return ourInstance;
}
public SpiceManager getSpiceManager() {
return spiceManager;
}
}
I have a class "HomeActivity", which is as follows:
public class HomeActivity extends FragmentActivity implements OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
// Create the list fragment and add it as our sole content.
if (fm.findFragmentById(android.R.id.content) == null) {
HomeFragment list = new HomeFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
public static class HomeFragment extends Fragment {
webServiceTask = WebServiceTask.getInstance(
getActivity(), Constants.METHOD_NAME_PRODUCTS,
Constants.PRODUCT_NAME, null);
public void Work() {}
}
}
I have another class WebServiceTask, which is as follows:
final public class WebServiceTask extends AsyncTask<String, String, String> {
private WebServiceTask(final Activity activity, final String methodName,
final String productName, final String addInfo[]) {
super();
this.activity = activity;
this.methodName = methodName;
this.productName = productName;
this.addInfo = addInfo;
}
public static WebServiceTask getInstance(final Activity activity,
final String methodName, final String productName,
final String additionalInfo[]) {
webServiceTask = new WebServiceTask(activity, methodName, productName,
additionalInfo);
return webServiceTask;
}
protected void onPostExecute() {
// Here I am trying to call the work() method in HomeFragment, How can I do that?
}
My question is how can i call the work() method in HomeFragment class from onPostExecute().
I would propose making a listener for you task, and invoke its method in post execute. It will geve you a lot more flexibility and control on what you want to deafter the task finishes. Here is sample code I would use:
public class MyTask extend AsyncTask<Void, Void, Boolean> {
public interface MyTaskListener {
void onSuccess();
void onFailure();
void onError(Throwable t);
}
private Throwable error;
private MyTaskListener listener;
public MyTask(MyTaskListener listener) {
this.listener = listener;
}
#Overrride
public Boolean doInBackground(Void... params) {
try {
if (workCompleted()) {
//work completed without error - return true
return Boolean.TRUE;
} else {
//work failed to complete - return false
return Boolean.FALSE;
}
} catch(Exception e) {
//unexpected error happened - remember error and return null
this.error = e;
return null;
}
}
#Override
public void onPostExecute(Boolean result){
if (!isCancelled()) { //you only want to process if task wasn't cancelled
if (this.error != null && result == null) { //we have error, process it
if (listener != null) {
listener.onError(this.error);
}
}
if (Boolean.FALSE.equals(result)) { //we have faile, process it
if (listener != null) {
listener.onFail();
}
}
if (Boolean.TRUE.equals(result)) { //we have success
if (listener != null) {
listener.onSuccess();
}
}
}
}
}
And then, in you activit/fragment/service/ use something like this:
public class MyActivity extends Activity {
private void someInstanceMethod() {/ *do your work here */}
#Override
public void onCreate(Bundle savedInstanceState) {
//setup ui, or do whatever you need
//create MyAsyncTask with proper listener
MyAsyncTask task = new MyAsyncTask(new MyAsyncTask.MyAsyncTaskListener() {
#Override
public void onSuccess() {
//call your instance method here
someInstanceMethod();
}
#Override
public void onFailure() {
//process fail
}
#Override
public void onError() {
//process error
}
});
}
}
This is one method. I don't know if it is the best one:
Make work function as public static void. Call it from Asynctask onpostexecute as
HomeActivity.Work();
Edit:
One more way( again not sure if this is the best way):
If you cant make this work, consider putting your asynctask class inside the home activity class
Well using the FragmentManger findFragmentById() or findFragmentByTag() you can get an instance of the current fragment and call your fragment method.
Create an interface file
public interface AsynAction
{
public void Work();
}
Implements AsynAction in HomeActivity
public class HomeActivity extends FragmentActivity implements OnClickListener,AsyncAction {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
// Create the list fragment and add it as our sole content.
if (fm.findFragmentById(android.R.id.content) == null) {
HomeFragment list = new HomeFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
public static class HomeFragment extends Fragment {
webServiceTask = WebServiceTask.getInstance(
getActivity(), Constants.METHOD_NAME_PRODUCTS,
Constants.PRODUCT_NAME, null);
#Override
public void Work()
{
}
}
}
Then make changes in you asynctask to receive asyncAction object as reference
final public class WebServiceTask extends AsyncTask<String, String, String> {
private WebServiceTask(final AyscAction asycAction,final Activity activity, final String methodName,
final String productName, final String addInfo[]) {
super();
this.activity = activity;
this.asycAction=asycAction;
this.methodName = methodName;
this.productName = productName;
this.addInfo = addInfo;
}
public static WebServiceTask getInstance(final AyscAction asycAction,final Activity activity,
final String methodName, final String productName,
final String additionalInfo[]) {
webServiceTask = new WebServiceTask(asycAction,activity, methodName, productName,
additionalInfo);
return webServiceTask;
}
protected void onPostExecute() {
// You can call work from here
if(asynAction!=null)
asyncAction.Work();
}