1

I'm trying to create a messenger-app that can share one picture between the chatroom participants including a later-to-be-implemented image-editor. The functionality of downloading and uploading from and to the server is functioning great, but when I try to update the image I get an IllegalStateException that says that I'm not allowed to call and AsyncTask more than one time, which is obviously pretty bad for my goal.

So I have my MainActivity:

package com.sixcoresecond.imageSync;

import io.socket.IOAcknowledge;
import io.socket.IOCallback;
import io.socket.SocketIO;
import io.socket.SocketIOException;

import java.io.File;

import javax.net.ssl.SSLContext;

import org.json.JSONObject;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;

import com.sixcoresecond.helper.ImageDownloader;
import com.sixcoresecond.helper.ImageUploader;
import com.sixcoresecond.helper.Toaster;
import com.sixcoresecond.sketchmessenger.R;

public class ImageSyncActivity extends Activity {

    private static final int SELECT_PHOTO = 100;

    //URL for SocketIO
    private static final String COMMS_URL = "http://132.199.139.24:8998/";

    //URL for downloads
    private static final String DOWNLOAD_URL = "http://132.199.139.24/~krm22840/uploads/";

    //URL for uploads
    private static final String UPLOAD_URL = "http://132.199.139.24/~krm22840/upload.php";

    //Path for saving files
    private static String STORAGE_PATH = "";

    private static String CHAT_ID = "";

    //UI stuff
    private ImageView mainImageView;
    private Button submitButton;

    //Subclass objects
    private IOClient socketClient;

    private ImageDownloader downloader;
    private ImageUploader uploader;
    private Toaster toaster;

