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.
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'm trying to get access into my database in the background so that i don't lock the threads and get an error, so i read i should use AsyncTask. from what i read up on it takes uses 3 data types. The data type it takes in, the data type it processes, and the return data type. so here im making a step tracker and want to access my database to get something by id, so i call my repository and pass in the database i'm using and the id i want to find
cStep = stepRepository.getStepById(stepDatabase,0);
and this here is my repository class and the AsyncTask within it
> public class StepRepository implements IStepDataSource {
private IStepDataSource mLocalDataSource;
private static StepRepository mInstance;
public StepRepository(IStepDataSource mLocalDataSource) {
this.mLocalDataSource = mLocalDataSource;
}
public static StepRepository getInstance(IStepDataSource mLocalDataSource){
if(mInstance == null)
mInstance = new StepRepository(mLocalDataSource);
return mInstance;
}
#Override
public Flowable<List<Step>> getAllSteps() {
return mLocalDataSource.getAllSteps();
}
#Override
public Step getStepById(StepDatabase db, int userId) {
return new getAsyncTask(db).execute(userId);
}
private static class getAsyncTask extends AsyncTask<Integer, Void, Step> {
getAsyncTask(StepDatabase db) {
this.db = db;
}
#Override
protected Step doInBackground(Integer... params) {
StepDao dao = db.stepDao();
return dao.getStepById(params[0]);
}
#Override
protected void onPostExecute(Step step) {
}
}
#Override
public void insertStep(Step... steps) {
mLocalDataSource.insertStep(steps);
}
#Override
public void updateStep(Step... steps) {
mLocalDataSource.updateStep(steps);
}
#Override
public void deleteStep(Step step) {
mLocalDataSource.deleteStep(step);
}
}
im not getting why getUserByid is giving me imcopatible type since AsyncTask takes in and interger and returns a steps which is what i want??
btw if its any help this is the IStepDataSource my repository implements
public interface IStepDataSource {
Flowable<List<Step>> getAllSteps();
Step getStepById(StepDatabase db, int userId);
void insertStep(Step... steps);
void updateStep(Step... steps);
void deleteStep(Step step);
}
The execute() method of AsyncTask returns void and your are attempting to return a void from a method declared as returning Step. It's probably best to get the AsyncTask out of the getStepById() method and instead use an AsyncTask where you invoke getStepById(). I think you're assuming that execute() blocks until the task is complete and that is incorrect. If this were the case, there'd be no point to using AsyncTask. execute() returns immediately and onPostExecute(Step step) is where the results should be processed/displayed/whatever.
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'm trying to create an app that makes HTTP requests through an intentservice. I need the app to wait for the service to finish its run (aka, have the request be returned with some data) before it continues its operations, as its operations involve manipulation of the data I hope to receive from the HTTP requests. I've tried numerous means of doing so - Semaphore, CountDownLatch, but it seems that for all of them, I need some method of passing in the waiting/counting object into the intentservice so that it can tell the main thread where that object is waiting that it is done processing. How do I go about doing that? Basically, I want a synchronous, blocking call to an http server to work conveniently with an Intent Service, since an intent service makes multi threading easy.
Again to reiterate just to make sure i'm not misusing terminology: What I mean by Synchronous and blocking/what I want: I make a call to the http server by sending an intent to my intentservice that makes the request. My UI thread, or thread from which this intent was sent, now waits until the request has been processed and a result has been returned before continuing to run.
If you think that I am going about this process (making http calls in a blocking, synchronous way) all wrong, what is another way you might choose to go about it? Thanks!
I am sorry, but I think your architecture is not right or I may understand it wrong. IntentService is built to do thing serial way on separate thread. Now you say you want it to be synchronous and blocking. You cannot block UI thread!
In order to create notification system from your IntentService to Activity/Fragment/etc. you have few choices: singleton, broadcast message (receiver, resultReceiver), others?
Based on assumption that service and other parts of the application are working in same process. Best option would be to create manager to do this job. Something like this can be built to start service as well as listen for completion event:
public class MyNetworkManager {
static MyNetworkManager sInstance;
Context mContext;
LinkedList<OnCompletionListener> mListeners;
private MyNetworkManager(Context context) {
mContext = context;
mListeners = new LinkedList<>();
}
public static MyNetworkManager getInstance(Context context) {
if (sInstance == null) {
synchronized (MyNetworkManager.class) {
if (sInstance == null) {
sInstance = new MyNetworkManager(context.getApplicationContext());
}
}
}
return sInstance;
}
// add listener to listen for completion event
public void addListener(OnCompletionListener listener) {
synchronized (mListeners) {
mListeners.add(listener);
}
}
// remove listener to stop listening for completion event
public void removeListener(OnCompletionListener listener) {
synchronized (mListeners) {
mListeners.remove(listener);
}
}
// call from UI to start service operation
public void startNetworkOperation() {
Intent service = new Intent();
mContext.startService(service);
}
// call from service to notify UI (still on different thread, can use Handler to make call on main thread)
public void notifyCompletion() {
synchronized (mListeners) {
for (OnCompletionListener listener : mListeners) {
listener.onCompleted(this);
}
}
}
public static interface OnCompletionListener {
void onCompleted(MyNetworkManager manager);
}
}
Use this pattern
public interface SynchronizationListener {
//void onStart(int id); not requered
//void onProgress(int id, long updateTime); not requered
void onFinish(Object data); // replace Object with your data type
}
In your service add end call this
private void startSynchronization() {
SynchronizationManager.getInstance().startSynchronizing();
}
Your Singleton Manager
public class SynchronizationManager {
private static SynchronizationManager instance;
private Object synRoot = new Object();
private boolean synchronizing = false;
private List<SynchronizationListener> synchronizationListeners;
public SynchronizationManager() {
synchronizationListeners = new ArrayList<SynchronizationListener>();
}
static {
instance = new SynchronizationManager();
}
public static SynchronizationManager getInstance() {
return instance;
}
public boolean isSynchronizing() {
synchronized (synRoot) {
return synchronizing;
}
}
public void startSynchronizing() {
synchronized (synRoot) {
if (synchronizing) {
return;
}
synchronizing = true;
}
Object data; // <-- replace Object with your data type
if (ConnectivityReceiver.hasGoodEnoughNetworkConnection()) { // check connection
data = sync();
}
synchronized (synRoot) {
synchronizing = false;
}
onSynchronizationFinish(data); // use listener for send data tu Observer Activity
}
public void stopSynchronizing() {
synchronized (synRoot) {
synchronizing = false;
}
}
public synchronized void registerSynchronizationListener(
SynchronizationListener listener) {
if (!synchronizationListeners.contains(listener)) {
synchronizationListeners.add(listener);
}
}
public synchronized void unregisterSynchronizationListener(
SynchronizationListener listener) {
if (synchronizationListeners.contains(listener)) {
synchronizationListeners.remove(listener);
}
}
public void onSynchronizationStart(int id) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onStart(id);
}
}
protected void onSynchronizationProgress(int id, long updateTime) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onProgress(id, updateTime);
}
}
protected void onSynchronizationFinish(Object data) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onFinish(data);
}
}
protected int sync) {
// code for load your data your HttpRequest
}
}
In your activity
private SynchronizationListener synchronizationListener = new SynchronizationListener() {
/*public void onStart(int id) {
}
public void onProgress(int id, long updateTime) {
}*/
public void onFinish(Object data) {
//elaborate data
}
};
#Override
protected void onResume() {
super.onResume();
SynchronizationManager.getInstance().registerSynchronizationListener(
synchronizationListener);
}
#Override
protected void onPause() {
super.onPause();
SynchronizationManager.getInstance().unregisterSynchronizationListener(
synchronizationListener);
}
See this code for example UnivrApp
A ContentProvider would be a better choice than an IntentService in my thinking. You can trigger each network call with a query and then return a MatrixCursor with details about the results of your background work. Android already has lots of good plumbing around running queries in background tasks and waiting for the results before triggering ui updates.
in ContentProvider query() method :
MatrixCursor cursor = new MatrixCursor(new String[]{"_id","uri", "status", "last_modified", "result"});
String lastModified=null;
int id =1;
// do your work here
// ..
// report your work here
cursor.addRow(new Object[]{id++, uri.toString(), HttpStatus.SC_OK, lastModified, "" });
// set uri for data observers to register
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
What you try to do is just communication between IntentService and Activity/Fragment.
You can try send broadcast at the end of onHandleIntent and catch it in registered receiver or use ResultReceiver - read more how to implement here.
Edit:
Try this:
Handle all background operations at once in onHandleIntent
On every step send new data using ResultReceiver
// operation 1
Bundle b1 = new Bundle();
b1.putParcelable("data", data1);
resultReceiver.send(0, b1);
// operation 2
Bundle b2 = new Bundle();
b2.putParcelable("data", data2);
resultReceiver.send(1, b2);
Handle it in ResultReceiver
public void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 0) { // handle step 1 }
else if (resultCode == 1) { // handle step 2 }
}
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.