我正在尝试创建一个可以在聊天室参与者之间共享一张图片的信使应用程序,包括后来要实现的图像编辑器。从服务器下载和上传到服务器的功能运行良好,但是当我尝试更新映像时,我得到一个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;为了使它发挥作用。
提前致谢。
答案 0 :(得分:2)
查看堆栈跟踪,您尝试多次执行同一个AsyncTask实例。尝试创建一个新实例..
答案 1 :(得分:1)
AsyncTask无法重复使用,因此bypassing
不是一个选项。它旨在用于“不超过几秒钟”的短期任务。在Android开发者中明确指出:
任务只能执行一次(如果是,则会抛出异常 尝试第二次执行。)
如果您的图像下载任务不是经常发生的,那么每次下载时都可以安全地使用AsyncTask的新实例。如果下载任务可能经常发生,您可能需要在Service或IntentService
中处理这些操作答案 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()
请求虚拟机运行垃圾收集器任务。