OK I must be missing something here, and I can't seen to place my finger on it. Here is my situation, I create my AsyncTask Externally (good practice and reusable), I have no problem connecting to the server, getting the response to my interface with the onPostExecute();, but taking the value from the interface to the Calling class and retaining that value is giving me a problem. Here is an example of my code.
The interface
interface getData {
void done (ArrayList<String> returnedData);
}
The AsyncTask and calling method using a wrapper class called serverRequests
public void getDataInBackground ( String params, getData retData){
new getDataAsync(params,retData).execute();
}
public class getDataAsync extends AsyncTask<Void,Void,ArrayList<String>> {
String params;
getData retData;
public getDataAsync( String params, getData retData){
this.params=params;
this.retData=retData;
}
#Override
protected ArrayList<String> doInBackground(Void... params) {
ArrayList<String> list = new ArrayList<>();
//Do a bunch of stuff that connects to my server
return list;
}
#Override
protected void onPostExecute(ArrayList<String> returnedData) {
retData.done(returnedData);
super.onPostExecute(returnedData);
}
}
Then my Activity Class that calls this process would be like so.
public class MyActivity extends AppCompatActivity {
ServerRequests serverRequests; // the Wrapper class to call my different AsyncTasks
ArrayList<String> someList;
String params="some kind of params";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_activity);
serverRequests= new serverRequests(this);//this being the context passed
someList=new ArrayList<>();
serverRequests.getDataInBackground(params, new getData() {
#Override
public void done(ArrayList<String> returnedData) {
someList.addAll(returnedData);
//somelist has data here
//used AlertDialogs to confirm data
}
});
//someList is empty here and data is needed to perform other tasks.
// again used AlertDialogs to confirm data
Now in this activity I perform several other AsyncTasks collecting data for the end user. The other task data depends on this first call. I have thought about storing the data on the phone temporarily, then I thought of my wife whose phone is always limited on memory space, that could generate an unwanted error. I thought about performing the other AsyncTask inside of this one but then I would be opening up multiple threads needlessly and wasting CPU on the phone. I have tried other suggestions I found on this site like making the someList static/final but that isn't working either. I have also tried just a simple assignment someList=returnedData. So any suggestions on how to get this value from AsyncTask and into the calling method?
Interface is working.
//access download task data
public interface MyAccessResponse {
void postResult(String asyncResult);
}
//download class
public class DownloadTask extends AsyncTask<String, Void, String> {
public MyAccessResponse delegate=null;
public DownloadTask(MyAccessResponse asyncResponse) {
delegate = asyncResponse;//Assigning call back interfacethrough constructor
}
#Override
protected String doInBackground(String... urls) {
URL url;
HttpURLConnection urlConnection = null;
try {
//read 1st string
url = new URL( urls[0] );
urlConnection = (HttpURLConnection) url.openConnection();
InputStream inpt = urlConnection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inpt));
StringBuilder builder = new StringBuilder();
String aux = "";
while ((aux = reader.readLine()) != null) {
builder.append(aux);
}
return builder.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
Log.i("tms delegate", "access delegate");
//super.onPostExecute(result);
if(result!=null && delegate!=null)
{
delegate.postResult(result);
} else {
Log.i("MyAccess", "You have not assigned IApiAccessResponse delegate");
}
}
} //end of Download Task
In onCreate:
...
DownloadTask task = new DownloadTask(new MyAccessResponse() {
#Override
public void postResult(String asyncResult) {
//html = asyncResult ;
//Log.i("tms postResult", html);
//process result--split point: "/imgs/hr_shadow.gif"
String[] splitResult = asyncResult.split("/imgs/hr_shadow.gif");
//example: <img src="/celebs/rihanna/thumbs/thumb1.jpg" alt="Rihanna" height="95" width="95" border="0" />
Pattern p = Pattern.compile("src=\"(.*?)\"");
Matcher m = p.matcher(splitResult[1]);
while(m.find()) {
celebURLs.add( m.group(1));
Log.i("tms src:", m.group(1));
}
p = Pattern.compile("alt=\"(.*?)\"");
m = p.matcher(splitResult[1]);
while(m.find()) {
celebNames.add( m.group(1));
Log.i("tms name:", m.group(1));
}
createNewQuestion();
}
});
Its perfectly acceptable to run a second AsyncTask in the onPostExecute of the first, if the second depends on the result of the first. You really shouldn't worry about the multiple threads- first off AsyncTasks are all queued on one thread unless you explicitly request otherwise. Secondly, your tasks are likely IO bound. Thirdly, CPU usage is a tiny amount of battery consumption compared to things like GPS and display- you pretty much have to try to make a dent with it.
I found the answer. In the activity class I created a void method to set the value compared to the context. Then when I got my value I sent it to that method. Here is the example.
protected void setValue(ArrayList<String> list){
this.someList=list;}
then in my original class activity where I had
someList.addAll(returnedData);
I replaced it with
setValue(returnedData);
Thus making the value accessible through out the whole class
Related
I have an activity class which is calling background AsyncTask twice like
BackgroundTask bt1 = new BackgroundTask(this, ApiHelper.GET);
bt.execute(url1);
BackgroundTask bt2 = new BackgroundTask(this, ApiHelper.POST, params);
bt.execute(url2);
one is used to GET the data and another is used to POST the data to server.
Constructors of this AsyncTask is as follows
public BackgroundTask(Context context, String method) {
this.context = context;
this.method = method;
this.callback = (onBackgroundTaskListener<String>) context;
}
public BackgroundTask(Context context, String method, ArrayList<NameValuePair> params) {
this.context = context;
this.method = method;
this.callback = (onBackgroundTaskListener<String>) context;
this.params = params;
}
Now MainActivity.class is implemented by onBackgroundTaskListener<String> interface as
public interface onBackgroundTaskListener<T> {
void onTaskComplete(T result);
}
onPostExecute of AsyncTask class, String result is returned or passed back to the calling class, shown below.
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
callback.onTaskComplete(result);
}
Now I have one method (onTaskComplete) in MainActivity to tackle two responses coming from one background task. I would like to use the condition to figure out which execution is returned a result like...
#Override
public void onTaskComplete(String result) {
if (execution == 1) {
// Parse the GET result
} else if(execution == 2) {
// Parse the POST result
}
}
I don't want to implement the multiple AsyncTask and would like to achieve it using single AsyncTask and called this AsyncTask multiple times within single activity. What should be the possible way to implement this.
enum BackgroundType{ GET, POST }
public interface onBackgroundTaskListener<T> {
void onTaskComplete(T result, BackgroundType type);
}
--------------------------
BackgroundTask bt1 = new BackgroundTask(this, ApiHelper.GET, GET);
bt.execute(url1);
BackgroundTask bt2 = new BackgroundTask(this, ApiHelper.POST, params, POST);
bt.execute(url2);
------------------------
#Override
public void onTaskComplete(String result, BackgroundType type) {
switch(type){
case GET:
break;
case POST:
break;
}
}
I would recommend you to create a new class and use this class instead of String when creating AsyncTask and communicating result back from the AsyncTask
Then the new class can then encapsulate the string you require along with other variables you would need.
For example:
// Create a new class to hold the result of `AsyncTask` execution
public class TaskExecutionResult {
public String method;
public String resultString;
}
// When subclassing AsyncType assign TaskExecutionResult as return type
// Please replace URL with input parameters you require (perhaps a method string)
private class BackgroundTask extends AsyncTask<URL, Integer, TaskExecutionResult> {
// doInBackground will now return TaskExecutionResult type of object
protected TaskExecutionResult doInBackground(URL... urls) {
// Here populate a value which will identify your AsyncTask into return value (perhaps a method string)
...
}
// onPostExecute will also accept TaskExecutionResult type of object
protected void onPostExecute(TaskExecutionResult result) {
super.onPostExecute(result);
callback.onTaskComplete(result);
}
}
Then use TaskExecutionResult in onTaskComplete:
public void onTaskComplete(TaskExecutionResult result) {
if (result.method.equals("GET")) {
// Parse the GET result (from result.resultString)
} else if(result.method.equals("POST")) {
// Parse the POST result (from result.resultString)
}
}
This approach will also allow you to easily update the result object if you needed to add more return variables.
I have an object Car that has this constructor:
public Car(int idCar, String name)
{
this.idCar = idCar;
this.name = name;
}
Here I don't have any problem so I created one object Car named newCar, like this:
Car newCar = new Car(1,"StrongCar");
The problem that I have it's that I want to pass this newCar to my AsyncTask, named modifyCar as a parameter, but I don't know how to do this.
I have searched in SO and I found this question: AsyncTask passing custom objects
but it doesn't solved my problem because in the solution that it is given they only pass an String to the AsyncTask and not the entirely object.
What I want it's to pass the entirely object as a parameter to the AsyncTask.
According to the solution that it is given in the question that I put above, I tried to do this to pass the object to the AsyncTask.
new modifyCar(newCar).execute();
So I declared the AsyncTask like this:
class modifyCar extends AsyncTask<Car, Integer, ArrayList<Evento>> {
protected void onPreExecute()
{
}
protected ArrayList<Evento> doInBackground(Car... newCarAsync)
{
//The rest of the code using newCarAsync
}
protected void onProgressUpdate()
{
}
protected void onPostExecute()
{
}
}
But I don't know if it is correct or not. If not, what I should do for that purpose?
Thanks in advance!
The solution that you read is right, you just did it wrong. You needed to create a constructor for your AsyncTask class easily and pass the object to it
class modifyCar extends AsyncTask<Void, Integer, ArrayList<Evento>> {
private Car newCar;
// a constructor so that you can pass the object and use
modifyCar(Car newCar){
this.newCar = newCar;
}
protected void onPreExecute()
{
}
protected ArrayList<Evento> doInBackground(Void... parms)
{
//The rest of the code using newCarAsync
}
protected void onProgressUpdate()
{
}
protected void onPostExecute()
{
}
}
and to execute this class
// pass the object that you created
new modifyCar(newCar).execute();
It makes no difference if your object is an instance of String or a Car or a AbstractDeathRayController, the expected way to pass them to an AsyncTask is through the execute method:
new modifyCar().execute(car);
BTW, Java convention for class names is to use CamelCase, so it might be a good idea to rename your class to ModifyCar.
You should pass the Car object on the execute method. You can read it in the documentation (http://developer.android.com/reference/android/os/AsyncTask.html):
An asynchronous task is defined by 3 generic types, called Params,
Progress and Result, and 4 steps, called onPreExecute, doInBackground,
onProgressUpdate and onPostExecute
Follow the documentation example:
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");
}
}
And call the async task with:
new DownloadFilesTask().execute(url1, url2, url3);
i want to get Ftp folders list from server using AsyncTask and return folders names ArrayList to main class and update spinner adapter.
In main class i got spinner with adapter
//the array i want to update in AsyncTask
static ArrayList<String> directoriesTeacher = new ArrayList<String>();
//The adapter
createfile_spinTeacher = (Spinner) findViewById(R.id.createfile_spinTeacher);
final ArrayAdapter<String> dataAdapterTeacher = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,directoriesTeacher);
dataAdapterTeacher.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
createfile_spinTeacher.setAdapter(dataAdapterTeacher);
An in AsyncTask:
package com.nedoGarazas.learnanylanguage;
import java.util.ArrayList;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import android.os.AsyncTask;
import android.util.Log;
public class FtpTeacher extends AsyncTask<ArrayList<String>, Void, ArrayList<String>> {
private static final String TAG = "MyFTPClient";
public FTPClient mFTPClient = null;
ArrayList<String> ftpTeacher = new ArrayList<String>();
#Override
protected ArrayList<String> doInBackground(ArrayList<String>... params) {
{
try {
mFTPClient = new FTPClient();
// connecting to the host
mFTPClient.connect("host.ftp.com", 21);
// now check the reply code, if positive mean connection success
if (FTPReply.isPositiveCompletion(mFTPClient.getReplyCode())) {
// login using username & password
boolean status = mFTPClient.login("admin", "admin");
if(status == true){
try {
FTPFile[] ftpFiles = mFTPClient.listFiles("/Wordsftp/");
int length = ftpFiles.length;
for (int i = 0; i < length; i++) {
String name = ftpFiles[i].getName();
boolean isDirectory = ftpFiles[i].isDirectory();
if (isDirectory) {
//adding to arraylist
ftpTeacher.add(name);
Log.i(TAG, "Yra : " + name);
}
else {
Log.i(TAG, "Directory : " + name);
}
}
} catch(Exception e) {
e.printStackTrace();
}
mFTPClient.setFileType(FTP.ASCII_FILE_TYPE);
mFTPClient.enterLocalPassiveMode();
}
}
} catch(Exception e) {
Log.d(TAG, "Error: could not connect to host ");
}
return ftpTeacher;
}
}
protected ArrayList<String>[] onPostExecute(ArrayList<String>... result) {
////How to return?
}
}
So how should i replace arraylist in AsyncTask with ArrayList in main class and update spinner updater dinamicly?
-- PSEUDO CODE --
Create a custom interface as followed:
public interface IAsyncTask {
void IAmFinished(ArrayList<Object> arrayList);
}
Add a constructor to your AsyncTask:
private IAsyncTask asyncTaskListener;
public MyAsyncTask(IAsyncTask asyncTaskListener){
this.asyncTaskListener = asyncTaskListener;
}
In your PostExecute of the AsyncTask:
public void onPostExecute(List<String> list) {
asyncTaskListener.IAmFinished(list);
}
In your Activity that starts your AsyncTask:
MyAsyncTask asyncTask = new MyAsyncTask(this);
asyncTask.execute(..);
Implement the interface:
public class MyActivity implements IAsyncTask
Implement the method:
public void IAmFinished(ArrayList<Object> list){
// Do whatever you want with your returned object
}
You already made your ArrayList static, make it public as well. and use that by your class name. and populate your ArrayList in onPostExecute(); like
protected void onPostExecute(ArrayList<String>... result) {
if(YourClassName.directoriesTeacher.size()>0)
{
YourClassName.directoriesTeacher.clear();
}
YourClassName.directoriesTeacher.addAll(result);
}
I assume you don't want a spinner while fetching data, but rather to fill your spinner with data from the background task? Returning data from AsyncTask commonly relies on this pattern, using interface.
1) Create an interface so that you can post back your results: (This class you can either create in separate file or just declare it in either class)
public interface ReturnData{
void handleReturnData(ArrayList<String> list);
}
2) Implement the ReturnData interface in your main class:
public class MyMainClass extends Activity implements ReturnData{
AsyncTask ftpTeacher = new FtpTeacher();//declare your async task
#Override
public void onCreate(Bundle savedInstanceState) {
ftpTeacher.returnData = this; //set this class as receiver for return data
//set up adapters etc, just like you do now
...
}
//Your new data will be returned here - update your current adapter with new list
#Override
void handleReturnData(ArrayList<String> list){
directoriesTeacher = list; //assign new data
dataAdapterTeacher.notifyDataSetChanged(); //Tell adapter it has new data = forces redraw
}
....
}
3) In your AsyncTask class:
public class FtpTeacher extends AsyncTask<ArrayList<String>, Void, ArrayList<String>> {
private static final String TAG = "MyFTPClient";
public FTPClient mFTPClient = null;
ArrayList<String> ftpTeacher = new ArrayList<String>();
public ReturnData returnData; // <--- PUBLIC
...
}
4) Finally, to return data:
protected ArrayList<String>[] onPostExecute(ArrayList<String>... result) {
returnData.handleReturnData(result);
}
In your main, where you are calling your AsyncTask, overwrite the onPostExecute method and put your adapter stuff in there. It gets called on the UI Thread, so it's save.
So where you are calling the AsyncTask, do
new FTPTeacher() {
public void onPostExecute(List<String> list) {
createfile_spinTeacher = (Spinner) findViewById(R.id.createfile_spinTeacher);
final ArrayAdapter<String> dataAdapterTeacher = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item,list);
dataAdapterTeacher.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
createfile_spinTeacher.setAdapter(dataAdapterTeacher);
}
}.execute();
onPostExecute methods runs in UI thread. You can assign the result in postexecute() to your arraylist in main method. Update the adapter by calling notifydatasetChanged to update your listview.
Implement a listener passing ArrayList and use this listener for returning your ArrayList.
public interface TaskListener {
public void onSuccess(ArrayList<String> result);
}
While invoking your async task for operation execution create an instance of TaskListener as follows:
TaskListener listener = new TaskListener() {
#Override
public void onSuccess(ArrayList<String> result) {
// Your result will come here
}
};
Pass this listenerobject as a parameter to the async task constructor. And create a global instance of TaskListener in the async task itself. Assign the TaskListener parameter in the constructor to the global instance.
Then in the onPostExecute of the async task class:
protected ArrayList<String>[] onPostExecute(ArrayList<String>... result) {
this.taskListenerGlobalInstance(result); // this will invoke the call back method
}
In your AsyncTask you could have a member (MyActivity m_activity) with the same class of your activity.
In your AsyncTask constructor, set a MyActivity parameter and record it in m_activity.
In your onPostExecute run a method of your activity that refresh your spinner adapter: m_activity.updateSpinner(ftpTeacher );
I am using AsyncTask class with the following signature:
public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
...
private String POST(List<NameValuePair>[] nameValuePairs){
...
return response;
}
}
protected String doInBackground(List<NameValuePair>... nameValuePairs) {
return POST(params);
}
I am trying to call it from other class through:
ApiAccess apiObj = new ApiAccess (0, "/User");
// String signupResponse = apiObj.execute(nameValuePairs);
String serverResponse = apiObj.execute(nameValuePairs); //ERROR
But here I get this error:
Type mismatch: cannot convert from AsyncTask<List<NameValuePair>,Integer,String> to String
Why is that when i have specified String as the third parameter in Class extension line?
You can get the result by calling AsyhncTask's get() method on the returned AsyncTask, but it will turn it from an asynchronous task into a synchronous task as it waits to get the result.
String serverResponse = apiObj.execute(nameValuePairs).get();
Since you have your AsyncTask in a seperate class, you can create an interface class and declare it in the AsyncTask and implement your new interface class as delegate in the class you wish to access the results from. A good guide is here: How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?.
I will attempt to apply the above link to your context.
(IApiAccessResponse)
public interface IApiAccessResponse {
void postResult(String asyncresult);
}
(ApiAccess)
public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
...
public IApiAccessResponse delegate=null;
protected String doInBackground(List<NameValuePair>... nameValuePairs) {
//do all your background manipulation and return a String response
return response
}
#Override
protected void onPostExecute(String result) {
if(delegate!=null)
{
delegate.postResult(result);
}
else
{
Log.e("ApiAccess", "You have not assigned IApiAccessResponse delegate");
}
}
}
(Your main class, which implements IApiAccessResponse)
ApiAccess apiObj = new ApiAccess (0, "/User");
//Assign the AsyncTask's delegate to your class's context (this links your asynctask and this class together)
apiObj.delegate = this;
apiObj.execute(nameValuePairs); //ERROR
//this method has to be implement so that the results can be called to this class
void postResult(String asyncresult){
//This method will get call as soon as your AsyncTask is complete. asyncresult will be your result.
}
I would suggest implementing a Handler Callback. You would pass the fragment's (or activity's) Handler to the AsyncTask, which the AsyncTask will call when it is finished. The AsyncTask can also pass back an arbitrary object.
Here is an example AsyncTask, which I have in its own file (not subclassed):
public class MyTask extends AsyncTask<Void, String, String> {
private static final String TAG = "MyTask";
private Handler mCallersHandler;
private Candy mObject1;
private Popsicle mObject2;
// Return codes
public static final int MSG_FINISHED = 1001;
public SaveVideoTask(Handler handler, Candy candyCane, Popsicle grapePop ) {
this.mCallersHandler = handler;
this.mObject1 = candyCane;
this.mObject2 = grapePop;
}
#Override
protected String doInBackground(Void... params) {
// Do all of the processing that you want to do...
// You already have the private fields because of the constructor
// so you can use mObject1 and mObject2
Dessert objectToReturn = mObject1 + mObject2;
// Tell the handler (usually from the calling thread) that we are finished,
// returning an object with the message
mCallersHandler.sendMessage( Message.obtain( mCallersHandler, MSG_FINISHED, objectToReturn ) );
return (null);
}
}
This example assumes that your AsyncTask needs a piece of Candy and a Popsicle. Then it will return a Dessert to your fragment.
You can construct and run the AsyncTask in one line from your fragment with:
( new MyTask( mFragmentHandler, candyCane, grapePop ) ).execute();
But of course, you will first need to set up the fragment's handler (myFragmentHandler). To do this, your fragment (or activity) should look like (NOTE the "implements Handler.Callback"):
public class MyFragment extends Fragment implements Handler.Callback {
private Handler mFragmentHandler;
private Candy candyCane;
private Popsicle grapePop;
#Override
public void onCreate(Bundle savedInstanceState) {
// Standard creation code
super.onCreate(savedInstanceState);
setRetainInstance(true);
// Create a handler for this fragment
mFragmentHandler = new Handler(this);
// Other stuff...
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
// Inflate the layout
View v = inflater.inflate(R.layout.my_fragment_layout, parent, false );
// The candyCane and grapePop don't need to be set up here, but
// they MUST be set up before the button is pressed.
// Here would be a good place to at least initialize them...
// Perhaps you have a button in "my_fragment_layout" that triggers the AsyncTask...
Button mButton = (Button) v.findViewById(R.id.mButton);
mButton.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View v) {
( new MyTask( mFragmentHandler, candyCane, grapePop ) ).execute();
}
});
return v;
}
#SuppressWarnings("unchecked")
#Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MyTask.MSG_FINISHED:
// Let's see what we are having for dessert
Dessert myDessert = (Dessert) msg.obj;
break;
}
return false;
}
}
If you use these pieces of code, a button press will trigger the AsyncTask. The calling fragment will continue to execute while the AsyncTask is processing. Then, when the AsyncTask is finished, it will send a message to the fragment saying that it is finished, and pass an object with the message. At this point, the fragment will see the message, and do whatever you want.
Note: There might be typos. This is cut from a very large and complicated code.
The problem is that when you call execute, the AsyncTask object is returned, but not the result yet. The result is computed in the background. The type of the result will eventually be a String (as you specified), and will be passed to onPostExecute().
You should use the AsyncTask as follows:
public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
...
private String POST(List<NameValuePair>[] nameValuePairs){
...
return response;
}
protected void onPreExecute (){
// this is run on the main (UI) thread, before doInBackground starts
}
protected void onPostExecute (String result){
// this is run on the main (UI) thread, after doInBackground returns
}
protected String doInBackground(List<NameValuePair>... nameValuePairs) {
// run in another, background thread
return POST(params);
}
}
Note that in your example you are not returning the result in doInBackground(), which you should.
Please read AsyncTask. You can get result on onPostExecute method. You can't do something like :
String serverResponse = apiObj.execute(nameValuePairs);
because it is async.
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.