I use an AsyncTask to perform a long process.
I don't want to place my long process code directly inside doInBackground. Instead my long process code is located in another class, that I call in doInBackground.
I would like to be able to call publishProgress from inside the longProcess function.
In C++ I would pass a callback pointer to publishProgress to my longProcess function.
How do I do that in java ?
EDIT:
My long process code:
public class MyLongProcessClass
{
public static void mylongProcess(File filetoRead)
{
// some code...
// here I would like to call publishProgress
// some code...
}
}
My AsyncTask code:
private class ReadFileTask extends AsyncTask<File, Void, Boolean>
{
ProgressDialog taskProgress;
#Override
protected Boolean doInBackground(File... configFile)
{
MyLongProcessClass.mylongProcess(configFile[0]);
return true;
}
}
EDIT #2
The long process method could also be non-static and called like this:
MyLongProcessClass fileReader = new MyLongProcessClass();
fileReader.mylongProcess(configFile[0]);
But that does not change my problem.
The difficulty is that publishProgress is protected final so even if you pass this into your static method call you still can't call publishProgress directly.
I've not tried this myself, but how about:
public class LongOperation extends AsyncTask<String, Integer, String> {
...
#Override
protected String doInBackground(String... params) {
SomeClass.doStuff(this);
return null;
}
...
public void doProgress(int value){
publishProgress(value);
}
}
...
public class SomeClass {
public static void doStuff(LongOperation task){
// do stuff
task.doProgress(1);
// more stuff etc
}
}
If this works please let me know! Note that calling doProgress from anywhere other than a method that has been invoked from doInBackground will almost certainly cause an error.
Feels pretty dirty to me, anyone else have a better way?
A solution could be placing a simple public class inside the AsyncTask (make sure the task you define is also public) which has a public method that calls publishProgress(val). Passing that class should be available from any other package or class.
public abstract class MyClass {
public MyClass() {
// code...
}
// more code from your class...
public class Task extends AsyncTask<String, Integer, Integer> {
private Progress progress;
protected Task() {
this.progress = new Progress(this);
}
// ...
#Override
protected Integer doInBackground(String... params) {
// ...
SomeClass.doStuff(progress);
// ...
}
// ...
#Override
protected void onProgressUpdate(Integer... progress) {
// your code to update progress
}
public class Progress {
private Task task;
public Progress(Task task) {
this.task = task;
}
public void publish(int val) {
task.publishProgress(val);
}
}
}
}
and then in the other class:
public class SomeClass {
public static void doStuff(Progress progress){
// do stuff
progress.publish(20);
// more stuff etc
}
}
This worked for me.
Split up the longProcess() function into smaller functions.
Sample code:
#Override
protected Boolean doInBackground(Void... params) {
YourClass.yourStaticMethodOne();
publishProgress(1);
YourClass.yourStaticMethodTwo();
publishProgress(2);
YourClass.yourStaticMethodThree();
publishProgress(3);
// And so on...
return true;
}
If this works please let me know! Note that calling doProgress from anywhere other than a method that has been invoked from doInBackground will almost certainly cause an error.
Yes, it works. I extended it so that you don't need to pass the AsyncTask as a parameter to your method. This is particularly useful if (like me) you've already written all your methods before deciding that actually you do need to publish some progress, or in my case, update the UI from an AsyncTask:
public abstract class ModifiedAsyncTask<A,B,C> extends AsyncTask<A,B,C>{
private static final HashMap<Thread,ModifiedAsyncTask<?,?,?>> threads
= new HashMap<Thread,ModifiedAsyncTask<?,?,?>>();
#Override
protected C doInBackground(A... params) {
threads.put(Thread.currentThread(), this);
return null;
}
public static <T> void publishProgressCustom(T... t) throws ClassCastException{
ModifiedAsyncTask<?, T, ?> task = null;
try{
task = (ModifiedAsyncTask<?, T, ?>) threads.get(Thread.currentThread());
}catch(ClassCastException e){
throw e;
}
if(task!=null)
task.publishProgress(t);
}
}
public class testThreadsActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void Button1Clicked(View v){
MyThread mthread = new MyThread();
mthread.execute((Void[])null);
}
private class MyThread extends ModifiedAsyncTask<Void, Long, Void>{
#Override
protected Void doInBackground(Void... params) {
super.doInBackground(params);
while(true){
myMethod(System.currentTimeMillis());
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
return null;
}
}
}
protected void onProgressUpdate(Long... progress) {
//Update UI
((TextView) findViewById(R.id.textView2)).setText("The Time is:" + progress[0]);
}
}
private void myMethod(long l){
// do something
// request UI update
ModifiedAsyncTask.publishProgressCustom(new Long[]{l});
}
}
Feels pretty dirty to me, anyone else have a better way?
My way is probably worse. I'm calling a static method for doProgress (which I called publishProgressCustom). It can be called from anywhere without producing an error (as if the thread has no corresponding AsyncTask in the hashMap, it won't call publishProgress). The down side is that you have to add the Thread-AsyncTask mapping yourself after the thread has started. (You can't override AsyncTask.execute() sadly as this is final). I've done it here by overriding doInBackground() in the super class, so that anyone extending it just has to put super.doInBackground() as the first line in their own doInBackground().
I don't know enough about Threads and AsyncTask to know what happens to the HashMap references once the Thread and/or AsyncTask comes to an end. I suspect bad things happen, so I wouldn't suggest anyone try my solution as part of their own unless they know better
When you say "my long process code is located in another class that I call in doInBackground", do you mean "located in another method that I call in doInBackground"?
If so, you could make that method a private method of your AsynTask class. Then you could call publishProgress inside the method whenever needed.
Related
Im working with the in-memory database, and doing some simple tasks like writing in some movies, then reading them out and displaying them. Im using RoomPersistance and i have some repositories set up. My problem:
Here i am getting the movies from a response, and inserting them in a database through the insertMovie method.
for (OMDBItem movie : response.body().getItems())
{
omdbItemRepository.insertMovie(movie);
}
This method looks like this:
public void insertMovie(OMDBItem movie){
AsyncTask<Void,Void,Void> atask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
movieDataBase.movieDao().insert(movie);
return null;
}
}.execute();
}
then i have this piece of code:
for (OMDBItem movie : response.body().getItems())
{
omdbItemRepository.insertMovie(movie);
}
lista_filmovi=omdbItemRepository.getAllMovies();
and getAllMovies() is a similar method that looks like this:
public List<OMDBItem> getAllMovies(){
new AsyncTask<Void,Void,Void>(){
#Override
protected Void doInBackground(Void... voids) {
lista_filmovi=movieDataBase.movieDao().getAllMovies();
return null;
}
}.execute();
return lista_filmovi;
}
The problem is that sometimes, this method getAllMovies returns me the movies i want, but sometimes it just returns null. And it only returns movies when i put some break-points and run it in the debugger. My quess is that by running it in the debugger and clicking though the methods, im giving the insertMovie(movie) AsyncTasks more time to do its job, and when getAllMovies() gets called, it gives me a good result. So basically the question is, is there anyway i can make the getAllMovies() AsyncTask not start until the insertMovie() AsyncTasks have finished. I know i can maybe put an onPostExecute in insertMovie(), but i want these methods sepereated ( i dont want to call getAllMovies() everytime after insertMovie()). Any solution?
You have a few problems in your code.
The first is that you need to wait for all the movies to be written in the database to start the reading back. Then in the reading you cannot just return the value of lista_filmovi as the reading will be async so the returned value will not be there when you will try to read it.
An example Async task to write movies could be:
public static class InsertMovie extends AsyncTask<OMDBItem,Void,Void> {
private final Database movieDataBase;
public InsertMovie(Database movieDataBase) {
this.movieDataBase = movieDataBase;
}
#Override
protected Void doInBackground(OMDBItem... movies) {
for (OMDBItem movie : movies)
movieDataBase.movieDao().insert(movie);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
// data is stored
}
}
To write the movies use the statement:
new InsertMovie(movieDataBase).execute(movies);
You shall not attempt to read the data until the OnPostExecute is called. There are various ways to do that but the simpler could be to just start the reading there.
And then to read it back:
public static class GetAllMovies extends AsyncTask<Void,Void,List<OMDBItem>> {
private final Database movieDataBase;
public GetAllMovies(Database movieDataBase) {
this.movieDataBase = movieDataBase;
}
#Override
protected List<OMDBItem> doInBackground(Void... voids) {
return movieDataBase.movieDao().getAllMovies();
}
#Override
protected void onPostExecute(List<OMDBItem> allMovies) {
// post the result to your activity
}
}
Again the result will be available in the OnPostExecute and you can't access it before that method is called.
The best ways to fit this in your Activity then varies. I suggest using an AndroidViewModel and get the result as notifications on LiveData objects. In this case you do not even need to use AsyncTask as you can just post the results in the LiveData.
Start from an AndroidViewModel like this:
/** ViewModel providing additional features to ease Room DB access */
public class RoomViewModel extends AndroidViewModel {
/** Thread executing Room operations */
private static class RoomThread extends Thread {
/** Queue of tasks pending execution */
private BlockingQueue<Runnable> tasks = new LinkedBlockingQueue<>();
/** Set to false to stop */
private boolean running = true;
/** Send this to stop the execution */
private Runnable STOP = new Runnable() {
#Override
public void run() {
running = false;
}
};
#Override
public void run()
{
while (running) {
try {
// execute next in line, when available
tasks.take().run();
} catch (InterruptedException e) {
// this should not happen
return;
}
}
}
}
/** Executes backround Room requests in order */
private RoomThread roomThread = new RoomThread();
public RoomViewModel(#NonNull Application application) {
super(application);
// start the background execution thread
roomThread.start();
}
/**
* Queues the specified Runnable for execution
* #param runnable The Runnable to be executed in background
*/
protected void execute(Runnable runnable)
{
roomThread.tasks.offer(runnable);
}
#Override
protected void onCleared() {
// queue the stop request
execute(roomThread.STOP);
super.onCleared();
}
}
This helps you as you will have just a single background thread for DB access and so the operations will be ordered.
In your MovieViewModel extending RoomViewModel you can then use:
// write the movies
execute(new Runnable() {
#Override
public void run() {
for (OMDBItem movie : movies) movieDataBase.movieDao().insert(movie);
}
});
and
// read the movies
execute(new Runnable() {
#Override
public void run() {
allMovies.postValue(movieDataBase.movieDao().getAllMovies());
}
});
In the Activity you can observe the allMovies as MutableLiveData<List<OMDBItem>> and get notification on when new data is available to show it.
I am using AsyncTask to do a bunch of code in background thread for me to prevent UI frozen. The AsyncTask result is OK and I can use that result in onPostExecute.
But the problem is that outside AsyncTask after initializing and executing the asyncTask i cannot access the result because it's on background thread.
for Example
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyObject mObject = new Myobject(getApplicationContext());
mObject.getStart();
mObject.getResult();
}
public class MyObject extends Parent{
Context context;
public MyObject (Context context, long startTimeStamp) {
super();
this.context = context;
setStart(startTimeStamp);
MyAsyncTask async = new MyAsyncTask ();
async.execute("");
}
private Object myTestMethod() {
if(true)
return true;
return false;
}
private class MyAsyncTask extends AsyncTask<String, Void, Object> {
#Override
protected Object doInBackground(String... strings) {
return myTestMethod();
}
#Override
protected void onPostExecute(Object o) {
setResult(o);
}
}
}
public class Parent{
private long start;
private Object result;
public long getStart() {
return start;
}
public void setStart(long start) {
this.start = start;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}
In the first class when I call getStart() from super class it return StartTime and works good, but when i Call getResult() it returns null because AsyncTask is working in background, I search and found this But
try {
MyAsyncTask async = new MyAsyncTask ();
setResult(async.execute().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
It will wait and frozen the Foreground.
If there is any solution for my setter method to wait until AsyncTask Complete the task and then return the result
or any other suggestion
I am glad to hear
If Example was unClear comment me to make you clear
Doesn't this defeat the purpose of an asyncTask? The purpose of an async task is to defer the processing work onto a background thread and then asynchronously make a callback to the main thread with the result.
MyObject mObject = new Myobject(getApplicationContext());
mObject.getStart();
mObject.getResult();
By doing the above its already synchronous. And i do not think it is wise to embedd an aynctask into an object constructor. What i suggest is to run the async task outside of the object and pass the object as a param into post execute with the object fields populated.
#Override
protected void onPostExecute(Object o) {
o.getResult() //this is executed on the main thread
}
I am new to Android. I am using Sockets in an asynchronous task and I wish to pass data back to the activity that called it. But I do not want the asynchronous task to handle the UI. I just wish to pass data.
The class that e![enter image description here][1]xtends async task is not a part of the class that extends activity
My activity has 2 buttons. When the button is clicked, async task is called and corresponding changes should be made to rest of the activity.
From How do I send data back from OnPostExecute in an AsyncTask:
class YourActivity extends Activity {
private static final int DIALOG_LOADING = 1;
public void onCreate(Bundle savedState) {
setContentView(R.layout.yourlayout);
new LongRunningTask1().execute(1,2,3);
}
private void onBackgroundTaskDataObtained(List<String> results) {
//do stuff with the results here..
}
private class LongRunningTask extends AsyncTask<Long, Integer, List<String>> {
#Override
protected void onPreExecute() {
//do pre execute stuff
}
#Override
protected List<String> doInBackground(Long... params) {
List<String> myData = new ArrayList<String>();
for (int i = 0; i < params.length; i++) {
try {
Thread.sleep(params[i] * 1000);
myData.add("Some Data" + i);
} catch(InterruptedException ex) { }
}
return myData;
}
#Override
protected void onPostExecute(List<String> result) {
YourActivity.this.onBackgroundTaskDataObtained(result);
}
}
}
Yes you can use handler to communicate between AsyncTask and Activity, see following example, it will help,
#Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("file", pdfPath);
message.setData(bundle);
handler.sendMessage(message); // pass handler object from activity
}
put following code into Activity class
Handler handler = new android.os.Handler() {
#Override
public void handleMessage(Message msg) {
String filePath = msg.getData().getString("file"); // You can change this according to your requirement.
}
};
If you dont't aware of Handler class then first read following link, it will help you
https://developer.android.com/training/multiple-threads/communicate-ui.html
There are different way to pass data back to activity. As explained below
Suppose u have one class
public class Socket {
private Activity activity;
//create interface
public interface OnAyscronusCallCompleteListener{
public void onComplete(/*your data as parameter*/);
}
private void setAsyncListener(Activity activity){
this.activity = activity;
}
//rest of your code
// send back data to activity
activity.onComplete(/* your data */)
}
//Now your activity
class YourActivity extends Activity implements Socket.OnAyscronusCallCompleteListener {
// rest of your activity life cycle methods
onCreate(Bundle onSaved)
{Socket socket = new Socket();
socket.setAsyncListener(this);
}
public void onComplete(/*your data*/){
// perform action on data
}
}
In your Activity Class
new YourAsyncTask().execute("String1","String2","12");
Your AsyncTask
AsyncTask<Params, Progress, Result>
private class YourAsyncTask extends AsyncTask<String, Void, Void > {
protected Long doInBackground(String... s) {
String s1 = s[0]; //="String1";
String s2 = s[1]; //="String2";
int s1 = Integer.parseInt(s[2]); //=3;
}
protected void onProgressUpdate(Void... values) {
}
protected void onPostExecute() {
}
}
A great explanation is here
Example to implement callback method using interface.
Define the interface, NewInterface.java.
package javaapplication1;
public interface NewInterface {
void callback();
}
Create a new class, NewClass.java. It will call the callback method in main class.
package javaapplication1;
public class NewClass {
private NewInterface mainClass;
public NewClass(NewInterface mClass){
mainClass = mClass;
}
public void calledFromMain(){
//Do somthing...
//call back main
mainClass.callback();
}
}
The main class, JavaApplication1.java, to implement the interface NewInterface - callback() method. It will create and call NewClass object. Then, the NewClass object will callback it's callback() method in turn.
package javaapplication1;
public class JavaApplication1 implements NewInterface{
NewClass newClass;
public static void main(String[] args) {
System.out.println("test...");
JavaApplication1 myApplication = new JavaApplication1();
myApplication.doSomething();
}
private void doSomething(){
newClass = new NewClass(this);
newClass.calledFromMain();
}
#Override
public void callback() {
System.out.println("callback");
}
}
Then regarding your answer, in actually you have a 2 possibilities... The first one is the answer from #Rodolfoo Perottoni and the other possibility are correctly, read this post please!
I prefer the second way because I can update when I need it.
I would create a inner class in the MainActivity that extends the AsyncTask and voila all data is there already by getters.
I have developed a class that manages all my API calls to my server (via AsyncTask)
problem is, that I want after the doInBackground(), in the onPostExecute() to pass externally a callback function that will be executed in the onPostExecute().
In that way, I can keep my communication class generic, and the Activity will send it a callback to activate and update the UI.
Any idea how do I do that?
thanks!
although answer is accepted, just adding another, real anonymous implementation. hope this help others.
your interface : you can implement this inside AsyncTask class.
public interface ImageLoaderListener{
void imageDownloaderCallBack(Bitmap bmp);
}
AsyncTask class Constructor :
// declare interface variable
private ImageLoaderListener listener;
private String link;
public ImageDownloader(String link, ImageLoaderListener listener){
this.link = link;
this.listener = listener;
}
onPostExecution :
#Override
protected void onPostExecute(Void result){
listener.imageDownloaderCallBack(bitmap);
// your code, i was returning bitmap
}
implementation in Activity class :
ImageDownloader imageDownloader = new ImageDownloader(url, new ImageLoaderListener(){
#Override
public void imageDownloaderCallBack(Bitmap bmp) {
// update Ui elements
}
});
imageDownloader.execute();
Also, you should remember that if any Ui elements need to be updated based on imageDownloaderCallBack return values, you should write that code inside function itself.
Here you go mate:
public class ApiMethods {
public interface OnCommandFinished {
public void onApiSuccess(String result);
public void onApiError(String error);
}
public void like(PARAMS .... , OnCommandFinished respondTo){
new runRequestTask(respondTo).execute(uri, params);
}
private class runRequestTask extends AsyncTask<Object, Void, String>{
private final OnAtomicCommandFinished mRespondTo;
public runRequestTask(OnCommandFinished respondTo){
mRespondTo = respondTo;
}
#Override
protected void onPostExecute(String result) {
// IF SUCCESS
mRespondTo.onAtomicSuccess(result);
// IF ERROR
mRespondTo.onApiError("404....");
}
}
}
To run the code, you simply call like(...) with a class that implements the OnCommandFinished
I am trying to design a helper class that implements methods using AsyncTask.
public interface ResultCallback
{
public String processResult();
}
public class ServerAdapter
{
// Required processResult to call this method. Kind of lousy but I do not know
// how to throw exception from onPostExcecute in AsyncTask.
public String getResult() throws AirplaneModeException, NoNetworkException
{
// code to get return value from Dowork throw exceptions on errors
}
public void getLicense(ResultCallback licenseCallback)
{
...// Set url, outmessage
new Dowork(url, outMessage, licenseCallback).execute();
}
public void queryServer(int queryId, ArrayList<String> args, ResultCallback queryCallback)
{
...// Set url, outmessage
new Dowork(url, outmessage, queryCallback);
}
private class Dowork extends AsyncTask<Void, Void, String>
{
...
private ResultCallback rc;
public Dowork(String url, String outMessage, ResultCallback rc)
{
// code here
}
protected String doInBackground(Void... params)
{
try
{
// code here
}
catch (AirplaneModeException e)
{
return "AirplaneModeException";
}
catch ...
}
protected void onPostExecute(String result)
{
this.result = result;
cb.processResult();
}
}
}
// Client class
public class myclass extends Activity
{
MyServerAdapter myAdapter;
public void onCreate(Bundle savedInstanceState)
{
...
myAdapter = new ServerAdapter();
myAdapter.getLicence(new MyLicenseCallback);
myAdapter.queryServer(id, args, new MyQueryCallback);
...
}
public class MyLicenseCallback extends ResultCallback implements processResult
{
try
{
String result = myAdapter.getResult;
...
}
catch
...
}
...
}
I am new to Java and Android and have a couple of questions:
1- Would several ServerAdapter method calls cause synchronize problem? For example while code for MyLicense callback is running, if onPostExecute calls MyQueryCallback, do I have to handle it or Java handles it?
2- How to get exception thrown in Dowork thrown in the callback instead of work around like in the code above?
Android guarantees you that methods in your activity and AsyncTask.onPostExecute runs in the same main UI thread.
You could save the exception in the task instance variable the same way as you do for result (return, say null as the result in this case). Check if exception present or not later to handle the error situation.
only for turkish speakers :( http://aaarkonusurum.blogspot.com/2011/10/asynctask-classtan-donen-parametreyi.html