When you try to modify/read a class attribute from the loadInBackground() method. What happens? Does android make a deep copy before passing in the variable?
Do modifications inside loadInBackground() actually change the class attribute values on the exterior context?
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Void>
{
public List<String> some_array = new ArrayList();
public String some_string = "Hello world";
...
#Override
public Loader<Void> onCreateLoader(int id, Bundle args)
{
return new AsyncTaskLoader<Void>(MainActivity.this)
{
#Override
protected void onStartLoading()
{
forceLoad();
}
#Override
public Void loadInBackground()
{
some_array.add("some element");
some_string = "good bye";
}
};
}
#Override
public void onLoadFinished(Loader<Void> loader, Void data)
{
// what are the values of some_array and some_string now?
}
#Override
public void onLoaderReset(Loader<Void> loader)
{
}
}
Okay so I manually tested it and found that in Java, arrays are passed as pointers to the background thread.
Therefore, modifications in the background thread do alter the class variables, but this should be avoided as it is asynchronous and can quickly become unpredictable and chaotic.
My current approach is to store the changes in a temporary, overwritable array and then merge them back in the onLoadFinished method, when we are all back in the same thread.
Related
I'm trying to get data using an AsyncLoader. The result I'm getting is only a bunch of links instead of having names of books. This is my code and this is the result. see image here
public class MainActivity extends AppCompatActivity implements
LoaderCallbacks<List<Book>> {
private static final int LOADER_ID=10;
public ArrayAdapter<Book> mAdapter;
private static final String DATA_API_REQUEST="https://www.googleapis.com/books/v1/volumes?q=android&maxResults=10";
private ListView LV;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getLoaderManager().initLoader(LOADER_ID,null, MainActivity.this);
}
#NonNull
#Override
public Loader<List<Book>> onCreateLoader(int i, #Nullable Bundle bundle) {
return new BookLoader(this,DATA_API_REQUEST);
}
#Override
public void onLoadFinished(android.content.Loader<List<Book>> loader, List<Book> data) {
LV=(ListView)findViewById(R.id.ListView1);
mAdapter=new ArrayAdapter<Book>(this,android.R.layout.simple_list_item_1,data);
LV.setAdapter(mAdapter);
if (data != null && !data.isEmpty()) {
mAdapter.addAll(data);}
}
#Override
public void onLoaderReset(android.content.Loader<List<Book>> loader) {
mAdapter.clear();
}
Update :
Use
variable.tostring
to the variable that you are setting TextView to. Here you are setting the object directly which the textview cannot convert so instead you can convert the object to String
You are actually printing a whole object try using the element you want to print for instance Book.name.toString() and then set it in the textview you are using in the adapter. furthurmore do paste the adapter code as well to let community understand the issue completely.
as i mentioned in the comment, you need to overwrite the toString() method of your Book class.
Something like this in your Book class:
#Override
public String toString() {
return this.bookName;
}
The output of the default toString method of most objects consists of the class name of the object as well as the object's hashcode, which is usually not what was intended. (From here)
I have a big database which takes time to find needed information. So I decided to use RxJava to make this process asynchronous.
#Override
public void afterTextChanged(final Editable s) {
final String query = s.toString();
Observable.create(new Observable.OnSubscribe<Cursor>() {
#Override
public void call(Subscriber<? super Cursor> subscriber) {
subscriber.onNext(database.search(query));
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Cursor>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(Cursor cursor) {
scAdapter.swapCursor(cursor);
}
});
}
But query is running on main thread: EditText where I entering text is freezing.
My question is how to run SQLite query asynchronously on background thread?
Probably this https://developer.android.com/reference/android/app/LoaderManager.html
will suite for you.
Besides, here is short implementation for you, but this is not RxJava.
Firstly, you need to implement LoaderManager.LoaderCallbacks<Cursor>, and usually this interface is implemented by Activity (or Fragment).
In onCreateLoader, a CursorLoader should be created and returned. Here is just an example with MyCursorLoader as descendant of CursorLoader, where you can perform connection to database and queries.
In onLoadFinished you have to treat cursor with results of query.
Please, consider the link to android.com, mentioned above.
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
// start loading data using LoaderManager of Activity
// third argument only has sense in this case
getLoaderManager().initLoader(0, null, this);
}
private static final String ACTIVITY_NAME = "main_activity";
private void treatCursorRow(Cursor cursor){
// treat record from cursor
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// this callback is called by LoaderManager in order to obtain CursorLoader
// here a new one loader is created
// created loader will be processed by LoaderManager
return new MyCursorLoader(this, ACTIVITY_NAME);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// this callback is called when loader finishes load cursor
// you don't need to destroy loader - just tread the data
if(data != null)
while(data.moveToNext())
treatCursorRow(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// here you can do something
// relevant to cancelling of loading data
// in example, when you have an event that cancels current
// loading and restarts new one
}
class MyCursorLoader extends CursorLoader {
private static final String DATABASE_NAME = "my_database";
private static final int DATABASE_VERSION = 1;
private String name_param;
public MyCursorLoader(Context context, String activity_name) {
super(context);
name_param = activity_name;
}
#Override
public Cursor loadInBackground() {
// assuming, that we have implemented SQLiteOpenHelper
// to treat sqlite-database
MyDatabaseHelper dbh = new MyDatabaseHelper(
MainActivity.this,
DATABASE_NAME,
null,
DATABASE_VERSION
);
return dbh.getWritableDatabase().rawQuery(
"SELECT * FROM some_table WHERE name=?",
new String[]{ name_param }
);
}
}
}
Another way, is in using of ContentProvider https://developer.android.com/guide/topics/providers/content-providers.html .
In this way you can separate data layer and business logic. Your data access will be abstracted to uris.
Using ContentProvider, you define your queries within it and load data using Uri:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return getContentResolver().query(
YourContentProvider.SOME_URI,
null,
null,
null,
null
);
}
This is convenient way if you have more than one or two customers of your data (Activities or Fragments) - you will use just predefined uris rather repeating sql queries or creating many CursorLoaders descendands.
Moreover, ContentProvider may be used from outside your app if you want.
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 am using a Loader in my application and based on the result I get from the query I perform on COntacts using this Loader I perform some calculations and store them back in a Sqlite DB. I want this operation to be Asynchronous, however I am confused between using an Async task, as I have lot of different data types to return or should I use a simple handler or an AsyncTaskLoader, I want it to be simple as I am new to Loaders. I tried to search around for examples of AsyncTaskLoader but it seems rocket science, a basic and simple functional example of any of the three in the context of my scenario would be a lot helpful.
If you wish to use AsyncTaskLoader, here's a nice sample for you.
EDIT: I've decided to make a simpler solution (based on this repo):
public abstract class AsyncTaskLoaderEx<T> extends AsyncTaskLoader<T> {
private static final AtomicInteger sCurrentUniqueId = new AtomicInteger(0);
private T mData;
public boolean hasResult = false;
public static int getNewUniqueLoaderId() {
return sCurrentUniqueId.getAndIncrement();
}
public AsyncTaskLoaderEx(final Context context) {
super(context);
onContentChanged();
}
#Override
protected void onStartLoading() {
if (takeContentChanged())
forceLoad();
//this part should be removed from support library 27.1.0 :
//else if (hasResult)
// deliverResult(mData);
}
#Override
public void deliverResult(final T data) {
mData = data;
hasResult = true;
super.deliverResult(data);
}
#Override
protected void onReset() {
super.onReset();
onStopLoading();
if (hasResult) {
onReleaseResources(mData);
mData = null;
hasResult = false;
}
}
protected void onReleaseResources(T data) {
//nothing to do.
}
public T getResult() {
return mData;
}
}
Usage:
in your activity:
getSupportLoaderManager().initLoader(TASK_ID, TASK_BUNDLE, new LoaderManager.LoaderCallbacks<Bitmap>() {
#Override
public Loader<Bitmap> onCreateLoader(final int id, final Bundle args) {
return new ImageLoadingTask(MainActivity.this);
}
#Override
public void onLoadFinished(final Loader<Bitmap> loader, final Bitmap result) {
if (result == null)
return;
//TODO use result
}
#Override
public void onLoaderReset(final Loader<Bitmap> loader) {
}
});
inner static class , or a normal class:
private static class ImageLoadingTask extends AsyncTaskLoaderEx<Bitmap> {
public ImageLoadingTask (Context context) {
super(context);
}
#Override
public Bitmap loadInBackground() {
//TODO load and return bitmap
}
}
Update: starting from support library 27.1.0, things changed a bit (link here) :
In version 27.1.0, onStartLoading() is called every time the Activity
is started. Since you call deliverResult() in onStartLoading(), you
trigger onLoadFinished(). This is Working as Intended.
You should remove your call to deliverResult() from onStartLoading()
as it is not needed (Loaders already deliver results computed in
loadInBackground() without any additional work needed on your part).
I've updated the code above for this change.
EDIT:
Updated, kotlin version can be found here.
Since Honeycomb and the v4 Compatibility Library it is possible to use AsyncTaskLoader. From what I understand, the AsyncTaskLoader can survive through config changes like screen flips. But using AsyncTask you can mess up with configuration changes.
Key information: AsyncTaskLoader is subclass of Loader. This class performs the same function as the AsyncTask, but a bit better, it can also be useful in handling configuration changes (screen orientation).
A very good example and explanation is given here.
http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html
Google has a pretty good example directly in the API Docs.
Android Design Patterns provides some more detail and the reasoning behind Loaders.
This tutorial will definetly help You. http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html
Here's step by step tutorial to implement AsyncTaskLoader. or check out this same article on Medium
Implement LoaderManager.LoaderCallbacks<String> on MainActivity and create a static int to uniquely identify your loader and create a String key to pass string url to your loader
public class MainActivity extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<String>{
public static final int OPERATION_SEARCH_LOADER = 22;
public static final String OPERATION_QUERY_URL_EXTRA = "query";
//...}
Override onCreateLoader,onLoadFinishedand onLoaderReset functions inside MainActivity
#Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
//Here we will initiate AsyncTaskLoader
return null;
}
#Override
public void onLoadFinished(Loader<String> loader, String operationResult) {
//Think of this as AsyncTask onPostExecute method, the result from onCreateLoader will be available in operationResult variable and here you can update UI with the data fetched.
Log.d("MAINACTIVITY","result : "+ operationResult);
}
#Override
public void onLoaderReset(Loader<String> loader) {
//Don't bother about it, Android Studio will override it for you
}
inside onCreateLoader() return a new AsyncTaskLoader<String> as an anonymous inner class with this as the constructor's parameter and override loadInBackground & onStartLoading inside anonymous
inner class
#Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
return new AsyncTaskLoader<String>(this) {
#Override
public String loadInBackground() {
//Think of this as AsyncTask doInBackground() method, here you will actually initiate Network call
return null;
}
#Override
protected void onStartLoading() {
//Think of this as AsyncTask onPreExecute() method,start your progress bar,and at the end call forceLoad();
forceLoad();
}
};
}
Inside loadInBackground make a network call using HTTPUrlConnection or OKHttp or anything that you use.
#Override
public String loadInBackground() {
String url = args.getString(OPERATION_QUERY_URL_EXTRA);//This is a url in string form
if (url!=null&&"".equals(url)) {
return null;//if url is null, return
}
String operationResult="";
try {
operationResult = NetworkUtils.getResponseFromHttpUrl(url);//This just create a HTTPUrlConnection and return result in strings
} catch (IOException e) {
e.printStackTrace();
}
return operationResult;
}
Inside onCreate initialize the loader with OPERATION_SEARCH_LOADER as the ID, null for the bundle, and this for the context
getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);
Now call this method, whenever and wherever you want to trigger the loader
private void makeOperationSearchQuery(String url) {
// Create a bundle called queryBundle
Bundle queryBundle = new Bundle();
// Use putString with OPERATION_QUERY_URL_EXTRA as the key and the String value of the URL as the value
queryBundle.putString(OPERATION_QUERY_URL_EXTRA,url);
// Call getSupportLoaderManager and store it in a LoaderManager variable
LoaderManager loaderManager = getSupportLoaderManager();
// Get our Loader by calling getLoader and passing the ID we specified
Loader<String> loader = loaderManager.getLoader(OPERATION_SEARCH_LOADER);
// If the Loader was null, initialize it. Else, restart it.
if(loader==null){
loaderManager.initLoader(OPERATION_SEARCH_LOADER, queryBundle, this);
}else{
loaderManager.restartLoader(OPERATION_SEARCH_LOADER, queryBundle, this);
}
}
Walla, you are done, just to remind you NetworkUtils.getResponseFromHttpUrl(url); is my custom function which take string convert it into URL which in turn used to create HTTPUrlConnection
I like this brief example AsyncTask and AsyncTaskLoader.
class FooLoader extends AsyncTaskLoader {
public FooLoader(Context context, Bundle args) {
super(context);
// do some initializations here
}
public String loadInBackground() {
String result = "";
// ...
// do long running tasks here
// ...
return result;
}
}
class FooLoaderClient implements LoaderManager.LoaderCallbacks {
Activity context;
// to be used for support library:
// FragmentActivity context2;
public Loader onCreateLoader(int id, Bundle args) {
// init loader depending on id
return new FooLoader(context, args);
}
public void onLoadFinished(Loader loader, String data) {
// ...
// update UI here
//
}
public void onLoaderReset(Loader loader) {
// ...
}
public void useLoader() {
Bundle args = new Bundle();
// ...
// fill in args
// ...
Loader loader =
context.getLoaderManager().initLoader(0, args, this);
// with support library:
// Loader loader =
// context2.getSupportLoaderManager().initLoader(0, args, this);
// call forceLoad() to start processing
loader.forceLoad();
}
}
Simplifying hard, maybe
private void loadContent() {
getLoaderManager().initLoader(1000, new Bundle(),
new LoaderManager.LoaderCallbacks<List<String>>() {
#Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
return new AsyncTaskLoader<List<String>>(MainActivity.this.getApplicationContext()) {
#Override
public List<String> loadInBackground() {
Log.i("B", "Load background data ");
ArrayList<String> data = new ArrayList<>();
for (int i = 0; i < 5000; i++) {
data.add("Data." + i + " " + System.currentTimeMillis());
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return data;
}
};
}
#Override
public void onLoadFinished(Loader<List<String>> loader, List<String> data) {
Log.i("B", "Here are your data loaded" + data);
if (!loader.isAbandoned()) {
mAdapter.setData(data); // Read also about RecyclerView
}
}
#Override
public void onLoaderReset(Loader<List<String>> loader) {
Log.i("B", "Loader reset");
}
}).forceLoad();
}
#Override
protected void onDestroy() {
// Abandon the loader so that it should not attempt to modify already dead GUI component
getLoaderManager().getLoader(1000).abandon();
super.onDestroy();
}
Make this part of your Activity. The sample simulates delay, but makes new entries easy to recognize because they will have the different time stamp suffix. Of course you also need RecyclerView to display the data, the answer to this question seems very good.
The loader in this example is the inner class that keeps the reference to the parent activity. It must be external static class without such reference in production.
I prefer using Bolts-Android. it is very easy.
https://github.com/BoltsFramework/Bolts-Android
Task.callInBackground(new Callable<Void>() {
public Void call() {
// Do a bunch of stuff.
}
}).continueWith(...);
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.