Created
March 13, 2014 07:26
-
-
Save xalexchen/9523327 to your computer and use it in GitHub Desktop.
Download image file into byte array
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) ${year} The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package com.example.android.threadsample; | |
import com.example.android.threadsample.PhotoDecodeRunnable.TaskRunnableDecodeMethods; | |
import java.io.EOFException; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.net.HttpURLConnection; | |
import java.net.URL; | |
/** | |
* This task downloads bytes from a resource addressed by a URL. When the task | |
* has finished, it calls handleState to report its results. | |
* | |
* Objects of this class are instantiated and managed by instances of PhotoTask, which | |
* implements the methods of {@link TaskRunnableDecodeMethods}. PhotoTask objects call | |
* {@link #PhotoDownloadRunnable(TaskRunnableDownloadMethods) PhotoDownloadRunnable()} with | |
* themselves as the argument. In effect, an PhotoTask object and a | |
* PhotoDownloadRunnable object communicate through the fields of the PhotoTask. | |
*/ | |
class PhotoDownloadRunnable implements Runnable { | |
// Sets the size for each read action (bytes) | |
private static final int READ_SIZE = 1024 * 2; | |
// Sets a tag for this class | |
@SuppressWarnings("unused") | |
private static final String LOG_TAG = "PhotoDownloadRunnable"; | |
// Constants for indicating the state of the download | |
static final int HTTP_STATE_FAILED = -1; | |
static final int HTTP_STATE_STARTED = 0; | |
static final int HTTP_STATE_COMPLETED = 1; | |
// Defines a field that contains the calling object of type PhotoTask. | |
final TaskRunnableDownloadMethods mPhotoTask; | |
/** | |
* | |
* An interface that defines methods that PhotoTask implements. An instance of | |
* PhotoTask passes itself to an PhotoDownloadRunnable instance through the | |
* PhotoDownloadRunnable constructor, after which the two instances can access each other's | |
* variables. | |
*/ | |
interface TaskRunnableDownloadMethods { | |
/** | |
* Sets the Thread that this instance is running on | |
* @param currentThread the current Thread | |
*/ | |
void setDownloadThread(Thread currentThread); | |
/** | |
* Returns the current contents of the download buffer | |
* @return The byte array downloaded from the URL in the last read | |
*/ | |
byte[] getByteBuffer(); | |
/** | |
* Sets the current contents of the download buffer | |
* @param buffer The bytes that were just read | |
*/ | |
void setByteBuffer(byte[] buffer); | |
/** | |
* Defines the actions for each state of the PhotoTask instance. | |
* @param state The current state of the task | |
*/ | |
void handleDownloadState(int state); | |
/** | |
* Gets the URL for the image being downloaded | |
* @return The image URL | |
*/ | |
URL getImageURL(); | |
} | |
/** | |
* This constructor creates an instance of PhotoDownloadRunnable and stores in it a reference | |
* to the PhotoTask instance that instantiated it. | |
* | |
* @param photoTask The PhotoTask, which implements TaskRunnableDecodeMethods | |
*/ | |
PhotoDownloadRunnable(TaskRunnableDownloadMethods photoTask) { | |
mPhotoTask = photoTask; | |
} | |
/* | |
* Defines this object's task, which is a set of instructions designed to be run on a Thread. | |
*/ | |
@SuppressWarnings("resource") | |
@Override | |
public void run() { | |
/* | |
* Stores the current Thread in the the PhotoTask instance, so that the instance | |
* can interrupt the Thread. | |
*/ | |
mPhotoTask.setDownloadThread(Thread.currentThread()); | |
// Moves the current Thread into the background | |
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); | |
/* | |
* Gets the image cache buffer object from the PhotoTask instance. This makes the | |
* to both PhotoDownloadRunnable and PhotoTask. | |
*/ | |
byte[] byteBuffer = mPhotoTask.getByteBuffer(); | |
/* | |
* A try block that downloads a Picasa image from a URL. The URL value is in the field | |
* PhotoTask.mImageURL | |
*/ | |
// Tries to download the picture from Picasa | |
try { | |
// Before continuing, checks to see that the Thread hasn't been | |
// interrupted | |
if (Thread.interrupted()) { | |
throw new InterruptedException(); | |
} | |
// If there's no cache buffer for this image | |
if (null == byteBuffer) { | |
/* | |
* Calls the PhotoTask implementation of {@link #handleDownloadState} to | |
* set the state of the download | |
*/ | |
mPhotoTask.handleDownloadState(HTTP_STATE_STARTED); | |
// Defines a handle for the byte download stream | |
InputStream byteStream = null; | |
// Downloads the image and catches IO errors | |
try { | |
// Opens an HTTP connection to the image's URL | |
HttpURLConnection httpConn = | |
(HttpURLConnection) mPhotoTask.getImageURL().openConnection(); | |
// Sets the user agent to report to the server | |
httpConn.setRequestProperty("User-Agent", Constants.USER_AGENT); | |
// Before continuing, checks to see that the Thread | |
// hasn't been interrupted | |
if (Thread.interrupted()) { | |
throw new InterruptedException(); | |
} | |
// Gets the input stream containing the image | |
byteStream = httpConn.getInputStream(); | |
if (Thread.interrupted()) { | |
throw new InterruptedException(); | |
} | |
/* | |
* Gets the size of the file being downloaded. This | |
* may or may not be returned. | |
*/ | |
int contentSize = httpConn.getContentLength(); | |
/* | |
* If the size of the image isn't available | |
*/ | |
if (-1 == contentSize) { | |
// Allocates a temporary buffer | |
byte[] tempBuffer = new byte[READ_SIZE]; | |
// Records the initial amount of available space | |
int bufferLeft = tempBuffer.length; | |
/* | |
* Defines the initial offset of the next available | |
* byte in the buffer, and the initial result of | |
* reading the binary | |
*/ | |
int bufferOffset = 0; | |
int readResult = 0; | |
/* | |
* The "outer" loop continues until all the bytes | |
* have been downloaded. The inner loop continues | |
* until the temporary buffer is full, and then | |
* allocates more buffer space. | |
*/ | |
outer: do { | |
while (bufferLeft > 0) { | |
/* | |
* Reads from the URL location into | |
* the temporary buffer, starting at the | |
* next available free byte and reading as | |
* many bytes as are available in the | |
* buffer. | |
*/ | |
readResult = byteStream.read(tempBuffer, bufferOffset, | |
bufferLeft); | |
/* | |
* InputStream.read() returns zero when the | |
* file has been completely read. | |
*/ | |
if (readResult < 0) { | |
// The read is finished, so this breaks | |
// the to "outer" loop | |
break outer; | |
} | |
/* | |
* The read isn't finished. This sets the | |
* next available open position in the | |
* buffer (the buffer index is 0-based). | |
*/ | |
bufferOffset += readResult; | |
// Subtracts the number of bytes read from | |
// the amount of buffer left | |
bufferLeft -= readResult; | |
if (Thread.interrupted()) { | |
throw new InterruptedException(); | |
} | |
} | |
/* | |
* The temporary buffer is full, so the | |
* following code creates a new buffer that can | |
* contain the existing contents plus the next | |
* read cycle. | |
*/ | |
// Resets the amount of buffer left to be the | |
// max buffer size | |
bufferLeft = READ_SIZE; | |
/* | |
* Sets a new size that can contain the existing | |
* buffer's contents plus space for the next | |
* read cycle. | |
*/ | |
int newSize = tempBuffer.length + READ_SIZE; | |
/* | |
* Creates a new temporary buffer, moves the | |
* contents of the old temporary buffer into it, | |
* and then points the temporary buffer variable | |
* to the new buffer. | |
*/ | |
byte[] expandedBuffer = new byte[newSize]; | |
System.arraycopy(tempBuffer, 0, expandedBuffer, 0, | |
tempBuffer.length); | |
tempBuffer = expandedBuffer; | |
} while (true); | |
/* | |
* When the entire image has been read, this creates | |
* a permanent byte buffer with the same size as | |
* the number of used bytes in the temporary buffer | |
* (equal to the next open byte, because tempBuffer | |
* is 0=based). | |
*/ | |
byteBuffer = new byte[bufferOffset]; | |
// Copies the temporary buffer to the image buffer | |
System.arraycopy(tempBuffer, 0, byteBuffer, 0, bufferOffset); | |
/* | |
* The download size is available, so this creates a | |
* permanent buffer of that length. | |
*/ | |
} else { | |
byteBuffer = new byte[contentSize]; | |
// How much of the buffer still remains empty | |
int remainingLength = contentSize; | |
// The next open space in the buffer | |
int bufferOffset = 0; | |
/* | |
* Reads into the buffer until the number of bytes | |
* equal to the length of the buffer (the size of | |
* the image) have been read. | |
*/ | |
while (remainingLength > 0) { | |
int readResult = byteStream.read( | |
byteBuffer, | |
bufferOffset, | |
remainingLength); | |
/* | |
* EOF should not occur, because the loop should | |
* read the exact # of bytes in the image | |
*/ | |
if (readResult < 0) { | |
// Throws an EOF Exception | |
throw new EOFException(); | |
} | |
// Moves the buffer offset to the next open byte | |
bufferOffset += readResult; | |
// Subtracts the # of bytes read from the | |
// remaining length | |
remainingLength -= readResult; | |
if (Thread.interrupted()) { | |
throw new InterruptedException(); | |
} | |
} | |
} | |
if (Thread.interrupted()) { | |
throw new InterruptedException(); | |
} | |
// If an IO error occurs, returns immediately | |
} catch (IOException e) { | |
e.printStackTrace(); | |
return; | |
/* | |
* If the input stream is still open, close it | |
*/ | |
} finally { | |
if (null != byteStream) { | |
try { | |
byteStream.close(); | |
} catch (Exception e) { | |
} | |
} | |
} | |
} | |
/* | |
* Stores the downloaded bytes in the byte buffer in the PhotoTask instance. | |
*/ | |
mPhotoTask.setByteBuffer(byteBuffer); | |
/* | |
* Sets the status message in the PhotoTask instance. This sets the | |
* ImageView background to indicate that the image is being | |
* decoded. | |
*/ | |
mPhotoTask.handleDownloadState(HTTP_STATE_COMPLETED); | |
// Catches exceptions thrown in response to a queued interrupt | |
} catch (InterruptedException e1) { | |
// Does nothing | |
// In all cases, handle the results | |
} finally { | |
// If the byteBuffer is null, reports that the download failed. | |
if (null == byteBuffer) { | |
mPhotoTask.handleDownloadState(HTTP_STATE_FAILED); | |
} | |
/* | |
* The implementation of setHTTPDownloadThread() in PhotoTask calls | |
* PhotoTask.setCurrentThread(), which then locks on the static ThreadPool | |
* object and returns the current thread. Locking keeps all references to Thread | |
* objects the same until the reference to the current Thread is deleted. | |
*/ | |
// Sets the reference to the current Thread to null, releasing its storage | |
mPhotoTask.setDownloadThread(null); | |
// Clears the Thread's interrupt flag | |
Thread.interrupted(); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment