How to Download Image from the Web in Android using Kotlin

In this tutorial, I’m going to show you how to download an image from the Web and save it in your Phone Storage (not the SD card), using the DownloadManager on Android.

As an example, we are going to download the following image I found on the Internet and uploaded on

Creating the Image Downloader

Go to your AndroidManifest.xml file (manifests > AndroidManifest.xml) and add the following permissions:

<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Code language: HTML, XML (xml)

Go to your Activity file and create a method with a parameter url and type String.

Also, set the directory you want to save the image.

In this example, we’re using the Pictures folder in our Phone Storage.

fun downloadImage(url: String) { val directory = File(Environment.DIRECTORY_PICTURES) if (!directory.exists()) { directory.mkdirs() } }
Code language: Kotlin (kotlin)

Create a DownloadManager.Request

private fun downloadImage(url: String) { // ... val downloadManager = this.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val downloadUri = Uri.parse(url) val request = DownloadManager.Request(downloadUri).apply { setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE) .setAllowedOverRoaming(false) .setTitle(url.substring(url.lastIndexOf("/") + 1)) .setDescription("") .setDestinationInExternalPublicDir( directory.toString(), url.substring(url.lastIndexOf("/") + 1) ) } // ... } }
Code language: Kotlin (kotlin)

In request‘s options, we set as a title of the image to be the URL name.

For example, if we download an image from the following link.

Then, the download image will have funnycat.png as a name.

Now, check the status of your download and show it in Toast.

var msg: String? = "" var lastMsg = "" private fun downloadImage(url: String) { // ... val downloadId = downloadManager.enqueue(request) val query = DownloadManager.Query().setFilterById(downloadId) Thread(Runnable { var downloading = true while (downloading) { val cursor: Cursor = downloadManager.query(query) cursor.moveToFirst() if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) { downloading = false } val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) msg = statusMessage(url, directory, status) if (msg != lastMsg) { this.runOnUiThread { Toast.makeText(this, msg, Toast.LENGTH_SHORT).show() } lastMsg = msg ?: "" } cursor.close() } }).start() } private fun statusMessage(url: String, directory: File, status: Int): String? { var msg = "" msg = when (status) { DownloadManager.STATUS_FAILED -> "Download has been failed, please try again" DownloadManager.STATUS_PAUSED -> "Paused" DownloadManager.STATUS_PENDING -> "Pending" DownloadManager.STATUS_RUNNING -> "Downloading..." DownloadManager.STATUS_SUCCESSFUL -> "Image downloaded successfully in $directory" + File.separator + url.substring( url.lastIndexOf("/") + 1 ) else -> "There's nothing to download" } return msg }
Code language: Kotlin (kotlin)

Using the Image Downloader

To use the downloader, just call the method downloadImage and pass the URL of your image, like that:

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) download_btn.setOnClickListener { downloadImage(imageUrl) } }
Code language: Kotlin (kotlin)

Asking for Permissions

If your app supports Android 9 (API level 28) and lower, you need to ask the user for permission before downloading the image on their device.

In Android 10 (API level 29) and above, you don’t need to do that.

Go to your AndroidManifest.xml file (manifests > AndroidManifest.xml) and add the following permission:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Code language: HTML, XML (xml)

Create a method askPermissions() and ask the user if they want to allow app to store images in their phone.

If they Deny, the next time an AlertDialog will show up and tell them why your app needs these permissions.

@TargetApi(Build.VERSION_CODES.M) fun askPermissions() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // Permission is not granted // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { // Show an explanation to the user *asynchronously* -- don't block // this thread waiting for the user's response! After the user // sees the explanation, try again to request the permission. AlertDialog.Builder(this) .setTitle("Permission required") .setMessage("Permission required to save photos from the Web.") .setPositiveButton("Accept") { dialog, id -> ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE) finish() } .setNegativeButton("Deny") { dialog, id -> dialog.cancel() } .show() } else { // No explanation needed, we can request the permission. ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE) // MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE is an // app-defined int constant. The callback method gets the // result of the request. } } else { // Permission has already been granted downloadImage(imageUrl) } } companion object { private const val MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1 }
Code language: Kotlin (kotlin)

After getting the permission results (Allow or Deny), the onRequestPermissionsResult method called.

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { when (requestCode) { MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE -> { // If request is cancelled, the result arrays are empty. if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { // permission was granted, yay! // Download the Image downloadImage(imageUrl) } else { // permission denied, boo! Disable the // functionality that depends on this permission. } return } // Add other 'when' lines to check for other // permissions this app might request. else -> { // Ignore all other requests. } } }
Code language: Kotlin (kotlin)

Now, you can use the image downloader like that:

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) download_btn.setOnClickListener { // After API 23 (Marshmallow) and lower Android 10 you need to ask for permission first before save in External Storage(Micro SD) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { askPermissions() } else { downloadImage(imageUrl) } } }
Code language: Kotlin (kotlin)
You can find the final project here

If you have any questionsplease feel free to leave a comment below

Notify of
Newest Most Voted
Inline Feedbacks
View all comments
Khumo Mashapa

Hello there. How can I use this to download multiple images with one click? Thank you

Khumo Mashapa

Sorry I also forgot to ask. How do I save the image to device internal storage? In case some phones don’t have SD cards

Khumo Mashapa

Oh lol. Sorry I saw SD card and I rushed to comment because literally every tutorial about this uses the SD card example. Thank you for replying to my questions so quickly.

Khumo Mashapa

Aye man. Is it possible for me to put this code in a different activity and reference a button from another activity. I’m actually trying to combine this code with a billing client, but I can’t seem to get them to work together

Khumo Mashapa

I found a way around it. I actually somehow missed a comma and a bracket, but thank you for your response. You’ve got some extremely useful tutorials too. Keep up the great work.
Thank you

Khumo Mashapa

I’ll use some of that code for another app though.

Khumo Mashapa

Sorry for bothering you again mate, but I want to know how to create my own name for the folder my images will be saved in?

Khumo Mashapa

Thank you