Android DownloadManager 튜토리얼 및 예시

이 세션에서는 android.app.DownloadManager 클래스, 사용 방법 및 중요한 이유를 살펴봅니다. DownloadManager 클래스로 만들 수 있는 다운로드 유형, 실제 요청 방법, 알림을 통해 상태 표시줄에 진행 상황을 표시하고 다운로드한 파일을 열고 제거하는 방법.

DownloadManager가 무엇인가요?

‘DownloadManager’는 장기 실행 다운로드를 처리하는 데 사용할 수 있는 시스템 서비스입니다.

왜 다운로드합니까?

다운로드의 필요성과 다운로드가 얼마나 복잡해질 수 있는지 과소평가하지 마십시오. 현재 시대의 대역폭 상태는 적어도 세계의 많은 지역에서 우리가 필요할 때 언제든지 필요한 파일을 얻을 수 없다는 것을 의미합니다. 비용이 많이 들고 장치의 배터리가 소모되기 때문에 사용자는 항상 인터넷을 켜두지 않습니다.

Doc Translator: 문서 번역기는 어떻게 사용합니까?

Doc Translator: 문서 번역기는...
Doc Translator: 문서 번역기는 어떻게 사용합니까?

따라서 파일을 다운로드하고 로컬에 저장할 수 있는 것이 중요합니다. 그러나 이것은 제대로 구현하기가 쉬운 일이 아닙니다. 특히 정확하고 효율적으로 수행합니다. 그러나 앱 간에 절대적으로 공유할 수 있는 작업 중 하나입니다. http 다운로드를 하는 동안 바퀴를 다시 발명할 필요가 거의 없습니다. 이 작업을 효율적으로 수행하고 작업이 완료되면 앱에 알릴 수 있는 사용하기 쉬운 클래스를 하나 갖는 것이 좋습니다.

데이터를 다운로드할 수 있다는 것은 우리 앱이 사용할 수 있는 인터넷에서 파일을 얻을 수 있기 때문에 앱을 풍부하게 하는 강력한 방법입니다. 사용자가 파일을 제거한 경우 다시 다운로드할 수 있습니다.

일반적인 질문

다음은 DownloadManager 클래스를 이해하는 데 도움이 되는 몇 가지 질문입니다.

DownloadManager가 처리하는 다운로드 유형

일반적으로 장치 간 통신에는 여러 프로토콜이 있습니다. 그리고 확실히 파일을 다운로드하는 것은 적어도 두 개의 장치 사이의 통신 또는 형식일 뿐입니다. 한 장치가 파일을 제공하는 동안 다른 장치가 파일을 수신합니다. DownloadManagerHTTP 다운로드만 처리하는 데 사용됩니다.

‘DownloadManager’는 다운로드가 오래 실행되는 경우 특히 유용합니다.

다운로드는 어디에서 이루어집니까?

스레드와 관련하여. 물론 다운로드는 백그라운드 스레드에서 이루어집니다.

왜 DownloadManager를 사용합니까?

그럼 장점이 뭔가요? 글쎄요. 예를 들어

  1. 우리가 말했듯이 다운로드는 백그라운드 스레드에서 발생합니다. 실제로 시스템 앱의 배경에서. 즉, 앱이 기본 스레드에서 수동으로 다운로드를 처리할 필요가 없으므로 UI가 항상 반응합니다. 이 사실은 상당히 중요합니다. 데이터 다운로드는 개인 가젯이 수행하는 가장 많은 시간과 리소스를 소모하는 작업 중 하나입니다. 그러나 실제로 장치를 있는 것만큼 강력하게 만드는 작업 중 하나입니다. 그래서 우리는 그것들을 해야 합니다. 그러나 우리는 백그라운드 스레드에서 작업을 수행해야 하며 DownloadManager는 확실히 준수합니다.
  2. 두 번째로 HTTP 상호 작용은 우리에게서 추상화됩니다. 다양한 HTTP 코드와 메시지, 발생할 수 있는 오류에 대해 걱정할 필요가 없습니다. 실제로 재시도도 당사를 대신하여 이루어집니다. 그러나 이러한 재시도는 연결 상태 변경 및 시스템 재부팅을 통해 놀랍게도 지속될 수 있습니다. 다운로드가 진행됨에 따라 진행 상황이 표시될 수 있으므로 매우 사용자 친화적입니다.

