Synchronizing AsyncTasks - android

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.

Related

return type for method with runnable in it

I'm using an executor for background operations. I have a method that takes data from a Room Database and returns a string, so that I can send it from the repository into a viewmodel into activity.
How can I return a string in the method while there is a runnable in it? Please see the following code for a better description:
public String singleLoad(final int id){
DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.execute(new Runnable() {
#Override
public void run() {
favNewsDao.loadSingle(id);
}
});
return favNewsDao.loadSingle(id);
}
The return gives an exception, saying that it cannot access the database on the Main Thread. How can I get a string from this method, like I have in this ViewModel class
public String singleLoad(int id) {
return repository.singleLoad(id);
}
Instead of using an Executor you can use an ExecutorService and submit a Callable. More information here: https://developer.android.com/reference/java/util/concurrent/ExecutorService.html#submit(java.util.concurrent.Callable%3CT%3E)
First Approach
In Repository class, use CountDownLatch with a count value of 1, and with until it reaches 0 to return back the correct result this can be achieved by using await() of this CountDownLatch which allows to urge executing the underlying code until the latch count value reaches 0.
CountDownLatch mLatch;
String singleLoad;
public String singleLoad(final int id){
mLatch = new CountDownLatch(1); // latch count is 1
DefaultExecutorSupplier.getInstance().forBackgroundTasks()
.execute(new Runnable() {
#Override
public void run() {
singleLoad = favNewsDao.loadSingle(id);
mLatch.countDown(); // Now you can allow returning back the result (id)
}
});
// Don't return now and wait until the Executer is done
try {
// Application's main thread awaits, till the
// CountDownLatch count value reaches 0
mLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return mId;
}
UPDATE
As first approach might have memory leak, here is a second approach by using a listener instead of CountDownLatch.
Second Approach
Here I am triggering a listener whenever the needed String is returned back from Room database to the Repository class; the listener is registered in the activity, cascaded to ViewModel, and then to the Repository.
The listener has a callback that retrieves the returned String from database which is eventually returned back to the activity.
Dao interface
#Dao
public interface MyDao {
...
#Query("SELECT text FROM notes WHERE id = :id") // change types according to your database
String loadSingle(int id);
}
Repository
public class AppRepository {
// ... non-relevent code is omitted
private static Executor executor = Executors.newSingleThreadExecutor();
public interface OnTextListener {
void onTextReceived(String text);
}
public void getNoteText(int id, OnTextListener listener) {
executor.execute(() -> {
String text = mDb.noteDao().loadSingle(id); // mDb is the database instance in repository
// trigger the listener in the ViewModel
listener.onTextReceived(text);
});
}
}
ViewModel
public class MainViewModel extends AndroidViewModel {
// ... non-relevent code is omitted
public void getNoteText(int id, AppRepository.OnTextListener listener) {
mRepository.getNoteText(id, listener);
}
}
Activity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ... non-relevent code is omitted
// Retrieve single data from Room database
int id = 39;
mViewModel.getNoteText(id, new AppRepository.OnTextListener() {
#Override
public void onTextReceived(String text) {
// The retrieved text from Room database
Log.d(TAG, "onTextReceived: " + text);
new Handler(Looper.getMainLooper()) {
}.post(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();
}
});
}
});
}
}

How to save data from parse to local data

