多次运行相同的AsyncTask

时间:2014-08-31 22:05:36

标签: android android-asynctask

我正在尝试创建一个可以在聊天室参与者之间共享一张图片的信使应用程序,包括后来要实现的图像编辑器。从服务器下载和上传到服务器的功能运行良好,但是当我尝试更新映像时,我得到一个IllegalStateException,表示我不允许多次调用和AsyncTask,这显然很漂亮对我的目标不利。

所以我有我的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);
        }
    }
}

然后我的Uploader类:

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;
        }
    }
}

Downloader类:

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;
        }
    }
}

这就是出来的结果:

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)

大多数代码,如toaster / progressor-class都可以忽略。这些部件工作正常。我已经研究过类似的问题所以我知道基本的多次通话是不可能的。但我想知道这可以通过&#34;绕过&#34;为了使它发挥作用。

提前致谢。

5 个答案:

答案 0 :(得分:2)

查看堆栈跟踪,您尝试多次执行同一个AsyncTask实例。尝试创建一个新实例..

答案 1 :(得分:1)

AsyncTask无法重复使用,因此bypassing不是一个选项。它旨在用于“不超过几秒钟”的短期任务。在Android开发者中明确指出:

  

任务只能执行一次(如果是,则会抛出异常   尝试第二次执行。)

如果您的图像下载任务不是经常发生的,那么每次下载时都可以安全地使用AsyncTask的新实例。如果下载任务可能经常发生,您可能需要在ServiceIntentService

中处理这些操作

答案 2 :(得分:0)

不要“绕过”它。只需重新分配异步任务。如果要在任务实例之间保留/共享一些初始设置代码,请将该预先计算的信息作为构造函数或执行参数传递。

答案 3 :(得分:0)

是的,所以其他评论是正确的,创建一个新实例(但不要去调用System.gc() - JVM / DVM比我们凡人的程序员更聪明。)

我用来克服这类问题的模式是依赖注入(DI)Provider<T>模式。当我请求可以尝试多次执行的AsyncTask时,我在需要新实例时使用javax.inject.Provider调用get();这个机制负责注入可能由@Inject的构造函数声明的更多类依赖项。

我建议使用依赖注入模式来解决您遇到的问题以及未来的任何Android / Java工作。适用于Android的DI的绝佳库是Dagger by Square

答案 4 :(得分:-2)

正如您已经想到的那样,AsyncTask不能执行多次,并且没有办法绕过&#34;绕过&#34;它。解决方案是每次希望运行时简单地重新实现AsyncTask。

如果您有内存问题(您在短时间内多次运行该任务),您可以每隔几次调用System.gc()请求虚拟机运行垃圾收集器任务。