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.
Create the service interface
1public interface IMyService {
2 @GET
3 @Streaming
4 Call getFile(@Url String url);
5}
Set permissions
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 file
Add the following code before opening the <application> in the AndroidManifest.xml file:
Create the AsyncTask
Since I’m downloading an image, I’m going to save the file in a folder named after the app in the pictures directory.
1private class DownloadFileAsyncTask extends AsyncTask {
2
3 final String appDirectoryName = "AppName";
4 final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(
5 Environment.DIRECTORY_PICTURES), appDirectoryName);
6 final String filename = "image.jpg";
7
8 @Override
9 protected Boolean doInBackground(InputStream... params) {
10 InputStream inputStream = params[0];
11 File file = new File(imageRoot, filename);
12 OutputStream output = null;
13 try {
14 output = new FileOutputStream(file);
15
16 byte[] buffer = new byte[1024]; // or other buffer size
17 int read;
18
19 Log.d(TAG, "Attempting to write to: " + imageRoot + "/" + filename);
20 while ((read = inputStream.read(buffer)) != -1) {
21 output.write(buffer, 0, read);
22 Log.v(TAG, "Writing to buffer to output stream.");
23 }
24 Log.d(TAG, "Flushing output stream.");
25 output.flush();
26 Log.d(TAG, "Output flushed.");
27 } catch (IOException e) {
28 Log.e(TAG, "IO Exception: " + e.getMessage());
29 e.printStackTrace();
30 return false;
31 } finally {
32 try {
33 if (output != null) {
34 output.close();
35 Log.d(TAG, "Output stream closed sucessfully.");
36 }
37 else{
38 Log.d(TAG, "Output stream is null");
39 }
40 } catch (IOException e){
41 Log.e(TAG, "Couldn't close output stream: " + e.getMessage());
42 e.printStackTrace();
43 return false;
44 }
45 }
46 return true;
47 }
48
49 @Override
50 protected void onPostExecute(Boolean result) {
51 super.onPostExecute(result);
52
53 Log.d(TAG, "Download success: " + result);
54 // TODO: show a snackbar or a toast
55 }
56}
In my case, the class is an inner class. TAG is defined in the outer class as:
1private static final String TAG = "###ActivityName";
Build an instance of the Service
1Retrofit retrofit = new Retrofit.Builder()
2 .baseUrl("https://api.base.url")
3 .build();
4IMyService 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.
Initiate the download
1service.getFile("https://image.url/goes/here).enqueue(new Callback() {
2 @Override
3 public void onResponse(Call call, Response response) {
4 Log.d(TAG, response.message());
5 if(!response.isSuccess()){
6 Log.e(TAG, "Something's gone wrong");
7 // TODO: show error message
8 return;
9 }
10 DownloadFileAsyncTask downloadFileAsyncTask = new DownloadFileAsyncTask();
11 downloadFileAsyncTask.execute(response.body().byteStream());
12 }
13
14 @Override
15 public void onFailure(Call call, Throwable t) {
16 Log.e(TAG, t.getMessage());
17 // TODO: show error message
18 }
19});