I have an asynctask and inside doInBackground, I don't have a for/while loop. Instead, I have a different class for it and that class generates a list using a for loop. So how can I update UI with onProgressUpdate?
Here is:
#Override
protected List<MyObject> doInBackground(Void... voids) {
IAppLogic appLogic = new AppLogic(mContext);
List<MyObject> list = appLogic.getApps(ALL_APPS);
return list;
}
MyObject is a custom object and IAppLogic is the interface of the class that gets installed applications.
You can implement this by giving the getApps()-method a callback-parameter. Pseudo code:
interface AppFoundCallback {
public onAppFound(App app, int progress);
}
// in AppLogic.getApps:
public getApps(String filter, AppFoundCallback callback)
for (int i = 0; i < listOfApps.length; i++) {
// Do the work you need to do here.
int progress = (i / listOfApps.length) * 100;
callback.onAppFound(app, progress);
}
}
// in your Task:
class Task extends AsyncTask implements AppFoundCallback {
// Implement the callback
#Override
onAppFound(App app, int progress) {
this.publishProgress(progress);
}
// Setup and register the listener
#Override
protected void doInBackground() {
// Ohter stuff
List<MyObject> list = appLogic.getApps(ALL_APPS, this);
}
// Render progress updates on the UI
#Override
protected void onProgressUpdate(Integer... progress) {
progressView.setProgress(progress[0]);
}
}
Simply put: your code notifies the caller of the getApps()-method every time you find something. This will then be published as a progress-update. The publishProgress()-method is part of AsyncTask and will take care of calling onProgressUpdate() on the UI-Thread, so that you can simply update your views.
Related
Like I need to check whether a username already existed in database, and the program won't continue until response arrives.
it seems you need asynctask, this is an example how to use it..
public class YourFragment extends Fragment implements YourAsyncTask.YourInterface {
public YourFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_your, container, false);
//do your initial things
.
.
.
YourAsyncTask yourAsyncTask = new YourAsyncTask(this);
yourAsyncTask.execute();
return view;
}
#Override
public void onJobFinishListener(YourDataType yourData) {
//when this method is trigered by your asynctask
//it means that you are in ui thread and update your ui component
//TODO: update ui component with your data
}
}
and this is the asynctask;
public class YourAsyncTask extends AsyncTask {
private YourInterface yourInterfaceListener;
private YourDataType yourData; //this data should be calculated in doInBackground method and send via interface
public YourAsyncTask(YourInterface yourInterfaceListener) {
this.yourInterfaceListener = yourInterfaceListener;
}
#Override
protected Object doInBackground(Object[] objects) {
//do your all background tasks here
.
.
.
yourData = do something here to fill your data..
return null;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
yourInterfaceListener.onJobFinishListener(yourData);
}
public interface YourInterface{
void onJobFinishListener(YourDataType yourData);
}
}
Use AsyncTask to perform network operations
public class TalkToServer extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
/*
* do things before doInBackground() code runs
* such as preparing and showing a Dialog or ProgressBar
*/
}
#Override
protected void onProgressUpdate(Void... values) {
/*
* updating data
* such a Dialog or ProgressBar
*/
}
#Override
protected Void doInBackground(Void... params) {
//do your work here
return null;
}
#Override
protected void onPostExecute(Void result) {
/*
* do something with data here
* display it or send to mainactivity
* close any dialogs/ProgressBars/etc...
*/
}
}
And you can execute it using
TalkToServer myTask = new MyTask(); // can add params for a constructor if needed
myTask.execute(); // here is where you would pass data to doInBackground()
After the network call is done and response obtained onPostExecute() is called. If the AsyncTask is in inner class of your activity you can update UI inside onPostExecute(). Otherwise you can use an Interface to create a callback to activity.
You can find AsyncTask Docs here
You have to use async task. Then you can uae it your_async_class.execute().get() in your activiry this ..get method you want to it. Main thread will wait your async task. You have to change your_async_class extends AsyncTask
My app has a NavigationDrawer activity that swaps fragments based on the selection. One of these fragments contains a listview with several buttons. The listview items themselves are not selectable, but the I need to handle the button clicks, which I'm able to do capture successfully in my custom adapter.
Here is my issue: some button clicks need to trigger an async task to call REST api urls. I'm not sure how to do this. I tried calling a static method on my fragment that would instantiate an instance of a private class (extends AsyncTask), but that won't work. I thought about making a private class inside my custom adapter that extends from AsyncTask, but that doesn't feel right. What's the proper way to do something like this?
It wouldn't be different then Async tasks from anywhere else. Take a look at the documentation
http://developer.android.com/intl/es/reference/android/os/AsyncTask.html
Create a class for your task
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Then create an instance of it in your onClick event
new DownloadFilesTask().execute(url1, url2, url3);
Here is the class which extends Asynctask
public class webservice extends AsyncTask<Object, Object, Object>{
private Context c;
public webservice(int mode)
{
}
public void set_context(Context c)
{this.c =c;
}
public void call_webservice_login()
{
// procedure to call webservice etc...
}
#Override
protected Object doInBackground(Object... arg0) {
// TODO Auto-generated method stub
call_webservice_login();
return 0;
}
protected void onProgressUpdate(Integer... progress) {
//update your UI, etc
}
protected void onPreExecute(){
super.onPreExecute();
pgd = new ProgressDialog(c);
pgd.setMessage("Loading...");
pgd.show();
}
protected void onPostExecute(Object result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
}
you can call it like this.
webservice wb1 = new webservice(1);
//wb1.set_context(this);
wb1.execute();
In order to have a fully functional ListView, you need an Adapter.
So, the best approach here would be:
In the Adapter constructor, declare a OnClickListener that calls the AsyncTask.
In the getView()method of your Adapter, add the listener to the buttons that must perform any action: myButton.setOnClickListener(mListener).
If you need any special parameters for different buttons, I recommend adding some flag or object using myView.setTag(Object) to the button in the getView() method. The listener receives a View parameter when called, which is the clicked view. Simply use view.getTag() in your listener code to figure which button was pressed.
I know how to use AsyncTask to download file, create a zip file or so.. as I call publishProgress() in my loop.
I got stuck when doInBackground() has a single slow line, no loops here, just creating an object where its constructor has slow loops.
I'm not sure about the reasonable way of updating progress in such case.
Here's a sample code:
public class Session {
private QQActivity activity;
public int createdParts;
public DailyClass daily;
private void checkDaily() {
if(!isDailyReady){
new SetAsyncQQDaily().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
class SetAsyncQQDaily extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
String sdq = null;
daily = new DailyClass(Session.this); //Very very Slow!
// Do other network http
sdq = new String(Base64.encode(bos.toByteArray(),Base64.DEFAULT));
// Do some work
return sdq;
}
#Override
protected void onPostExecute(String sdq) {
//Never mind
}
#Override
protected void onPreExecute() {
Toast.makeText(activity,"Preparing the daily. Get ready!",Toast.LENGTH_LONG).show();
}
#Override
protected void onProgressUpdate(Void... values) {
//TODO: Update Value of leftBar
activity.leftBar.setProgress((100*createdParts)/Utils.DAILY_PART_COUNT);
}
}
}
In the slow constructor class, I can set-back an integer of the current progress: createdParts, but cannot call publishProgress.
public class DailyClass implements Serializable {
public DailyClass(Session session){
for(int i=1;i<=partCount;i++ ){ //Very slow loop
session.createdParts = i; //TODO: reflect value to progress bar!?
for(int j=0;j<questionsCount;j++){
objects[i-1][j] = createDefined(i);
}
Log.d("Daily","created part"+i);
}
}
//Bla .. !
}
I also though of passing the object of the AsyncTask to the slow constructor in order to call publishProgress() from there, but cannot. As publishProgress() is accessible only from doInBackground()
What's the best practice?
my issue is the same as this
Instance variable of Activity not being set in onPostExecute of AsyncTask or how to return data from AsyncTask to main UI thread
but i want to send the data back to the same calling activity. Doesnt startActivity for intents always restart the activity
On option is to use listeners, where you create an interface that your activity implents, something like:
public interface AsyncListener {
public void doStuff( MyObject obj );
}
That way, if you're subclassing AsyncTask, it is easy to add this listener, then in onPostExecute(), you could do something like:
protected void onPostExecute( MyObject obj ) {
asyncListener.doStuff(obj);
}
This depends on your class structure, but if your AsyncTask is a class within your Activity then you can reference methods of that activity.
What you would do is in your onPostExecute method call a function of your Activity that passes some data that was retrieved in the AsyncTask to the activity where you can then use it..
The code would look like this
class YourActivity extends Activity {
private static final int DIALOG_LOADING = 1;
public void onCreate(Bundle savedState) {
setContentView(R.layout.yourlayout);
showDialog(DIALOG_LOADING);
new LongRunningTask1().execute(1,2,3);
}
protected Dialog onCreateDialog(int dialogId) {
switch(dialogId) {
case DIALOG_LOADING:
ProgressDialog pDialog = new ProgressDialog(this);
pDialog.setTitle("Loading Data");
pDialog.setMessage("Loading Data, please wait...");
return pDialog;
default:
return super.onCreateDialog(dialogId);
}
}
private void onBackgroundTaskDataObtained(List<String> results) {
dismissDialog(DIALOG_LOADING);
//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);
}
}
}
So the typical flow is like this, set the view of the current page, and then show a progress dialog. Right after that start the async task (or whenever, it doesn't matter really).
After your async task is complete, call a function of the activity and pass it the data.
Don't use shared data within the async task or you risk issues with threading.. Instead once you are done with it pass it to the activity. If you want to update the view progressively while doing work you can use on onProgressUpdate
I am having a problem with ProgressDialog UI being frozen when I start the action in the AsyncTask.
My problem is somewhat different than the bunch of other similar question because the my background task consists of two parts:
- first part (loadDB()) is related to the database access
- second part (buildTree()) is related to building the ListView contents and is started with runOnUiThread call
The progress dialog is correctly updated during the 1st part of the task, but not during the 2dn part.
I tried moving the buildTree part in the AsyncTask's onPostExecute but it doesn't help, this part of the code still causes the progress to freeze temporarily until this (sometimes quite lengthy) part of the work is done. I can not recode the buildTree part from scratch because it is based on external code I use.
Any tips on how to resolve this? Is there a method to force updating some dialog on screen?
The code goes here:
public class TreePane extends Activity {
private ProgressDialog progDialog = null;
public void onCreate(Bundle savedInstanceState) {
// first setup UI here
...
//now do the lengthy operation
new LoaderTask().execute();
}
protected class LoaderTask extends AsyncTask<Void, Integer, Void>
{
protected void onPreExecute() {
progDialog = new ProgressDialog(TreePane.this);
progDialog.setMessage("Loading data...");
progDialog.show();
}
protected void onPostExecute(final Void unused) {
if (progDialog.isShowing()) {
progDialog.dismiss();
}
}
protected void onProgressUpdate(Integer... progress) {
//progDialog.setProgress(progress[0]);
}
protected Void doInBackground(final Void... unused)
{
//this part does not block progress, that's OK
loadDB();
publishProgress(0);
//long UI thread operation here, blocks progress!!!!
runOnUiThread(new Runnable() {
public void run() {
buildTree();
}
});
return null;
}
}
public void buildTree()
{
//build list view within for loop
int nCnt = getCountHere();
for(int =0; i<nCnt; i++)
{
progDialog.setProgress(0);
//add tree item here
}
}
}
Don't run your whole buildTree() method inside the UI thread.
Instead, run only the changes you want to make to the UI in the UI thread:
protected Void doInBackground(final Void... unused)
{
//this part does not block progress, that's OK
loadDB();
publishProgress(0);
buildTree();
return null;
}
And then:
public void buildTree()
{
//build list view within for loop
int nCnt = getCountHere();
for(int =0; i<nCnt; i++)
{
progDialog.setProgress(0);
runOnUiThread(new Runnable() {
public void run() {
// update your UI here and return
}
});
// now you can update progress
publishProgress(i);
}
}
You should call AsyncTask's publishProgress method and not the progDialog.setProgress(0); as you call.
Also the buildTree shouln't run on the UI thread since it will block it.
run the logic from the doInBackground method.
note that you don't actually build the ListView, rather you should build it's data model.
look here
something like this:
protected Void doInBackground(final Void... unused)
{
//this part does not block progress, that's OK
loadDB();
publishProgress(0);
buildTree();
}
public void buildTree()
{
//build list view within for loop
int nCnt = getCountHere();
for(int =0; i<nCnt; i++)
{
publishProgress(i); //for exmple...
//add tree item here
}
}