    //Private objects
    private Bitmap currentBitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_sync_layout);

        //Setup directory for all images
        setupDirectory();
        //Setup the UI
        initUI();

        //Assign Listener to all Buttons
        assignListener();

        //Set a default image to the ImageView if necessary
        setupDefaultImage();

        //Setting up a toaster, a downloader and an uploader
        toaster = new Toaster(this);
        downloader = new ImageDownloader(this, mainImageView);
        uploader = new ImageUploader(this);

        //Setting up a socketIO client and connecting it to the server
        try {
            socketClient = new IOClient(COMMS_URL);
        } catch (Exception e) {
            e.printStackTrace();
            Log.i("Debug", e.toString());
        }
    }

    @Override
    protected void onStart(){
        super.onStart();
        if(socketClient != null && CHAT_ID.equals("")){
            socketClient.send("NEW_ID_REQUEST", "");
        }else{
            toaster.popToast("No server-connection established. \nPlease try again later.");
        }
    }

    private void setupDirectory(){
        Log.i("Debug", "Setting up Directory...");
        if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
            Log.i("Debug", "Directory Error: No SDCARD");
        }else{
            File directory = new File(Environment.getExternalStorageDirectory() + File.separator + "SketchMessenger");
            directory.mkdirs();
            STORAGE_PATH = directory.getAbsolutePath();
            Log.i("Debug", "Directory created/set to: " + STORAGE_PATH);
        }
    }

    private void initUI(){
        mainImageView = (ImageView)findViewById(R.id.syncImageView);
        submitButton = (Button)findViewById(R.id.syncSubmitButton);
    }

    private void assignListener(){
        mainImageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                openImagePicker();
            }
        });

        submitButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                submitCurrentImage();
            }
        });
    }

    private void setupDefaultImage(){
        if((!CHAT_ID.equals(""))){
            File file = new File(STORAGE_PATH + "/" + CHAT_ID + ".png");
            if(!file.exists()){
                Log.i("Debug", "Setting up a default Image for the main view...");
                Drawable drawable = getResources().getDrawable(R.drawable.scribble_icon_white);
                currentBitmap = ((BitmapDrawable)drawable).getBitmap(); 
            }
        }
    }

    private void openImagePicker(){
        Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent, SELECT_PHOTO);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent){
        super.onActivityResult(requestCode, resultCode, imageReturnedIntent);

        switch(requestCode){
        case SELECT_PHOTO:
            if(resultCode == Activity.RESULT_OK){
                Uri selectedImage = imageReturnedIntent.getData();
                String[] filePathColumn = {MediaStore.Images.Media.DATA};

                Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
                cursor.moveToFirst();

                int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
                String filePath = cursor.getString(columnIndex);
                cursor.close();

                Bitmap mySelectedImage = BitmapFactory.decodeFile(filePath);
                if(mySelectedImage != null){
                    mainImageView.setImageBitmap(mySelectedImage);
                    currentBitmap = mySelectedImage;
                }
            }
        }
    }

    private void submitCurrentImage(){
        if(!CHAT_ID.equals("")){
            Log.i("Debug", "UploadUrl" + UPLOAD_URL + "\n" + "Path: " + STORAGE_PATH + "\n" + "ChatID: " + CHAT_ID);
            uploader.setup(UPLOAD_URL, mainImageView, STORAGE_PATH, CHAT_ID);
            uploader.execute(STORAGE_PATH, CHAT_ID);
        }else{
            toaster.popToast("No ID acquired. Connect to Server.");
        }
    }

    private void updateCurrentImage(){
        if(!CHAT_ID.equals("")){
            downloader.execute(DOWNLOAD_URL, CHAT_ID, STORAGE_PATH);
        }
    }

    public class IOClient implements IOCallback{

        private SocketIO socket;

        public IOClient(String url) throws Exception{
            SocketIO.setDefaultSSLSocketFactory(SSLContext.getDefault());
            socket = new SocketIO(url);
            socket.connect(this);
        }

        @Override
        public void on(String arg0, IOAcknowledge arg1, final Object... args) {
            if(arg0.equals("NEW_ID_REQUEST_ECHO")){
                String result = args[0].toString();
                CHAT_ID = result;
                downloader.execute(DOWNLOAD_URL, CHAT_ID, STORAGE_PATH);
            }
            else if(arg0.equals("NEW_CHAT_ECHO")){
                final String result = args[0].toString();
                CHAT_ID = result;
                submitCurrentImage();
            }
        }

        @Override
        public void onConnect() {
            Log.i("Debug", "onConnect");
        }

        @Override
        public void onDisconnect() {
            Log.i("Debug", "onDisconnect");
        }

        @Override
        public void onError(SocketIOException arg0) {
            Log.i("Debug", "on Error: " + arg0.getCause().toString());
        }

        @Override
        public void onMessage(String arg0, IOAcknowledge arg1) {
            Log.i("Debug", "onMessageString: " + arg0);
        }

        @Override
        public void onMessage(JSONObject arg0, IOAcknowledge arg1) {
            Log.i("Debug", "onMessageJSON: " + arg0.toString());
        }

        public void send(String tag, String msg){
            socket.emit(tag, msg);
        }

        public void disconnect(){
            socket.disconnect();
        }
    }

    @Override
    protected void onDestroy(){
        socketClient.disconnect();
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){
        case R.id.menu_update:
            updateCurrentImage();
            return true;
        default: 
            return super.onOptionsItemSelected(item);
        }
    }
}

Then my Uploader class:

package com.sixcoresecond.helper;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Looper;
import android.util.Log;
import android.widget.ImageView;

import com.sixcoresecond.imageSync.ImageSyncActivity.IOClient;

public class ImageUploader extends AsyncTask<String, Void, Integer> {

    private int serverResponseCode = 0;

    private String uploadURL;
    private ImageView imgView;
    private String filePath;
    private String chatID;

    private Toaster toaster;
    private Progressor progressor;

    private boolean fileCreated = true;

    public ImageUploader(Context context){
        toaster = new Toaster(context);
        progressor = new Progressor(context);
        uploadURL = "";
        imgView = null;
        filePath = "";
        chatID = "";
    }

    public void setup(String serverUrl, ImageView view, String path, String id){
        uploadURL = serverUrl;
        imgView = view;
        filePath = path;
        chatID = id;
    }

    @Override 
    protected void onPreExecute(){
        Looper.prepare();
        progressor.showProgressor("Uploading Image");
        try {
            saveImage(filePath, chatID);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.i("Debug", "Saving Failed: " + e.toString());
            this.cancel(true);
            return;
        }
    }

