I've started using retrofit recently and it's intuitively simpler than Google's volley. And it's got really good benchmarks. And it's made by the same guys who made Dagger and Picasso. So you should give it a try too if you haven't.
I spent considerable amount of time figuring how to download an image yesterday. So here's how to do it using retrofit. This will work for any file type - not just images.
public interface IMyService {
@GET
@Streaming
Call<ResponseBody> getFile(@Url String url);
}
The app will require the following permissions:
INTERNET
- to access the internet & download the fileWRITE_EXTERNAL_STORAGE
- to be able to write to the filesystem & save the fileAdd the following code before opening the <application>
in the AndroidManifest.xml file:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Since I'm downloading an image, I'm going to save the file in a folder named after the app in the pictures directory.
private class DownloadFileAsyncTask extends AsyncTask<InputStream, Void, Boolean> {
final String appDirectoryName = "AppName";
final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), appDirectoryName);
final String filename = "image.jpg";
@Override
protected Boolean doInBackground(InputStream... params) {
InputStream inputStream = params[0];
File file = new File(imageRoot, filename);
OutputStream output = null;
try {
output = new FileOutputStream(file);
byte[] buffer = new byte[1024]; // or other buffer size
int read;
Log.d(TAG, "Attempting to write to: " + imageRoot + "/" + filename);
while ((read = inputStream.read(buffer)) != -1) {
output.write(buffer, 0, read);
Log.v(TAG, "Writing to buffer to output stream.");
}
Log.d(TAG, "Flushing output stream.");
output.flush();
Log.d(TAG, "Output flushed.");
} catch (IOException e) {
Log.e(TAG, "IO Exception: " + e.getMessage());
e.printStackTrace();
return false;
} finally {
try {
if (output != null) {
output.close();
Log.d(TAG, "Output stream closed sucessfully.");
}
else{
Log.d(TAG, "Output stream is null");
}
} catch (IOException e){
Log.e(TAG, "Couldn't close output stream: " + e.getMessage());
e.printStackTrace();
return false;
}
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
Log.d(TAG, "Download success: " + result);
// TODO: show a snackbar or a toast
}
}
In my case, the class is an inner class. TAG
is defined in the outer class as:
private static final String TAG = "###ActivityName";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.base.url")
.build();
IMyService service = retrofit.create(IMyService.class);
The base url is required. The url doesn't matter if you're going to be using an absolute url to download the file. Relative urls must however be relative to the base url.
service.getFile("https://image.url/goes/here).enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
Log.d(TAG, response.message());
if(!response.isSuccess()){
Log.e(TAG, "Something's gone wrong");
// TODO: show error message
return;
}
DownloadFileAsyncTask downloadFileAsyncTask = new DownloadFileAsyncTask();
downloadFileAsyncTask.execute(response.body().byteStream());
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, t.getMessage());
// TODO: show error message
}
});