Мне нужен был процесс загрузки изображения, и я не использовал HttpMultipartClient из-за проблем с реализацией (проблемы с получением пакета из-за ошибок gradle и зависимостей).
Еще одна проблема, с которой я столкнулся, заключалась в получении фактического размера файла изображения, которое я хотел загрузить.
Мои требования также включали загрузку в области уведомлений. Вот мое решение:
Получение размера изображения
protected int sizeOf(Bitmap data) {
/*
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return data.getAllocationByteCount();
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1) {
return data.getRowBytes() * data.getHeight();
} else {
return data.getByteCount();
}
// NONE OF THE ABOVE RETURN ACCURATE RESULTS!
// A Bitmap, when stored as a file takes up more room because it represents
// full pixel data and is not compressed on disk.
*/
byte[] bitmapdata = getBmpAsByteArray(data);
return (bitmapdata == null) ? 0 : bitmapdata.length;
}
AsyncHttpPostTask extends AsyncTask<UploadableImage, Integer, String>
AsyncHttpPostTask#onProgressUpdate
Эта функция вызывается из AsyncHttpPostTask#doInBackground
, которая вызывает обратный вызов для оповещения об активности изменения статуса.
@Override
protected void onProgressUpdate(Integer... progress) {
((ImageUploadActivity) activity).updateProgress(progress[0]);
}
AsyncHttpPostTask#doInBackground
Как я упоминал ранее, я не использовал HttpMultipartClient
, поэтому мне пришлось как-то реализовать свою собственную. Большая часть этого поступает от http://www.androidsnippets.com/multipart-http-requests
@Override
protected String doInBackground(InputStream... inStream) {
if (MainActivity.isDebugMode) {
Log.d(TAG, "doInBackground");
}
HttpURLConnection connection;
DataOutputStream outputStream;
InputStream inputStream;
String twoHyphens = "--";
String boundary = "----------MobileFormData";
String lineEnd = "\r\n";
String result;
int bytesRead, bytesAvailable, bufferSize;
byte[] buffer;
int maxBufferSize = 32768; // 2^15 = 32k -- http://stackoverflow.com/a/11221907/940217
try {
InputStream is = inStream[0];
totalSize = curUpImage.getFileSize();
Log.e(TAG, "Determined the file size to be " + totalSize + " bytes");
URL url = new URL(this.server);
connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setChunkedStreamingMode(maxBufferSize);
connection.setRequestMethod("POST");
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("User-Agent", "Android Multipart HTTP Client 1.0");
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
outputStream = new DataOutputStream(connection.getOutputStream());
// Upload POST Data
Log.e(TAG, "Args: "+this.postArgs);
String[] posts = this.postArgs.split("&");
for (String post : posts) {
outputStream.writeBytes(twoHyphens + boundary + lineEnd);
String[] kv = post.split("=");
outputStream.writeBytes(String.format("Content-Disposition: form-data; name=\"%s\"", kv[0]));
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(String.format("%s", kv[1]));
outputStream.writeBytes(lineEnd);
}
outputStream.writeBytes(twoHyphens + boundary + lineEnd);
outputStream.writeBytes("Content-Disposition: form-data; name=\"" + this.fileParamConst + "\"; filename=\"image.jpg\"" + lineEnd);
outputStream.writeBytes("Content-Type: image/jpeg" + lineEnd);
outputStream.writeBytes(lineEnd);
bytesAvailable = is.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
int totalByteRead = 0;
bytesRead = is.read(buffer, 0, bufferSize);
while (bytesRead > 0) {
totalByteRead += bytesRead;
Log.w(TAG, "totalByteRead: "+totalByteRead+", totalSize: "+totalSize);
publishProgress((int) ((totalByteRead / (float) totalSize) * 100));
outputStream.write(buffer, 0, bufferSize);
bytesAvailable = is.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = is.read(buffer, 0, bufferSize);
}
if (totalByteRead == 0){
Log.e(TAG, "Total bytes read from image file: "+totalByteRead);
} else {
Log.d(TAG, "Total bytes read from image file: "+totalByteRead);
}
outputStream.writeBytes(lineEnd);
outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
inputStream = connection.getInputStream();
result = this.convertStreamToString(inputStream);
is.close();
inputStream.close();
outputStream.flush();
outputStream.close();
return result;
} catch (MalformedURLException e) {
result = "Error - Malformed URL";
e.printStackTrace();
} catch (FileNotFoundException e) {
result = "Error - Image file not found.";
e.printStackTrace();
} catch (IOException e) {
result = "Error - IO Exception.";
e.printStackTrace();
}
return result;
}
AsyncHttpPostTask#onPostExecute
Здесь я анализирую ответ JSON моего сервера, чтобы увидеть, была ли возможность успешно обработать закачку, затем возвращаю сообщение в Activity, которая контролирует уведомление.
@Override
protected void onPostExecute(String result) {
String resultString = null;
if (MainActivity.isDebugMode){
Log.d(TAG, "Async result: "+result);
}
boolean successful = false;
String[] errorMessages = null;
try {
JSONObject mainObject = new JSONObject(result);
String resultJsonString = mainObject.getString("result");
JSONArray messagesJsonArray = mainObject.getJSONArray("messages");
if (resultJsonString != null){
if (resultJsonString.equalsIgnoreCase("success")){
successful = true;
} else {
Log.e(TAG, "result was: "+resultJsonString);
}
}
errorMessages = new String[messagesJsonArray.length()];
for (int i = 0; i < messagesJsonArray.length(); i++){
errorMessages[i]= (String)messagesJsonArray.get(i);
}
} catch (JSONException e){
Log.e(TAG, "JSON Exception -- The string that I tried to parse was:\n"+result);
e.printStackTrace();
}
if (successful) {
Toast.makeText(this.activity, "Upload completed successfully!", Toast.LENGTH_SHORT).show();
resultString = "Upload complete.";
} else {
String eMessages;
if (errorMessages != null && errorMessages.length > 0){
eMessages = TextUtils.join(", ", errorMessages);
resultString = "Image upload failed:\n"+eMessages;
} else {
resultString = "Image upload failed!";
}
}
((ImageUploadActivity) activity).updateProgress(null);
((ImageUploadActivity) activity).setPostResult(resultString);
}
Отображение прогресса
В Activity, отвечающей за уведомление, у меня есть функция обратного вызова, которая вызывается из асинхронной задачи. Отображение прогресса здесь также можно сделать, используя одно из решений, обсуждавшихся в сообщении Джона Рассела в блоге . Это действие запускается в режиме singleTop
, поэтому при выводе его с уведомлением состояние сохраняется.
ImageUploadActivity # buildNotify
private void buildNotify(){
Intent resultIntent = new Intent(this, ImageUploadActivity.class);
// Because clicking the notification opens a new ("special") activity, there's
// no need to create an artificial back stack.
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
this,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
mNotifyManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentIntent(resultPendingIntent);
mBuilder.setContentTitle("Image Upload")
.setContentText("Image upload in progress")
.setSmallIcon(android.R.drawable.ic_menu_upload);
}
ImageUploadActivity # UpdateProgress
Этот метод сбрасывает ход выполнения в уведомление, а также в пользовательский интерфейс, содержащийся в действии.
public void updateProgress(Integer progress){
this.currentProgress = progress;
if (uploadStatusTV != null && this.currentProgress != null){
currentStatus = "uploading image: "+this.currentProgress+"%";
uploadStatusTV.setText("uploading image: "+this.currentProgress+"%");
if (mBuilder == null){
buildNotify();
}
// Sets the progress indicator to a max value, the
// current completion percentage, and "determinate" state
mBuilder.setProgress(100, currentProgress, false);
// Displays the progress bar for the first time.
mNotifyManager.notify(notify_id, mBuilder.build());
} else if (uploadStatusTV != null){
return;
} else {
Log.e(TAG, "You should never see this message.");
finish();
}
}