DownloadManager 빠른 방법 및 스니펫

1. DownloadManager 클래스를 사용하여 비디오를 다운로드하는 방법

Android에서 DownloadManager 클래스를 사용하여 비디오를 다운로드하고 싶습니다. 우리는 그들에게 적절한 이름을 부여하고 외부 저장소에 저장하기를 원합니다.

    public void downloadvideo(String videoURL)
    {
        if(videoURL.contains(".mp4"))
        {
            File directory = new File(Environment.getExternalStorageDirectory()+File.separator+"My Videos");
            directory.mkdirs();
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(videoURL));
            int Number=pref.getFileName();
            request.allowScanningByMediaScanner();
            request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            File root = new File(Environment.getExternalStorageDirectory() + File.separator+"Facebook Videos");
            Uri path = Uri.withAppendedPath(Uri.fromFile(root), "Video-"+Number+".mp4");
            request.setDestinationUri(path);
            DownloadManager dm = (DownloadManager)getActivity().getSystemService(getActivity().DOWNLOAD_SERVICE);
            if(downloadlist.contains(videoURL))
            {
                Toast.makeText(getActivity().getApplicationContext(),"The Video is Already Downloading",Toast.LENGTH_LONG).show();
            }
            else
            {
                downloadlist.add(videoURL);
                dm.enqueue(request);
                Toast.makeText(getActivity().getApplicationContext(),"Downloading Video-"+Number+".mp4",Toast.LENGTH_LONG).show();
                Number++;
                pref.setFileName(Number);
            }

        }
    }

먼저 비디오 URL을 매개변수로 전달했습니다. mp4 파일만 다운로드하고 싶다고 가정해 봅시다. 그래서 mp4가 ‘포함’되어 있는지 확인하기 위해 간단한 부울 검사를 제공합니다. 원한다면 contains() 대신 endsWith()를 사용할 수도 있습니다.

DownloadManager 예제

몇 가지 DownloadManager 예제를 살펴보겠습니다.

예 1. 파일 다운로드, 전체 다운로드 보기, 다운로드 삭제

이 완전한 예제에서 우리는 downloadManager 클래스를 사용하여 인터넷에서 파일을 다운로드하는 방법을 보고 싶습니다. 그런 다음 시스템 표시줄이나 앱 내부에서 알림을 클릭하여 다운로드를 열 수 있습니다. 또한 파일을 삭제하고 모든 다운로드 등을 볼 수 있습니다.

동영상 튜토리얼

여기에 이 ​​예제에 대한 비디오 자습서가 있습니다.

데모

다음은 앱의 데모입니다.

앱 개요

  1. 사용자가 다운로드 버튼을 클릭합니다.
  2. 다운로드가 시작됩니다.
  3. 그동안 진행 상황은 알림으로 상태 표시줄에 표시됩니다. 알림에는 다운로드의 제목과 설명도 포함됩니다.
  4. 다운로드가 완료되면 시스템 상태 표시줄 텍스트를 통해 사용자에게 알립니다.
  5. 사용자가 알림을 클릭하면 파일이 열립니다. 발견되지 않으면 토스트 메시지가 표시됩니다.
  6. 사용자가 ‘모두 보기’ 버튼을 클릭하면 모든 다운로드를 표시하는 보기가 표시됩니다. 다른 다운로드가 대기열에 있을 수 있으며 일부는 애플리케이션에서 제공되지 않습니다.
  7. 사용자가 ‘삭제’ 버튼을 클릭하면 부분 또는 전체 다운로드가 삭제됩니다.
(ㅏ). 메인 액티비티.자바