    @Override
    protected Integer doInBackground(String... params) {
        String fileName = params[0] + "/" + params[1] + ".png";
        Log.i("Debug", fileName);

        HttpURLConnection conn = null;
        DataOutputStream dos = null;  
        String lineEnd = "\r\n";
        String twoHyphens = "--";
        String boundary = "*****";
        int bytesRead, bytesAvailable, bufferSize;
        byte[] buffer;
        int maxBufferSize = 1 * 1024 * 1024; 
        File sourceFile = new File(fileName); 

        try{
             // open a URL connection to the Server
            FileInputStream fileInputStream = new FileInputStream(sourceFile);
            URL url = new URL(uploadURL);

            // Open a HTTP  connection to  the URL
            conn = (HttpURLConnection) url.openConnection(); 
            conn.setDoInput(true); // Allow Inputs
            conn.setDoOutput(true); // Allow Outputs
            conn.setUseCaches(false); // Don't use a Cached Copy
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("ENCTYPE", "multipart/form-data");
            conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
            conn.setRequestProperty("uploaded_file", fileName); 

            dos = new DataOutputStream(conn.getOutputStream());

            dos.writeBytes(twoHyphens + boundary + lineEnd); 
            dos.writeBytes("Content-Disposition: form-data; name='uploaded_file'; filename=" + fileName + "" + lineEnd);

            dos.writeBytes(lineEnd);

            // create a buffer of  maximum size
            bytesAvailable = fileInputStream.available(); 

            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            buffer = new byte[bufferSize];

            // read file and write it into form...
            bytesRead = fileInputStream.read(buffer, 0, bufferSize);  

            while (bytesRead > 0) {

              dos.write(buffer, 0, bufferSize);
              bytesAvailable = fileInputStream.available();
              bufferSize = Math.min(bytesAvailable, maxBufferSize);
              bytesRead = fileInputStream.read(buffer, 0, bufferSize);   

             }

            // send multipart form data necesssary after file data...
            dos.writeBytes(lineEnd);
            dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

            // Responses from the server (code and message)
            serverResponseCode = conn.getResponseCode();
            String serverResponseMessage = conn.getResponseMessage();

            Log.i("Debug", "HTTP Response is : " + serverResponseMessage + ": " + serverResponseCode);  

            if(serverResponseCode == 200){
                String msg = "File Upload Completed.";
                toaster.popToast(msg);               
            }

            //close the streams //
            fileInputStream.close();
            dos.flush();
            dos.close();

        }catch(Exception e){
            e.printStackTrace();
            toaster.popToast(e.toString());
            Log.i("Debug", "Upload failed: " + e.toString());
        }
        return null;
    }

    @Override
    protected void onPostExecute(Integer result){
        progressor.dismiss();
        toaster.popToast("Upload done.");
    }

    @SuppressWarnings("finally")
    private String saveImage(String path, String id) throws FileNotFoundException{

        Drawable drawable = imgView.getDrawable();
        Rect bounds = drawable.getBounds();
        Bitmap bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.draw(canvas);
        OutputStream fOut = null;
        try{
            new File("/storage/sdcard0/SketchMessenger").mkdirs();
            fOut = new FileOutputStream(path + "/" + id + ".png");
            bitmap.compress(Bitmap.CompressFormat.PNG, 95, fOut);
        }finally{
            if(fOut != null){
                try{
                    fOut.close();
                    return path + id + ".png";
                }catch(IOException e){
                    e.printStackTrace();
                    Log.i("Debug", e.toString());
                }

            }
            return null;
        }
    }
}

And the Downloader class:

package com.sixcoresecond.helper;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.ImageView;

public class ImageDownloader extends AsyncTask<String, Void, Bitmap> {

    private static int imageWidth;
    private static int imageHeight;

    private String filePath;
    private String chatID;

    private ImageView imgView;
    private Toaster toaster;
    private Progressor progressor;

    public ImageDownloader(Context context, ImageView view){
        toaster = new Toaster(context);
        progressor = new Progressor(context);
        imageWidth = 0;
        imageHeight = 0;
        imgView = view;

        filePath = "";
        chatID = "";
    }

