安卓下载管理器已完成。

71

Android中的下载管理器有一个小问题。这是我第一次使用它,已经成功地下载了多个文件并打开了它们。但我的问题是如何检查下载是否完成。

情况是我下载一个PDF文件并打开它,通常文件很小,所以在打开之前就完成了。但如果文件稍微大一些,我该如何在打开它之前检查下载管理器是否已经完成了下载。

我的下载方式:

Intent intent = getIntent();
DownloadManager downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
Uri Download_Uri = Uri.parse(intent.getStringExtra("Document_href"));
DownloadManager.Request request = new DownloadManager.Request(Download_Uri);

//Restrict the types of networks over which this download may proceed.
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
//Set whether this download may proceed over a roaming connection.
request.setAllowedOverRoaming(false);
//Set the title of this download, to be displayed in notifications.
request.setTitle(intent.getStringExtra("Document_title"));
//Set the local destination for the downloaded file to a path within the application's external files directory
request.setDestinationInExternalFilesDir(this,Environment.DIRECTORY_DOWNLOADS,intent.getStringExtra("Document_title") + ".pdf");
//Enqueue a new download and same the referenceId
Long downloadReference = downloadManager.enqueue(request);

我如何打开这个文件

Uri uri = Uri.parse("content://com.app.applicationname/" + "/Download/" + intent.getStringExtra("Document_title") + ".pdf");
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(uri, "application/pdf");
target.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

startActivity(target);

所以在下载和打开文件的过程中,我希望能够添加一个if语句来检查它是否应该继续执行或等待文件。

6个回答

104

当下载完成时,DownloadManager 会发送广播,因此您需要使用相应的意图动作(ACTION_DOWNLOAD_COMPLETE)注册广播接收器来捕获此广播:

要注册接收器

registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

和一个BroadcastReceiver处理程序

BroadcastReceiver onComplete=new BroadcastReceiver() {
    public void onReceive(Context ctxt, Intent intent) {
        // your code
    }
};

你也可以创建AsyncTask来处理大文件的下载。

创建一个下载对话框以显示下载状态,然后处理文件的打开:

protected void openFile(String fileName) {
    Intent install = new Intent(Intent.ACTION_VIEW);
    install.setDataAndType(Uri.fromFile(new File(fileName)),"MIME-TYPE");
    startActivity(install);
}

您还可以查看示例链接

示例代码


3
registerReceiver 函数应该在哪个对象上调用? - sudoExclaimationExclaimation
嘿,我能否为除“下载完成”以外的所有操作注册接收器? - Aditi Parikh
2
@PranoyC 关于 Context 对象的内容。 - Yun
2
请确保在 onDestroy 中取消注册接收器 public void onDestroy() { super.onDestroy(); try { unregisterReceiver(onComplete); } catch (Exception e) { e.printStackTrace(); } } - UdayaLakmal
context.registerReceiver 对我有用。 - MindRoasterMir
显示剩余2条评论

70

来源:Android DownloadManager示例

被接受的答案并不完全正确。收到“ACTION_DOWNLOAD_COMPLETE”广播并不意味着您的下载已完成。请注意,当任何下载完成时,DownloadManager会广播“ACTION_DOWNLOAD_COMPLETE”。这并不一定意味着正在等待的是同一下载。

解决方案是在开始下载时保存由enqueue()返回的下载ID。这个长的下载ID在整个系统中是唯一的,并可用于检查下载状态。

DownloadManager downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
long downloadID = downloadManager.enqueue(request);// enqueue puts the download request in the queue.

按照以下三个步骤,您可以在下载完成时收到通知:

如下代码段所示,创建一个BroadcastReceiver。在接收器内部,我们仅检查接收到的广播是否为我们的下载,方法是通过将接收到的下载ID与我们已排队的下载进行匹配。

private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
       @Override
       public void onReceive(Context context, Intent intent) {
           //Fetching the download id received with the broadcast
           long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
           //Checking if the received broadcast is for our enqueued download by matching download id
           if (downloadID == id) {
               Toast.makeText(MainActivity.this, "Download Completed", Toast.LENGTH_SHORT).show();
           }
       }
   };

一旦创建 BroadcastReceiver,您可以在活动的 onCreate 方法中注册 ACTION_DOWNLOAD_COMPLETE。

@Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

   }

同样重要的是,在 onDestroy 中注销 BroadcastReceiver。这将确保您只在活动处于活动状态时侦听此广播。

@Override
  public void onDestroy() {
      super.onDestroy();
      unregisterReceiver(onDownloadComplete);
  }

我建议您在此处阅读完整的示例


2
谢谢!它需要文件存储权限吗?请参见https://dev59.com/am025IYBdhLWcg3wqXxo#54986577。如果您在一个活动中开始下载并在另一个活动中完成,会发生什么? - CoolMind