상상할 수 있는 ‘MainActivity’는 런처 활동입니다. ‘AppCompatActivity’에서 파생됩니다. 여기에서 모든 코드를 작성합니다. 그러나 먼저 여러 수입품을 만드는 것으로 시작합니다. 여기에는 다음이 포함됩니다.

  1. DownloadManager – 시스템 다운로드 관리자를 사용할 수 있도록 하는 클래스입니다.
  2. 의도 – 시스템 다운로드 관리자 보기를 열 수 있도록 하는 클래스입니다.
  3. Uri – 파일을 다운로드할 수 있는 Uri로 문자열 URL을 구문 분석할 수 있습니다.
  4. 환경 – 다운로드한 파일이 저장될 공용 디렉토리를 지정하도록 허용합니다.
  5. 토스트 – 빠른 메시지를 표시할 수 있습니다.

하우투

몇 가지 방법을 살펴보겠습니다.

(ㅏ). DownloadManager를 초기화하는 방법

시스템 서비스로서 DownloadManager는 직접 인스턴스화되지 않습니다. 대신 getSystemService() 메소드를 사용하여 초기화하고 결과 객체를 DownloadManager 클래스로 캐스팅합니다.

    private void initializeDownloadManager() {
        downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
    }

(비). 다운로드 요청 생성 방법

DownloadManager 클래스 내부에는 Request 클래스라는 내부 클래스가 있습니다. 이 클래스를 사용하여 HTTP 요청을 정의할 수 있습니다. 빌더 패턴을 통해 편리하게 이 작업을 수행합니다.

        DownloadManager.Request request=new DownloadManager.Request(Uri.parse("https://raw.githubusercontent.com/Oclemy/SampleJSON/master/spacecrafts/voyager.jpg"));
        request.setTitle("Voyager")
                .setDescription("File is downloading...")
                .setDestinationInExternalFilesDir(this,
                        Environment.DIRECTORY_DOWNLOADS,fileName)
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

분명히 우리는 UriRequest 생성자에 전달했음을 알 수 있습니다. 그런 다음 제목, 설명, 대상 및 알림 가시성을 설정합니다.

(씨). 다운로드를 대기열에 넣는 방법

다운로드 대기열에 추가한다는 것은 다운로드 관리자의 다운로드 대기열에 추가하는 것을 의미합니다. 그런 다음 대기열은 시스템에서 자동으로 처리됩니다. ‘enqueue()’ 메서드를 사용하여 다운로드를 대기열에 넣습니다.

        downLoadId=downloadManager.enqueue(request);

그러면 다운로드 ID가 반환됩니다.

(디). 다운로드한 파일 제거/삭제 방법

DownloadManager 클래스의 remove() 메서드를 사용하여 다운로드 관리자에서 다운로드한 파일을 제거할 수 있습니다. 다운로드 ID를 전달합니다.

    private void deleteDownloadedFile(){
        downloadManager.remove(downLoadId);
    }

다음은 전체 소스 코드입니다.

package info.camposha.mrdownloadmanager;

import android.app.DownloadManager;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import java.io.FileNotFoundException;

public class MainActivity extends AppCompatActivity {

    private DownloadManager downloadManager;
    private String fileName=null;
    private long downLoadId;

    /**
     * Initialize download manager
     */
    private void initializeDownloadManager() {
        downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        fileName="Voyager";
    }

    /**
     * Set the title and desc of this download, to be displayed in notifications.
     */
    private void downloadFile(){
        DownloadManager.Request request=new DownloadManager.Request(Uri.parse("https://raw.githubusercontent.com/Oclemy/SampleJSON/master/spacecrafts/voyager.jpg"));
        request.setTitle("Voyager")
                .setDescription("File is downloading...")
                .setDestinationInExternalFilesDir(this,
                        Environment.DIRECTORY_DOWNLOADS,fileName)
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        //Enqueue the download.The download will start automatically once the download manager is ready
        // to execute it and connectivity is available.
        downLoadId=downloadManager.enqueue(request);
    }

    /**
     * Cancel downloads and remove them from the download manager.
     * If there is a downloaded file, partial or complete, it is deleted.
     */
    private void deleteDownloadedFile(){
        downloadManager.remove(downLoadId);
    }
    private void openOurDownload(){
       //we can open download here
    }