    @Override
    protected void onPreExecute(){
        progressor.showProgressor("Downloading latest Image.");
    }

    @Override
    protected Bitmap doInBackground(String... args) {
        String url = args[0] + args[1] + ".png";
        Log.i("Debug", "Downloading from: " + url);
        Bitmap bitmap = null;

        byte[] imageData = getImageFromUrl(url);

        bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length);

        /*
        ByteArrayInputStream inputStream = new ByteArrayInputStream(imageData);
        bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(inputStream), 296, 425, true);*/
        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap){
        imgView.setImageBitmap(bitmap);
        try {
            saveImage(filePath, chatID);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.i("Debug", "Downloader failed to save image\n" + e.toString());
        }
        progressor.dismiss();
        toaster.popToast("Download done.");
    }

    private byte[] getImageFromUrl(String url){

        InputStream in = null;
        byte[] byteImage = null;

        try{
            URL imageUrl = new URL(url);
            URLConnection conn = imageUrl.openConnection();
            HttpURLConnection httpConn = (HttpURLConnection) conn;
            httpConn.setAllowUserInteraction(false);
            httpConn.setInstanceFollowRedirects(true);
            httpConn.setRequestMethod("GET");
            httpConn.connect();

            int response = httpConn.getResponseCode();

            if(response == HttpURLConnection.HTTP_OK){
                in = httpConn.getInputStream();
            }

            int nRead;
            byte[] data = new byte[16384];      
            ByteArrayOutputStream buffer = new ByteArrayOutputStream();

            while((nRead = in.read(data, 0, data.length)) != -1){
                buffer.write(data, 0, nRead);
            }

            buffer.flush();         
            byteImage = buffer.toByteArray();
        }
        catch(IOException e){
            e.printStackTrace();
            Log.i("Debug", "Download Error: " + e.toString());
        }   

        return byteImage;
    }

    @SuppressWarnings("finally")
    private String saveImage(String path, String id) throws FileNotFoundException{

        Drawable drawable = imgView.getDrawable();
        Rect bounds = drawable.getBounds();
        Bitmap bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.draw(canvas);
        OutputStream fOut = null;
        try{
            new File("/storage/sdcard0/Download").mkdir();
            fOut = new FileOutputStream(path + "/" + id + ".png");
            bitmap.compress(Bitmap.CompressFormat.PNG, 95, fOut);
        }finally{
            if(fOut != null){
                try{
                    fOut.close();

                    return path + id + ".png";
                }catch(IOException e){
                    e.printStackTrace();
                    Log.i("Debug", e.toString());
                }
            }
            return null;
        }
    }
}

And this is what comes out:

08-31 23:36:20.669: E/AndroidRuntime(1108): FATAL EXCEPTION: main
08-31 23:36:20.669: E/AndroidRuntime(1108): java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:578)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.os.AsyncTask.execute(AsyncTask.java:534)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.sixcoresecond.imageSync.ImageSyncActivity.updateCurrentImage(ImageSyncActivity.java:197)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.sixcoresecond.imageSync.ImageSyncActivity.onOptionsItemSelected(ImageSyncActivity.java:276)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.app.Activity.onMenuItemSelected(Activity.java:2566)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.android.internal.policy.impl.PhoneWindow.onMenuItemSelected(PhoneWindow.java:1039)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:735)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:874)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.android.internal.view.menu.ActionMenuView.invokeItem(ActionMenuView.java:547)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.android.internal.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:115)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.view.View.performClick(View.java:4247)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.view.View$PerformClick.run(View.java:17728)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.os.Handler.handleCallback(Handler.java:730)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.os.Handler.dispatchMessage(Handler.java:92)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.os.Looper.loop(Looper.java:137)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at android.app.ActivityThread.main(ActivityThread.java:5289)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at java.lang.reflect.Method.invokeNative(Native Method)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at java.lang.reflect.Method.invoke(Method.java:525)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:555)
08-31 23:36:20.669: E/AndroidRuntime(1108):     at dalvik.system.NativeStart.main(Native Method)

Most of the code, like the toaster/progressor-class can be ignored. Those parts are working properly. I already researched similar questions so I know that a basic multiple call is not possible. But I would like to know how this could by "bypassed" in order to make it work.