16
我花了一周的时间研究如何使用DownloadManager下载和打开文件,但始终没有找到完全适合我的答案,所以我不得不拼凑出一些方法来找到有效的解决方案。我尽力记录了我的代码。如果您有任何问题,请随时在下面的评论中留言。
此外,请不要忘记将以下行添加到AndroidManifest.xml文件中! <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 这是我的下载管理器:
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Environment;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.widget.Toast;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyDownloadListener implements DownloadListener {
    private Context mContext;
    private DownloadManager mDownloadManager;
    private long mDownloadedFileID;
    private DownloadManager.Request mRequest;

    public MyDownloadListener(Context context) {
        mContext = context;
        mDownloadManager = (DownloadManager) mContext
            .getSystemService(Context.DOWNLOAD_SERVICE);
    }

    @Override
    public void onDownloadStart(String url, String userAgent, String
        contentDisposition, final String mimetype, long contentLength) {

        // Function is called once download completes.
        BroadcastReceiver onComplete = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // Prevents the occasional unintentional call. I needed this.
                if (mDownloadedFileID == -1)
                    return;
                Intent fileIntent = new Intent(Intent.ACTION_VIEW);

                // Grabs the Uri for the file that was downloaded.
                Uri mostRecentDownload =
                    mDownloadManager.getUriForDownloadedFile(mDownloadedFileID);
                // DownloadManager stores the Mime Type. Makes it really easy for us.
                String mimeType =
                    mDownloadManager.getMimeTypeForDownloadedFile(mDownloadedFileID);
                fileIntent.setDataAndType(mostRecentDownload, mimeType);
                fileIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                try {
                    mContext.startActivity(fileIntent);
                } catch (ActivityNotFoundException e) {
                    Toast.makeText(mContext, "No handler for this type of file.",
                        Toast.LENGTH_LONG).show();
                }
                // Sets up the prevention of an unintentional call. I found it necessary. Maybe not for others.
                mDownloadedFileID = -1;
            }
        };
        // Registers function to listen to the completion of the download.
        mContext.registerReceiver(onComplete, new
            IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

        mRequest = new DownloadManager.Request(Uri.parse(url));
        // Limits the download to only over WiFi. Optional.
        mRequest.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
        // Makes download visible in notifications while downloading, but disappears after download completes. Optional.
        mRequest.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
        mRequest.setMimeType(mimetype);

        // If necessary for a security check. I needed it, but I don't think it's mandatory.
        String cookie = CookieManager.getInstance().getCookie(url);
        mRequest.addRequestHeader("Cookie", cookie);

        // Grabs the file name from the Content-Disposition
        String filename = null;
        Pattern regex = Pattern.compile("(?<=filename=\").*?(?=\")");
        Matcher regexMatcher = regex.matcher(contentDisposition);
        if (regexMatcher.find()) {
            filename = regexMatcher.group();
        }

        // Sets the file path to save to, including the file name. Make sure to have the WRITE_EXTERNAL_STORAGE permission!!
        mRequest.setDestinationInExternalFilesDir(mContext, Environment.DIRECTORY_DOWNLOADS, filename);
        // Sets the title of the notification and how it appears to the user in the saved directory.
        mRequest.setTitle(filename);

        // Adds the request to the DownloadManager queue to be executed at the next available opportunity.
        mDownloadedFileID = mDownloadManager.enqueue(mRequest);
    }
}

只需在现有的 WebView 中添加以下行:

webView.setDownloadListener(new MyDownloadListener(webView.getContext()));

这将为您的 WebView 添加一个下载监听器。

当文件下载正在进行时,如果网络连接中断,并且一段时间后恢复了互联网连接,那么如何恢复下载?你有什么想法吗?我是使用安卓的下载管理器在下载。 - Kevan Aghera

4

我来做出贡献。 在我的情况下,我下载了一个PDF文件,然后在下载完成后打开它:

val onComplete: BroadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if(intent.action == DownloadManager.ACTION_DOWNLOAD_COMPLETE){
            intent.extras?.let {
                
                //retrieving the file
                val downloadedFileId = it.getLong(DownloadManager.EXTRA_DOWNLOAD_ID)
                val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
                val uri: Uri = downloadManager.getUriForDownloadedFile(downloadedFileId)
                
                //opening it
                val intent = Intent(Intent.ACTION_VIEW, uri)
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                context.startActivity(intent)
            }
        }
    }
}

0

您无需创建文件即可查看它。可以在setDataAndType()中使用COLUMN_LOCAL_URI中的URI。请参见下面的示例。

 int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
 String downloadedPackageUriString = cursor.getString(uriIndex);
 Intent open = new Intent(Intent.ACTION_VIEW);
 open.setDataAndType(Uri.parse(downloadedPackageUriString), mimeType);
 open.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
 startActivity(open);

什么是“光标”? - Starwave

0

您可以使用服务或线程,并实现此代码,下载完成时将会通知您

    private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {    
            @Override
           public void onReceive(Context context, Intent intent) { long downloadId;
           long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
                matching download id
                if (downloadID == id) {
                    Toast.makeText(MainActivity.this, "Download Completed", Toast.LENGTH_SHORT).show();
                }
            }  
 };

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接