    /**
     * View all downloads in the downloadmanager
     */
    private void viewAllDownloads(){
        Intent intent=new Intent();
        intent.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS);
        startActivity(intent);
    }

    /**
     * Handle button clicks
     * @param view
     */
    public void clickView(View view){
        switch (view.getId()){
            case R.id.downloadBtn:
                downloadFile();
                break;
            case R.id.openDownloadBtn:
                openOurDownload();
                break;
            case R.id.viewDownloadsBtn:
                viewAllDownloads();
                break;
            case R.id.deleteBtn:
                deleteDownloadedFile();
                break;
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initializeDownloadManager();
    }
}
(b). 활동_main.xml

우리는 MainActivity에 대한 레이아웃이 필요합니다. 여기 코드가 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout

android_layout_width="match_parent"
android_layout_height="match_parent"
    android_orientation="vertical"
android_background="#009688"
tools_context=".MainActivity">

<TextView
    android_id="@+id/headerTxt"
    android_layout_width="match_parent"
    android_layout_height="wrap_content"
    android_text="DownloadManager Example"
    android_textAlignment="center"
    android_fontFamily="casual"
    android_textStyle="bold"
    android_textAppearance="@style/TextAppearance.AppCompat.Large"
    android_textColor="@color/white" />

<ImageView
    android_layout_width="match_parent"
    android_layout_height="300dp"
    android_layout_centerHorizontal="true"
    android_src="@drawable/logo"
    android_contentDescription="@string/app_name"/>

<LinearLayout
    android_id="@+id/button_zone"
    android_layout_width="match_parent"
    android_layout_height="wrap_content"
    android_orientation="horizontal"
    android_layout_centerInParent="true">

    <Button
        android_id="@+id/downloadBtn"
        style="?android:attr/button"
        android_layout_width="0dp"
        android_layout_height="wrap_content"
        android_layout_weight="1"
        android_onClick="clickView"
        android_text="Download" />
    <Button
        android_id="@+id/viewDownloadsBtn"
        style="?android:attr/button"
        android_layout_width="0dp"
        android_layout_height="wrap_content"
        android_layout_weight="1"
        android_onClick="clickView"
        android_text="View All" />
</LinearLayout>

    <LinearLayout
        android_layout_width="match_parent"
        android_layout_height="wrap_content"
        android_orientation="horizontal"
        android_layout_centerInParent="true">
    <Button
        android_id="@+id/openDownloadBtn"
        style="?android:attr/button"
        android_layout_width="0dp"
        android_layout_height="wrap_content"
        android_layout_weight="1"
        android_onClick="clickView"
        android_text="Open" />

        <Button
            android_id="@+id/deleteBtn"
            style="?android:attr/button"
            android_layout_width="0dp"
            android_layout_height="wrap_content"
            android_layout_weight="1"
            android_onClick="clickView"
            android_text="Delete" />

</LinearLayout>

</LinearLayout>
(씨). AndroidManifest.xml

AndroidManifest에서 인터넷 연결 및 외부 저장소에 대한 쓰기 권한을 추가해야 합니다. 인터넷에서 이미지를 다운로드하여 외부 저장소에 쓰기 때문입니다.

여기 내 전체 AndroidManifest.xml 파일이 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest
    package="info.camposha.mrdownloadmanager">

    <uses-permission android_name="android.permission.INTERNET"/>
    <uses-permission android_name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android_allowBackup="true"
        android_icon="@mipmap/ic_launcher"
        android_label="@string/app_name"
        android_roundIcon="@mipmap/ic_launcher_round"
        android_supportsRtl="true"
        android_theme="@style/AppTheme">
        <activity android_name=".MainActivity">
            <intent-filter>
                <action android_name="android.intent.action.MAIN" />

                <category android_name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
다운로드

다음은 참조 리소스입니다.

번호 위치 링크
1. 깃허브 직접 다운로드
2. 깃허브 찾아보기
3. 유튜브 동영상 튜토리얼
4. 유튜브 프로그래밍위저즈 TV채널

예제 2: Kotlin Android 간단한 DownloadManager 예제

완전 초보자에게 적합한 간단한 오픈 소스 Kotlin 다운로드 관리자 예제입니다.

1단계: 프로젝트 생성

빈 AndroidStudio 프로젝트를 생성하여 시작합니다.

2단계: 종속성

이 프로젝트에는 특별한 타사 라이브러리가 필요하지 않습니다.