Thanks in advance.

nomad
  • 95
  • 1
  • 8

5 Answers5

2

looking at the stack trace, you are trying to execute the same instance of an AsyncTask more than once.. try to create a new instance..

Matias Elorriaga
  • 8,880
  • 5
  • 40
  • 58
  • yes! that one helped, but only half way. I also had to execute the task on the UI-thread through runOnUiThread(...); – nomad Sep 01 '14 at 15:32
1

AsyncTask cannot be reused so bypassing is not an option. It was designed to serve to short tasks "not longer than few seconds". In Android developers is clearly stated:

The task can be executed only once (an exception will be thrown if a second execution is attempted.)

If your image downloading task is not intended to happen frequently you can safely use new instances of AsyncTask every time a download will be necessary. If the downloading task might happen frequently perhaps you need to handle these operations in a Service or IntentService

eldjon
  • 2,800
  • 2
  • 20
  • 23
0

Don't "bypass" it. Just reallocate the async task. If you want to preserve/share some initial setup code between task instances, pass in that precomputed information as a constructor or execute parameter.

Michael Aaron Safyan
  • 93,612
  • 16
  • 138
  • 200
0

Yep, so the other comments are correct, create a new instance (but don't go invoking System.gc() - the JVM/DVM is cleverer than us mere mortal programmers).

The pattern I use to overcome this sort of issue is the Dependency Injection (DI) Provider<T> pattern. When I request an AsyncTask that I know could be attempted to be executed more than once, I employ the use of a javax.inject.Provider invoking get() when I need a new instance; this mechanism takes care of injecting further class dependencies that maybe declared by the @Inject'ed constructor.

I recommend using the dependency injection pattern to work around the problems you are having and for any future Android / Java work. A great library for DI on Android is Dagger by Square.

BrantApps
  • 6,362
  • 2
  • 27
  • 60
  • Can you explain why you need a Provider instead of just a plain old T in an @Inject constructor? Shouldn't the DI framework provide it automatically (assuming the object was created through the injection framework)? – Michael Aaron Safyan Sep 02 '14 at 04:12
  • The Provider impl should ensure that a new instance of T will be returned for each call to the get() contract. A normal @Inject instantiation of the AsyncTask will result in the same task object being invoked twice - the scenario the question creator wanted to avoid. – BrantApps Sep 02 '14 at 13:10
  • Ah, you're using a Provider of AsyncTask...I thought this was a provider of the AsyncTask's dependencies. – Michael Aaron Safyan Sep 02 '14 at 16:10
-2

As you already figured out, AsyncTask cannot be executed more than once, and there's no way to "bypass" it. The solution is to simply re-instatntiate the AsyncTask every time you wish to run it.

If you have memory concerns (you're running the task many times in a short period of time) you can call System.gc() to request the virtual machine to run the garbage collector every few times you run the task.

mittelmania
  • 3,393
  • 4
  • 23
  • 48
  • 2
    i would never suggest to run System.gc() "if you have memory concerns" – eldjon Aug 31 '14 at 23:26
  • @eldjon I see your point, however Max's question might be relevant because he wants to run the task many times. Calling System.gc() is slightly better than doing nothing (even though Java is a memory self-managed language, it's better to have a backup) – mittelmania Aug 31 '14 at 23:29
  • It's guaranteed that you will never get an `OutOfMemoryError` until the gc has run and there is nothing to free. The only reason I know of to ever call `System.gc()` yourself is if you're writing a memory analyzer. – Kevin Krumwiede Aug 31 '14 at 23:47
  • I respectfully disagree. `AsyncTask` is a heavy object that requires a lot of memory, and since you're able to run many of these tasks in parallel there's no guarantee that GC will run when you need it. Calling it manually, although not a requirement, can always be a safe bet. If you need additional proof, please read the Javadoc on the difference between `Runtime.gc()` and `System.gc()` – mittelmania Sep 01 '14 at 08:50
  • http://stackoverflow.com/questions/2414105/why-is-it-bad-practice-to-call-system-gc – Kevin Krumwiede Sep 01 '14 at 09:49