I'm a beginer in Android.
I have small problem in my project , i wanna save my data in parse to local data, but i don't know how to do:
I loaded data from BUS_STOP in parse to my BusStop arraylist.
I added Parse.enableLocalDatastore(getApplicationContext()); to my Application.
This is my code download data to busstops:
busStops = new ArrayList<>();
try {
ParseQuery<ParseObject> query = new ParseQuery<>("BUS_STOP");
query.orderByAscending("stop_id");
query.setLimit(2000);
listA = query.find();
for (ParseObject mBusStop : listA) {
BusStop newBusStop = new BusStop();
newBusStop.setName((String) mBusStop.get("name"));
newBusStop.setStreet((String) mBusStop.get("street"));
newBusStop.setStyle((String) mBusStop.get("Type"));
newBusStop.setNext_id((int) mBusStop.get("next_id"));
newBusStop.setBus_id((int) mBusStop.get("bus_id"));
newBusStop.setStop_id((int) mBusStop.get("stop_id"));
double x, y;
x = (double) mBusStop.get("coor_x");
y = (double) mBusStop.get("coor_y");
LatLng a = new LatLng(x, y);
newBusStop.setLatLngBus(a);
busStops.add(newBusStop);
}
} catch (com.parse.ParseException e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
and this is my class BusStop
#ParseClassName("BUS_STOP")
public class BusStop extends ParseObject{
String name;
String street;
String style;
LatLng latLngBus;
int bus_id;
int next_id;
int stop_id;
public String getPName(){
return getString("name");
}
public void setPName(String name) {
put("name", name);
}
public String getPStreet(){
return getString("street");
}
public void setPStreet(String street) {
put("street", street);
}
public String getPStyle(){
return getString("Type");
}
public void setPStyle(String type) {
put("Type", type);
}
public double getMCoor_x(){
return getDouble("coor_x");
}
public void setMCoor_x(double coor_x) {
put("coor_x", coor_x);
}
public double getMCoor_y(){
return getDouble("coor_y");
}
public void setMCoor_y(double coor_y) {
put("coor_y", coor_y);
}
public int getMBus_id(){
return getInt("bus_id");
}
public void setMCoor_y(int bus_id) {
put("bus_id", bus_id);
}
public int getMNext_id(){
return getInt("next_id");
}
public void setMNext_id(int next_id) {
put("next_id", next_id);
}
public int getMStop_id(){
return getInt("stop_id");
}
public void setMStop_id(int stop_id) {
put("stop_id", stop_id);
}
public int getStop_id() {
return stop_id;
}
public void setStop_id(int stop_id) {
this.stop_id = stop_id;
}
public int getNext_id() {
return next_id;
}
public void setNext_id(int next_id) {
this.next_id = next_id;
}
public int getBus_id() {
return bus_id;
}
public void setBus_id(int bus_id) {
this.bus_id = bus_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getStyle() {
return style;
}
public void setStyle(String style) {
this.style = style;
}
public LatLng getLatLngBus() {
return latLngBus;
}
public void setLatLngBus(LatLng latLngBus) {
this.latLngBus = latLngBus;
}
}
If my way is the bad way, tell me the great way please !
From what I've understood from your code and question, you're locking your main thread (the UI Thread) when you load and parse your data.
The main thread is responsible to handle user input and all the screen drawing. So if one operation is taking too long, your app will be frozen until it finishes, and for the user it'll seems like your app crashed.
This video from Udacity explains it very well.
You can read more about Thread on Android here.
And this link shows you how to run things on the background.
EDIT: Sample code for AsyncTask. Taken from the Android dev site.
AsyncTask allows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.
To use it, you must subclass AsyncTask and implement the doInBackground() callback method, which runs in a pool of background threads. To update your UI, you should implement onPostExecute(), which delivers the result from doInBackground() and runs in the UI thread, so you can safely update your UI. You can then run the task by calling execute() from the UI thread.
For example, you can implement the previous example using AsyncTask this way:
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
Now the UI is safe and the code is simpler, because it separates the work into the part that should be done on a worker thread and the part that should be done on the UI thread.
You should read the AsyncTask reference for a full understanding on how to use this class, but here is a quick overview of how it works:
You can specify the type of the parameters, the progress values, and
the final value of the task, using generics
The method doInBackground() executes automatically on a worker thread
onPreExecute(), onPostExecute(), and onProgressUpdate() are all
invoked on the UI thread
The value returned by doInBackground() is sent to onPostExecute()
You can call publishProgress() at anytime in doInBackground() to
execute onProgressUpdate() on the UI thread
You can cancel the task at any time, from any thread
Caution: Another problem you might encounter when using a worker thread is unexpected restarts in your activity due to a runtime
configuration change (such as when the user changes the screen
orientation), which may destroy your worker thread. To see how you can
persist your task during one of these restarts and how to properly
cancel the task when the activity is destroyed, see the source code
for the Shelves sample application.

How to wait until an async method finishes using Futures?

I have an async Retrofit-based API call in Android and need to wait with the DB calls until the API call finishes, so that I am sure the proper data gets entered into the DB.
I read that you can use Futures to accomplish this task, however with my current implementation I get a null pointer exception.
Below is the API method:
public Future<Void> postPrintMode(String authorization, final int userid, String deviceuid, final Map payload){
api.postPrintMode(authorization, userid, deviceuid, payload, new Callback<PrintMode>() {
#Override
public void success(PrintMode printMode, Response response) {
if (printMode.get_id() != 0) {
dbOps.writePrintMode(userid, printMode);
bus.getBus().post(new EVTNewPrintMode(printMode));
}
}
#Override
public void failure(RetrofitError retrofitError) {
retrofitError.printStackTrace();
APIUtils.showAPIResponseBody(retrofitError);
}
});
return null;
}
And here the block where I want to ensure that the async code is executed BEFORE I continue to read the DB results.
Future<Void> f = APIExec.getInstance().postPrintMode(IConstants.authorization, IConstants.userId, IConstants.deviceUid, payload);
// here I get the null pointer exception
f.get();
// the code below needs to be executed after the postPrintMode(...) async method;
DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
assertNotNull("Print Mode does not exist", printMode);
You can make your class that calls public Future<Void> postPrintMode method implement the new Callback<PrintMode> interface. After, you can your postPrintMode from it and pass a reference to itself into the method.
Here is a rough example (code not tested)
class Foo implements Callback<PrintMode> {
Future<Void> f;
public Foo(){
f = APIExec.getInstance().postPrintMode(IConstants.authorization, IConstants.userId, IConstants.deviceUid, this);
}
#Override
public void success(PrintMode printMode, Response response) {
if (printMode.get_id() != 0) {
dbOps.writePrintMode(userid, printMode);
bus.getBus().post(new EVTNewPrintMode(printMode));
}
if (f != null){
f.get();
// the code below needs to be executed after the postPrintMode(...) async method;
DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
assertNotNull("Print Mode does not exist", printMode);
}
}
#Override
public void failure(RetrofitError retrofitError) {
retrofitError.printStackTrace();
APIUtils.showAPIResponseBody(retrofitError);
}
}
Create a AsyncTaskThread class as below,
public class AsyncTaskThread extends AsyncTask<Void, Void, Void> {
Context context;
Handler myHandler;
public AsyncTaskThread( Context activityContext, Handler handler ) {
this.context = activityContext;
this.myHandler = handler;
}
#Override
protected void onPreExecute() {
// before starting thread you can pre process few things here if needed
}
#Override
protected Void doInBackground(Void... params) {
// do whatever you want to do here like calling your API and return your result
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
// after doIn Background this method is called which will set the meesage object and give it back to handler
Message message = new Message();
message.obj = result;
myHandler.sendMessage(message);
}
}
call this async class as,
new AsyncTaskThread(this, new MyHandler()).execute();
and You will have to put this handler class inside the class you are putting above line, depending upon the result you get in handle you can perform further operations,
private class MyHandler extends Handler {
#Override
public void handleMessage(Message msg) {
}
}

How to check if thread finished execution Android Studio

I am trying to create an app that contains 2 spinners with data from a data base. When a button is pressed, a new intent is created, showing the 2 spinners. The problem is that I have to create the DB queries in a new thread, and when I run the app, I get a null pointer exception (as far as my understanding goes, it is because the array in which I store the DB data is not yet populated).
My question is, how can I delay the creation of the spinners until the queries from the DB are made?
Below is a sample code of my implementation:
The intent where I create the spinners and make a call to a class that makes the DB queries:
String [][] dbData; //the array where i store data from DB
getDBdata myData = new getDBdata(); // create a new instance of the class that queries the DB
dbData = myData.getData(); // I get the data
Log.e("My log: ", String.valueOf(dbData.length)); //and here it crashes, giving me a null pointer exception
And the class where I create a new thread to make a DB query:
public getDBdata()
{
Thread thread = new Thread(new Runnable(){
#Override
public void run() {
DBconn(); //make a DB query
}
});
thread.start();
}
public String[][] getData()
{
return data;
}
The easiest way is a using of AsyncTask. The basic idea of AsyncTask is splitting execution of your task into three steps which go one after another. Each step is running in a separate thread. The last one runs in the Main(UI) where you can create your spinners. Sample:
public class DBQueryTask extends AsyncTask<Void, Void, String[][]> {
#Override
protected String[][] doInBackground(Void... params) {
DBconn();
String[][] a;
//...populating array
return a;//returning populated array
}
#Override
protected void onPostExecute(String[][] strings) {
//strings - array already populated
//this method runs on the Main thread. Therefore you can create your spinner
}
}
You don't need a new thread if you want to wait it to finish. But if you do, then you don't need to wait it to finish, but use a callback method instead
private static final int MESSAGE_DATA_RETRIEVED = 1;
private String [][] dbData; //the array where i store data from DB
private Handler mHandler;
// in onCreate or constructor depending what are you in
mHandler = new DataHandler(this);
// end of onCreate or constructor
private void getData() {
final getDBdata myData = new getDBdata(mHandler);
myData.getData();
}
private void onDataRetrieved() {
Log.e("My log: ", String.valueOf(dbData.length));
}
private static final class DataHandler extends Handler {
private final WeakReference<YourEnclosingClass> mClassReference;
DataHandler(final YourEnclosingClass instance) {
mClassReference = new WeakReference<>(instance);
}
#Override
public void handleMessage(Message msg) {
if (msg.what == MESSAGE_DATA_RETRIEVED) {
final YourEnclosingClass instance = mClassReference.get();
if (instance != null) {
instance.onDataRetrieved();
}
}
}
}
The getting data class
private final Handler mHandler;
// calling a class a verb is bad. Should be a noun, but I haven't took a time to rename it here
public getDBdata(final final Handler handler) {
mHandler = handler;
// starting a Thread in constructor is a bad idea. I moved it to a method
}
public void getData()
{
final Thread thread = new Thread(new Runnable(){
#Override
public void run() {
DBconn(); //make a DB query
nHandler.sendEmptyMessage(MESSAGE_DATA_RETRIEVED);
}
});
thread.start();
}
That's how multithreading done.
But anyway that's a bad idea. You have to use AsyncTask or CursorLoader for your task instead.

publishProgress from inside a function in doInBackground?

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.

Categories

Resources