3단계: 레이아웃 디자인

아래와 같이 ConstraintLayout에 텍스트 보기를 추가하여 MainActivity 레이아웃을 디자인합니다.

활동_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

4단계: 코드 작성

가져오기를 추가하여 시작합니다.

import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.opengl.Visibility
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

AppCompatActivity를 확장하여 MainActivity를 만듭니다.

class MainActivity : AppCompatActivity() {

다운로드 ID 정의:

    var downloadid: Long = 0

onCreate() 콜백 재정의:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

익명의 BroadcastReceiver를 만들고 그 내부에서 다음과 같이 ‘onReceive()’를 재정의합니다.

        val new = object: BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
                if (id == downloadid) Log.d("DOWNLOAD", "DONE")
            }
        }

다운로드 URI 생성:

        val uri = Uri.parse("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")

DownloadRequest 초기화:

        val request = DownloadManager.Request(uri).setDescription("DummyFile").setTitle("Dummy").setAllowedOverMetered(true).setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)

다운로드 대기열에 추가:

        val location = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
        downloadid = location.enqueue(request)

BroadcastReceiver 등록:

        registerReceiver(new, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))

전체 코드는 다음과 같습니다.

MainActivity.kt

import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.opengl.Visibility
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log

class MainActivity : AppCompatActivity() {

    var downloadid: Long = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val new = object: BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                val id = intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
                if (id == downloadid) Log.d("DOWNLOAD", "DONE")
            }
        }

        val uri = Uri.parse("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")

        val request = DownloadManager.Request(uri).setDescription("DummyFile").setTitle("Dummy").setAllowedOverMetered(true).setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)

        val location = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
        downloadid = location.enqueue(request)

        registerReceiver(new, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
    }
}

5단계: 권한 추가

‘AndroidManifest’에서 다음 권한을 추가합니다.

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

운영

코드를 복사하거나 아래 링크에서 다운로드하여 빌드하고 실행합니다.

참조

참조 링크는 다음과 같습니다.

다운로드 예제
팔로우 코드 작성자

예제 3: Kotlin Android – 동적 다운로드

이 예에서는 링크에서 다운로드하는 방법을 배웁니다. 링크는 EditText를 통해 런타임에 제공됩니다.

1단계: 프로젝트 생성

빈 AndroidStudio 프로젝트를 생성하여 시작합니다.

2단계: 종속성

이 프로젝트에는 타사 종속성이 필요하지 않습니다.

3단계: 레이아웃 디자인

메인 액티비티의 레이아웃에 EditText와 Button을 추가하세요:

활동_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/tilLink"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"

        android:hint="Введите ссылку"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etLink"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btnLoad"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="load"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tilLink" />

</androidx.constraintlayout.widget.ConstraintLayout>

4단계: 코드 작성

다음과 같이 MainActivity.kt에 가져오기를 추가하여 시작합니다.

import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.view.Gravity
import android.widget.PopupMenu
import android.widget.Toast
import com.google.android.material.textfield.TextInputEditText

활동 만들기:

class MainActivity : AppCompatActivity() {

인스턴스 필드 정의:

    private var binding: ActivityMainBinding? = null
    private var dm: DownloadManager? = null

다음과 같이 익명의 BroadcastReceiver 클래스를 만들고 ‘onReceive()’ 메서드를 구현합니다.

    private val broadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action
            if (DownloadManager.ACTION_DOWNLOAD_COMPLETE == action) {
                Toast.makeText(context, "Download Completed", Toast.LENGTH_SHORT).show()
            }
        }
    }

설정 이벤트 핸들러:

    private fun setupListeners() {
        binding?.btnLoad?.setOnLongClickListener {
            val popup = PopupMenu(this, it)
            popup.inflate(R.menu.popup)
            popup.show()
            download()
            false
        }
    }

