I am trying to unzip a folder using Android's AsyncTask. The class (called Decompress) is an inner class of Unzip where Unzip itself is a non-Activity class. The pseudo-code is:
public class Unzip {
private String index;
private String unzipDest; //destination file for storing folder.
private Activity activity;
private boolean result; //result of decompress.
public void unzip(String loc) {
Decompress workThread = new Decompress(loc, activity);
workThread.execute();
if(unzip operation was successful) {
display(index);
}
//Class Decompress:
class Decompress extends AsyncTask<Void, Integer, Boolean> {
private ProgressDialog pd = null;
private Context mContext;
private String loc;
private int nEntries;
private int entriesUnzipped;
public Decompress(String location, Context c) {
loc = location;
mContext = c;
nEntries = 0;
entriesUnzipped = 0;
Log.v(this.toString(), "Exiting decompress constructor.");
}
#Override
protected void onPreExecute() {
Log.v(this.toString(), "Inside onPreExecute.");
pd = new ProgressDialog(mContext);
pd.setTitle("Unzipping folder.");
pd.setMessage("Unzip in progress.");
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
Log.v(this.toString(), "Showing dialog and exiting.");
pd.show();
}
#Override
protected Boolean doInBackground(Void... params) {
//unzip operation goes here.
unzipDest = something; //unzip destination is set here.
if(unzip operation is successful) {
result = true;
index = url pointing to location of unzipped folder.
} else {
result = false;
}
}
#Override
protected void onPostExecute(Boolean result) {
if(result) {
if(pd != null) {
pd.setTitle("Success");
pd.setMessage("folder is now ready for use.");
pd.show();
pd.dismiss();
pd = null;
Log.v(this.toString(), "Unzipped.");
index = unzipDest + "/someURL";
Log.v(this.toString(), "index present in: " + index);
}
} else {
pd = ProgressDialog.show(mContext, "Failure", "Cannot unzip.");
pd.dismiss();
}
}
}
Problems I am facing:
1. The value of unzipDest and index, updated in doInBackground, remain null to Unzip and all its objects. How can I ensure that the values remain updated?
2. I know that doInBackground occurs in a thread separate from the main UI thread. Does that mean that any values updated in the new thread will be lost once that thread returns?
How can I ensure that the values remain updated?
They will be updated since they are member variables. However, since AsyncTask is asynchrounous, they might not be updated yet when you check them. You can use an interface to create a callback when these values are updated. This SO answer covers how to do this
Does that mean that any values updated in the new thread will be lost once that thread returns?
No they shouldn't be "lost". They probably just haven't been changed in the AsyncTask when you check them.
Since this isn't your actual code I can't see when you are trying to access them but you can use the interface method or call the functions that need these values in onPostExecute(). You also can do a null check before trying to access them. It just depends on the functionality and flow that you need as to which is the best way. Hope that helps.
Edit
In the answer I linked to, you tell the Activity that you will use that interface and override its method(s) with implements AsyncResponse in your Activity declaration after creating the separate interface class
public class MainActivity implements AsyncResponse{
then, in your Activity still, you override the method you declared in that class (void processFinish(String output);)
#Override
void processFinish(String output){ // using same params as onPostExecute()
//this you will received result fired from async class of onPostExecute(result) method.
}
then this is called in onPostExecute() when the listener sees that it is done with delegate.processFinish(result); delegate is an instance of AsyncResponse (your interface class)
public class AasyncTask extends AsyncTask{
public AsyncResponse delegate=null;
#Override
protected void onPostExecute(String result) {
delegate.processFinish(result);
}
Interface example taken from linked answer above and adjusted/commented for clarity. So be sure to upvote that answer if it helps anyone.
Related
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?
I am trying to download a file at android using AsyncTask. Because I want to download many files I want to pass the url of the file by arguments. Also I want to change the UI when a user clicks the button that triggers the AsyncTask. As I figured out, apart from the URL I need to pass and the Activity itself so I can have access to it's views so I can change them. And my problem is this how can make the AsyncTask take multiple and different arguments.
Questions:
1.The only way to do this I think is to pass as Object the first argument so anything can pass, right?
public final Example extends AsyncTask<Objects, String, String>
2.When I call the AsyncTask how do I write the function?
Example a=new Example(??????); //I want to pass both a URL and something to give me access to the UI
a.execute(?????);
3. How can I use the "something" stated above argument to change visibility for example at a view?
Thx In advance for your time
Try this..
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
new Example("URL","something").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[]{null});
else
new Example("URL","something").execute(new String[]{null});
and Example AsyncTask Class
public class Example extends AsyncTask<String, Void, JSONObject> {
// variables passed in:
String url;
String something;
// constructor
public Example(String url, String something) {
this.url = url;
this.something = something;
}
#Override
protected void onPreExecute() {
}
#Override
protected JSONObject doInBackground(String... args) {
}
#Override
protected void onPostExecute(JSONObject jObjOut) {
}
}
AsyncTask takes 3 generic types:
1st is the argument which you send to execution;
2nd is for publishing updates on UI
3rd is for returning result
If you want to skip one or all of them use Void class:
example: new AsyncTask();
If you want to use your own constructor in AsyncTask you should define it in your own class that extends AsyncTask, something like this:
import android.app.Activity;
import android.os.AsyncTask;
public class Example extends AsyncTask<Object, String, String>{
private Object data;
private String data1;
private String data2;
private Activity activity;
public Example(Object data, String data1, String data2, Activity activity){
this.data = data;
this.data1 = data1;
this.data2 = data2;
this.activity = activity;
}
#Override
protected String doInBackground(Object... arg0) {
//Do here all your needs except UI updating
//transfer data you need for UI updating like this:
publishProgress("UI update data argument");
return null;
}
#Override
protected void onProgressUpdate(String... arg){
super.onProgressUpdate(arg);
//Update your UI using activity
}
}
To execute your Async task :
new Example(data, data1, data2, activity).execute(object);
You can use a parameterized constructor to achieve the same. But be aware that if you have multiple arguments you would end up creating multiple constructors. Since you need to modify multiple activities in response you need to pass a context as well.
query = UtilityClass.getQueries(UtilityClass.CHECKRIDE, paramsList);
new Example(HomeActivity.this, UtilityClass.CHECKRIDE).execute(query);
This is how you can create a query/url and pass it to the AsyncTask.
A Sample AsyncTask :
public class Example extends AsyncTask<String , Void, String> {
public Example(Context contextVar, int calloutTypeVar){
context = contextVar;
CALLOUTTYPE = calloutTypeVar;
}
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(context, "Please wait", "Loading...");
}
#Override
protected String doInBackground(String...sqlQueryList) {
String result = "";
String sqlQuery = sqlQueryList[0];
result = UtilityClass.doHttpCallout(sqlQuery);
return result;
}
// Handle the response from the callout
#Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
progressDialog.dismiss();
switch (CALLOUTTYPE) {
case UtilityClass.SCHEDULERIDE:
ResponseHandlerClass.scheduleRide(result, context);
break;
}
}
I have implemented a response handler to handle the response from various callout results.
Hope this helps in handling your response from different urls.
The idea behind using AsyncTask is to avoid penalizing the responsiveness of the UI when performing blocking operations such as the file download of your scenario. When you use AsyncTask, the doInBackground method executes in another thread than the UI and the UI can not be modified from it so there is no point in passing any UI related data to it.
In order to modify your UI upon your background work completion, you can use the onPostExecute method. As #Tamilan suggested, you may pass the Activity object to the constructor of your AsyncTask if you want to use it there.
An example task could be as follows. Notice that you may replace String with URL, or even Object if you still want to pass different object types to the doInBackground method. Also, this example does not cover error handling beyond returning a simple boolean.
private class ExampleTask extends AsyncTask<String, Integer, Boolean>
{
private Activity mActivity;
public ExampleTask(Activity activity)
{
mActivity = activity;
}
#Override
protected Boolean doInBackground(String... params) {
// this is executed in a background thread
boolean success = true;
// do your background operations
publishProgress(50);
// more operations
return Boolean.valueOf(success);
}
#Override
protected void onProgressUpdate(Integer... progress) {
updateProgressbar(progress[0]); // update UI
}
#Override
protected void onPostExecute(Boolean result) {
// this is executed on your UI thread
if (!result)
// show error
else
// modify your UI
}
}
In order to execute this task:
ExampleTask task = new ExampleTask(anActivity);
task.execute(new String[] { "oneUrl", "anotherUrl" });
I hate inner class.
I've a main activity who launches a 'short-life' AsyncTask.
AsyncTask is in a separate file, is not an inner class of main activity
I need async task updates a textView from main Activity.
I know i can update a TextView from onProgressUpdate, if AsyncTask is a inner class
But how from an external, indipendent, async task ?
UPDATE: This looks like working :
In acitivty i call the task
backgroundTask = new BackgroundTask(this);
backgroundTask.execute();
In the constructor i've
public BackgroundTask(Activity myContext)
{
debug = (TextView) myContext.findViewById(R.id.debugText);
}
where debug was a private field of AsyncTask.
So onProgressUpdate I can
debug.append(text);
Thanks for all of you suggestions
AsyncTask is always separate class from Activity, but I suspect you mean it is in different file than your activity class file, so you cannot benefit from being activity's inner class. Simply pass Activity context as argument to your Async Task (i.e. to its constructor)
class MyAsyncTask extends AsyncTask<URL, Integer, Long> {
WeakReference<Activity> mWeakActivity;
public MyAsyncTask(Activity activity) {
mWeakActivity = new WeakReference<Activity>(activity);
}
...
and use when you need it (remember to NOT use in during doInBackground()) i.e. so when you would normally call
int id = findViewById(...)
in AsyncTask you call i.e.
Activity activity = mWeakActivity.get();
if (activity != null) {
int id = activity.findViewById(...);
}
Note that our Activity can be gone while doInBackground() is in progress (so the reference returned can become null), but by using WeakReference we do not prevent GC from collecting it (and leaking memory) and as Activity is gone, it's usually pointless to even try to update it state (still, depending on your logic you may want to do something like changing internal state or update DB, but touching UI must be skipped).
Using Interface
1) Create one Interface
public interface OnDataSendToActivity {
public void sendData(String str);
}
2) Implements it in your Activity
public class MainActivity extends Activity implements OnDataSendToActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
new AsyncTest(this).execute(new String[]{"AnyData"}); // start your task
}
#Override
public void sendData(String str) {
// TODO Auto-generated method stub
}
}
3) Create constructor in AsyncTask(Activity activity){}
Register your Interface in AsyncTask file
and call interface method as below.
public class AsyncTest extends AsyncTask<String, Integer, String> {
OnDataSendToActivity dataSendToActivity;
public AsyncTest(Activity activity){
dataSendToActivity = (OnDataSendToActivity)activity;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
dataSendToActivity.sendData(result);
}
}
Here, your OnPostExecute will call after all task done by AsyncTask and will get "result"
as a parameter, returned by doInBackground(){ return "";}.
While "dataSendToActivity.sendData(result);" it will call activity's overrided method "public void sendData(String str) {}".
An edge case to remember: Be sure to pass this, i.e. you current activity's context to AsyncTask and not create another instance of your activity, otherwise your Activity will be destroyed and new one is created.
Make an static function in your activity class passing context in it to update your text view and then call this function in your AsynkTask class to update.
In Activity class:
public static void updateTextView(){
//your code here
}
In AynckTask class call this function.
Just pass the context (activity or whatever) to your AsyncTask in a constructor and then in onSuccess or onProgressUpdate call whatever you need on the context.
I wrote a small extension to AsyncTask for this kind of scenario. It allows you to keep your AsyncTask in a separate class, but also gives you convenient access to the Tasks's completion:
public abstract class ListenableAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result>{
#Override
protected final void onPostExecute(Result result) {
notifyListenerOnPostExecute(result);
}
private AsyncTaskListener<Result> mListener;
public interface AsyncTaskListener<Result>{
public void onPostExecute(Result result);
}
public void listenWith(AsyncTaskListener<Result> l){
mListener = l;
}
private void notifyListenerOnPostExecute(Result result){
if(mListener != null)
mListener.onPostExecute(result);
}
}
So first you extend ListenableAsyncTask instead of AsyncTask. Then in your UI code, make a concrete instance and set listenWith(...).
The Question has already been answered, still im posting how it should be done i guess..
Mainactivity class
public class MainActivity extends Activity implements OnClickListener
{
TextView Ctemp;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Ctemp = (TextView) findViewById(R.id.Ctemp);
doConv = (Button) findViewById(R.id.doConv);
doConv.setOnClickListener(this);
}
#Override
public void onClick(View arg0) // The conversion to do
{
new asyncConvert(this).execute();
}
}
now in the async class
public class asyncConvert extends AsyncTask<Void, Void, String>
{
SoapPrimitive response = null;
Context context;
public asyncConvert(Context callerclass)
{
contextGUI = callerclass;
}
.
.
.
.
protected void onPostExecute(String result)
{
((MainActivity) contextGUI).Ctemp.setText(result); // changing TextView
}
}
/**
* Background Async Task to Load all product by making HTTP Request
* */
public static class updateTExtviewAsyncTask extends AsyncTask<String, String, String> {
Context context;
ProgressDialog pDialog;
String id, name;
String state_id;
//--- Constructor for getting network id from asking method
public updateTExtviewAsyncTask(Context context,String id,String city)
{
context = context;
state_id = id;
city_name = city;
}
/* *
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute()
{
super.onPreExecute();
pDialog = ProgressDialog.show(context, "","Please wait...", true, true);
pDialog.show();
}
/**
* getting All products from url
* */
protected String doInBackground(String... args)
{
return null;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url) {
YourClass.UpdateTextViewData("Textview data");
}
}
// place this code inside your activity class and also declare updating textview static
public static void UpdateTextViewData(String tvData)
{
tv.setText(tvData);
}
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
Quick summary: I'm making an application that parses a binary file, stores vertices and their properties, and displays them in openGL. I'm trying to implement a ProgressDialog while it parses, but I'm having considerable trouble. I've tried implementing this in many places, but this is my current setup:
public class PointViewer extends Activity{
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
filePath = extras.getString("file_path");
mGLView = new GLSurfaceView(this);
theRenderer = new OpenGLRenderer();
mGLView.setRenderer(theRenderer);
//Parse the file and set the arrays
theRenderer.setLAS(filePath);
setContentView(mGLView);
}
...
}
The Rendering class...
public class OpenGLRenderer extends Activity implements GLSurfaceView.Renderer {
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void setLAS (String fileName){
new ProgressTask(this).execute();
}
...
/*
* The class for the progress dialog
*/
private class ProgressTask extends AsyncTask<String, Void, Boolean> {
private ProgressDialog dialog;
private Context context;
//private List<Message> messages;
public ProgressTask(Context ctx) {
context = ctx;
dialog = new ProgressDialog(context);
}
/** progress dialog to show user that the backup is processing. */
protected void onPreExecute() {
this.dialog.setMessage("Progress start");
this.dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
this.dialog.show();
}
#Override
protected void onPostExecute(final Boolean success) {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (success) {
Toast.makeText(context, "OK", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Error", Toast.LENGTH_LONG).show();
}
}
protected Boolean doInBackground(final String... args) {
try{
ptCloud = new PointCloud(args[0]);
...
dialog.setProgress(percentParsed);
return true;
} catch (Exception e){
Log.e("tag", "error", e);
return false;
}
}
}
When I call dialog = new ProgressDialog(context); It errors on a null pointer exception, I'm assuming because there is a context issue... Does anyone have any ideas?
First, you shouldn't create OpenGLRenderer yourself, because its an Activity and is supposed to be managed by system. When you create OpenGLRenderer yourself, then this activity has its context incorrectly initialized. And when your create ProgressDialog with invalid context, you receive NullPointerException.
But even if you start OpenGlRenderer correctly, your app will still crash at line:
dialog.setProgress(percentParsed);
You should use publishProgress instead and update ProgressDialog in AsyncTask's onProgressUpdate function. That's because you can't update UI from non-ui thread.
maybe try replaceing "this" with "OpenGLRenderer.this" inside your setLAS() method. It doesn't seem like it from the code you've posted but sometimes if you are making that call from a different object type it will try to pass in an OnClickListener (or whatever object your calling from) instead of an activity, thus the object has no context. Like I said doesn't seem like that is the case, but worth a shot.
Also where are you calling setLAS() from? perhaps post that section of your code too.
Edit:
Try modifying your setLAS() method to have a Context parameter and pass it in from your first activity, and just pass it along from the second activity to the async task instead of using the context from the second activity.