항목을 다운로드하는 기능은 다음과 같습니다.

    private fun download() {
        dm?.enqueue(
                DownloadManager.Request(Uri.parse(LINK_VIDEO))
                        .setAllowedNetworkTypes(
                                DownloadManager.Request.NETWORK_MOBILE or
                                        DownloadManager.Request.NETWORK_WIFI
                        )
                        .setTitle("Download File.mp4")
                        .setDescription("This is very important file")
                        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
//                        .setDestinationInExternalFilesDir(
//                                applicationContext , Environment.DIRECTORY_DOWNLOADS,   "managerDownload24.mp4"
//                        )
                        .setDestinationInExternalPublicDir(
                                Environment.DIRECTORY_DOWNLOADS,
                                "managerDownload24"
                        )
        )
    }

onCreate()에서 BroadcastReceiver 등록을 취소합니다.

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(broadcastReceiver)
    }

전체 코드는 다음과 같습니다.

MainActivity.kt

import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.view.Gravity
import android.widget.PopupMenu
import android.widget.Toast
import com.google.android.material.textfield.TextInputEditText
import ru.trinitydigital.downloadmanager.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private var binding: ActivityMainBinding? = null
    private var dm: DownloadManager? = null

    private val broadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action
            if (DownloadManager.ACTION_DOWNLOAD_COMPLETE == action) {
                Toast.makeText(context, "Download Completed", Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding?.root)
        registerReceiver(broadcastReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
        dm = getSystemService(DOWNLOAD_SERVICE) as DownloadManager

        setupListeners()
    }

    private fun setupListeners() {
        binding?.btnLoad?.setOnLongClickListener {
            val popup = PopupMenu(this, it)
            popup.inflate(R.menu.popup)
            popup.show()
            download()
            false
        }
    }

    private fun download() {
        dm?.enqueue(
                DownloadManager.Request(Uri.parse(LINK_VIDEO))
                        .setAllowedNetworkTypes(
                                DownloadManager.Request.NETWORK_MOBILE or
                                        DownloadManager.Request.NETWORK_WIFI
                        )
                        .setTitle("Download File.mp4")
                        .setDescription("This is very important file")
                        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
//                        .setDestinationInExternalFilesDir(
//                                applicationContext , Environment.DIRECTORY_DOWNLOADS,   "managerDownload24.mp4"
//                        )
                        .setDestinationInExternalPublicDir(
                                Environment.DIRECTORY_DOWNLOADS,
                                "managerDownload24"
                        )
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(broadcastReceiver)
    }

    companion object {
        private const val LINK_VIDEO = "https://images.unsplash.com/photo-1569974507005-6dc61f97fb5c?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MXx8anBnfGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=900&q=60"
    }
}

안드로이드 매니페스트

‘AndroidManifest.xml’에서 다음 권한을 추가합니다.

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

운영

코드를 복사하거나 아래 링크에서 다운로드하여 빌드하고 실행합니다.

참조

참조 링크는 다음과 같습니다.

번호 링크
1. 다운로드 예제
2. 팔로우 코드 작성자

예제 4: 런타임 권한 및 코루틴이 있는 Kotlin DownloadManager

다음은 Kotlin으로 작성된 또 다른 DownloadManager 예제입니다. 이것은 Kotlin 코루틴을 사용합니다. 또한 다운로드 전에 런타임에 필요한 권한을 확인하는 작업도 포함됩니다.

1단계: 프로젝트 생성

빈 ‘AndroidStudio’ 프로젝트를 생성하여 시작합니다.

2단계: 종속성

타사 종속성이 사용되지 않습니다.

3단계: 레이아웃 디자인

버튼과 텍스트 보기를 사용하여 간단한 레이아웃을 디자인합니다.

활동_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"

    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerVertical="true"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/text_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:padding="12dp"
            android:textColor="@color/black"
            android:textSize="25sp" />

        <Button
            android:id="@+id/download_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:padding="12dp"
            android:text="DOWNLOAD PDF"
            android:textColor="@android:color/white" />

    </LinearLayout>

</RelativeLayout>

4단계: 코드 작성

가져오기를 추가하여 시작합니다.

import android.Manifest
import android.annotation.TargetApi
import android.app.DownloadManager
import android.content.Context
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File

AppCompatActivity 확장:`

class MainActivity : AppCompatActivity() {

그런 다음 다운로드 URL을 인스턴스 필드로 정의합니다.

    private var imageUrl = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"

‘onCreate()’ 재정의:

    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 an image
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                askPermissions()
            } else {
                downloadImage(imageUrl)
            }
        }

승인되면 다운로드를 시작합니다.

이미지 다운로드를 시작하는 기능은 다음과 같습니다.

    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("abc")
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
                .setDestinationInExternalPublicDir(
                    Environment.DIRECTORY_DOWNLOADS,
                    url.substring(url.lastIndexOf("/") + 1)
                )

        }
        //use when just to download the file with getting status
        //downloadManager.enqueue(request)

        val downloadId = downloadManager.enqueue(request)
        val query = DownloadManager.Query().setFilterById(downloadId)

        lifecycleScope.launchWhenStarted {
            var lastMsg: String = ""
            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))
                val msg: String? = statusMessage(url, File(Environment.DIRECTORY_DOWNLOADS), status)
                Log.e("DownloadManager", " Status is :$msg")
                if (msg != lastMsg) {
                    withContext(Dispatchers.Main) {
                        // Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
                        text_view.text = msg
                        //Log.e("DownloadManager", "Status is :$msg")
                    }
                    lastMsg = msg ?: ""
                }
                cursor.close()
            }
        }
    }

다음은 다운로드 상태를 사용자에게 표시할 수 있는 문자열로 구성하고 반환하는 함수입니다.

    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 -> "PDF downloaded successfully in $directory" + File.separator + url.substring(
                url.lastIndexOf("/") + 1
            )
            else -> "There's nothing to download"
        }
        return msg
    }

다음 기능을 사용하면 다운로드를 진행하기 전에 필요한 권한을 요청할 수 있습니다.

    @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("Allow") { 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)
        }
    }

그런 다음 다음 콜백을 다시 분석하여 권한 요청을 처리합니다.

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        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.
            }
        }
    }

전체 코드는 다음과 같습니다.

MainActivity.kt

import android.Manifest
import android.annotation.TargetApi
import android.app.DownloadManager
import android.content.Context
import android.content.pm.PackageManager
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File

class MainActivity : AppCompatActivity() {
    private var imageUrl = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
    //private var imageUrl = "http://10.0.2.2:8087/getFile"

    //private var imageUrl = "https://www.orimi.com/pdf-test.pdf"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        download_btn.setOnClickListener {
            //http://localhost:8087/getFile
            //downloadImage("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")
            //downloadImage("http://localhost:8087/getFile")
            // After API 23 (Marshmallow) and lower Android 10 you need to ask for permission first before save an image
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                askPermissions()
            } else {
                downloadImage(imageUrl)
            }
        }
    }

    private fun downloadImage(url: String) {
        //val directory = File(Environment.DIRECTORY_PICTURES)

//        if (!directory.exists()) {
//            directory.mkdirs()
//        }

        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("abc")
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
                .setDestinationInExternalPublicDir(
                    Environment.DIRECTORY_DOWNLOADS,
                    url.substring(url.lastIndexOf("/") + 1)
                )

        }
        //use when just to download the file with getting status
        //downloadManager.enqueue(request)

        val downloadId = downloadManager.enqueue(request)
        val query = DownloadManager.Query().setFilterById(downloadId)

        lifecycleScope.launchWhenStarted {
            var lastMsg: String = ""
            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))
                val msg: String? = statusMessage(url, File(Environment.DIRECTORY_DOWNLOADS), status)
                Log.e("DownloadManager", " Status is :$msg")
                if (msg != lastMsg) {
                    withContext(Dispatchers.Main) {
                        // Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
                        text_view.text = msg
                        //Log.e("DownloadManager", "Status is :$msg")
                    }
                    lastMsg = msg ?: ""
                }
                cursor.close()
            }
        }
    }

    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 -> "PDF downloaded successfully in $directory" + File.separator + url.substring(
                url.lastIndexOf("/") + 1
            )
            else -> "There's nothing to download"
        }
        return msg
    }

    @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("Allow") { 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)
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        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.
            }
        }
    }

    companion object {
        private const val MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 1
    }
}

운영

코드를 복사하거나 아래 링크에서 다운로드하여 빌드하고 실행합니다.

참조

참조 링크는 다음과 같습니다.

다운로드 예제
팔로우 코드 작성자