了解DownloadManager

一、DownloadManager简介


        DownloadManager是android为我们提供的一个服务,这个服务主要用于优化长时间的下载任务。

        其优点是:在下载过程中如果发生一些突发情况,比如sd卡被拔出,网络变化等,其会等到状态恢复正常后继续下载任务,并且下载也支持断点下载。

        但是其也有缺点,那就是它对外只提供了添加,移除,查询任务功能。对于下载过程中的可控性不佳,比如无法手动暂停一个正在下载的任务。

         DownLoadManager有两个内部类,Request用于封装一个下载请求,Query则用于封装一个查询请求。


二、DownloadManager的使用


1、权限配置

        需要访问网络权限和写外部存储权限(如果需要的话)

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

2、配置并开启一个任务

        这里以下载android 版qq来开启一个任务。

final DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
// 这里直接写死下载一个qq android application
String url = "https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk";
Uri uri = Uri.parse(url);
DownloadManager.Request request = new DownloadManager.Request(uri);
// 设置成下载过程中和下载完成都能够看到通知
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setTitle("下载");
request.setDescription("android-qq.apk 正在下载");
request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, "qq-test.apk");
// 实际的保存地址为 内部存储/android/data/com.hh.testandroid/files/Download/qq-test.apk
request.allowScanningByMediaScanner();
currentTaskId = downloadManager.enqueue(request);

        上面通过对DownloadManager.Request进行一些配置然后通过downloadManager将这个request加入队列,然后其会返回一个long型的变量,

这个变量就可以代表这个任务,也就是该任务的id,后续的查询或者删除动作都需要使用此id。


       3、设置任务监听

// 注册任务完成监听广播
        if (receiver_download_complete == null) {
            receiver_download_complete = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    Toast.makeText(DownloadActivity.this, "下载完成", Toast.LENGTH_LONG).show();
                }
            };
            IntentFilter filter_download_complete = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
            registerReceiver(receiver_download_complete, filter_download_complete);
        }

        // 注册下载过程中的点击事件,点击进入控制界面
        if (receiver_download_clicked == null) {
            receiver_download_clicked = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    Intent intent_startActivity = new Intent(DownloadActivity.this, DownloadActivity.class);
                    startActivity(intent_startActivity);
                }
            };
            IntentFilter filter_download_clicked = new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED);
            registerReceiver(receiver_download_clicked, filter_download_clicked);
        }

     上面代码注册了两个广播,其中第一个广播主要用于监听任务完成,第二个广播监听当任务在进行的过程中用户点击了通知这一事件(PS:注意是下载过程中点击才有回调,下载完后就是根据mineType来调用对应的程序打开)。


        4、获取任务信息

DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(currentTaskId);
Cursor cursor = downloadManager.query(query);
while (cursor.moveToNext()) {
      String downId= cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_ID));
      String title = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE));
      String address = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
      String status = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
      String size= cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
      String sizeTotal = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
      StringBuilder sb = new StringBuilder();
      sb.append("downId = " + downId + "\n")
              .append("title = " + title + "\n")
              .append("address = " + address + "\n")
              .append("status = " + status + "\n")
              .append("size = " + size + "\n")
              .append("sizeTotal = " + sizeTotal + "\n");
      mTv.setText(sb.toString());
}
cursor.close();

     上面通过DownloadManager.Query这个对象来查询下载情况,返回的是一个游标,然后我们可以通过这个来进行信息读取。


        5、移除任务

private void removeTask () {
     DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
     downloadManager.remove(currentTaskId);
}
     移除一个任务只需要传递其id值即可,如果任务正在进行则暂停,并且下载的文件也会被删除掉,后续也无法使用Downloadmanager来查询到该任务的信息。


三、了解DownloadManager

 

     为什么DownloadManager能够实现自动断点续传,网络故障发生的时候停止下载,网络恢复的时候继续下载呢?
这是因为DownloadManager其本身就只是一个代理而已,我们enqueue一个task或者remove一个task,其都是通过内部的ContentResolver来联系到系统的DownloadProvider的,然后对于执行一些操作,比如enqueue则对应于insert一条task记录,移除一个task的时候就删除一条记录。而这部分的变化会触发DownloadService,然后执行对应的操作,并且写回数据。

          上面的图大致演示了这整个工作过程,
         a、其中DownloadManager通过配置Request来将数据插入到DownloadProvider中,插入的过程会启动DownloadService,然后会读取DownloadProvider中的信息,再使用线程池执行,执行后的过程会回写到DownloadProvider,方便DownloadManager来查询数据。
         b、另外当DownloadProvider中数据发送变化的时候也会被ContentProviderObserver监听到,同样也能够启动DownloadService中的逻辑,也当我们调用DownloadManager的remove方法的时候正是通过这条路径来的;
         c、而当由于储存问题,网络问题等导致下载中断后其可以通过监听特定的情况来启动DownloadReciver,进而启动DownloadService,再进行下载。


 1、添加任务的过程

        添加一个任务,当我们配置好DownloadManager.Request之后,我们会调用DownloadManager的enqueue方法,在该方法中将Request中的配置转化为ContentValues形式,然后插入到DownloadProvider中。
路径 : \frameworks\base\core\java\android\app\DownloadManager.java

public long enqueue(Request request) {
    ContentValues values = request.toContentValues(mPackageName);
    Uri downloadUri = mResolver.insert(Downloads.Impl.CONTENT_URI, values);
    long id = Long.parseLong(downloadUri.getLastPathSegment());
    return id;
}        

路径:\packages\providers\DownloadProvider\src\com\android\providers\downloads\DownloadProvider.java

public Uri insert(final Uri uri, final ContentValues values) {
    // ... 省略了对参数的判断与处理
    
    long rowID = db.insert(DB_TABLE, null, filteredValues);
    if (rowID == -1) {
        Log.d(Constants.TAG, "couldn't insert into downloads database");
        return null;
    }
    insertRequestHeaders(db, rowID, values);
    notifyContentChanged(uri, match);
    // Always start service to handle notifications and/or scanning
    final Context context = getContext();
    context.startService(new Intent(context, DownloadService.class));
    return ContentUris.withAppendedId(Downloads.Impl.CONTENT_URI, rowID);
}
从上面的方法中可以知道,其干了三件重要的事:
        a、将处理好的数据插入到数据库
        b、调用notifyContentChanged通知监听在该uri上的所有观察者
        c、启动DownloadService.

路径:\packages\providers\DownloadProvider\src\com\android\providers\downloads\DownloadService.java

public void onCreate() {
        super.onCreate();
        ....        
        mUpdateThread = new HandlerThread(TAG + "-UpdateThread");
        mUpdateThread.start();
        mUpdateHandler = new Handler(mUpdateThread.getLooper(), mUpdateCallback);
        mScanner = new DownloadScanner(this);
        mNotifier = new DownloadNotifier(this);
        mNotifier.cancelAll();
        mObserver = new DownloadManagerContentObserver();
        getContentResolver().registerContentObserver(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
                true, mObserver);
    }
这DownloadService的onCreate方法中开启了一个HandlerThread,主要用来处理更新信息。并且添加了该uri上的observer。

public int onStartCommand(Intent intent, int flags, int startId) {
        ...
        enqueueUpdate();
        return returnValue;
}
public void onChange(final boolean selfChange) {
        enqueueUpdate();
}
       而其中开启一个DownloadService和数据库发送变化的时候都会调用enqueueUpdate方法。而enqueueUpdate则会发送消息给HandlerThread执行相应的操作。在handlerThread中会先从ContentProvider中读取所有的下载记录,然后将符合启动条件的任务使用一个默认长度为10的线程池来执行下载任务,具体下载任务的执行者为DownloadThread。

       DownloadThread首先会先检查目前设备能否执行下载任务, 如果ok则会使用HttpUrlConnection来下载,如果下载过程中发生了意外情况,比如网络中断的情况,其就会抛出异常,并且将信息更新到DownloadProvider中,如果不发生异常也将信息记录到DownloadProvider中并且发送任务完成广播。


2、异常恢复

        异常恢复是因为DownloadProvider这个项目有一个监听网络变化,存储器变化,机器重启的DownloadReciver存在,一旦发生这些情况的时候就会启动DownloadService,然后又开始从数据库中读取下载情况,如果发现因为故障而暂停的任务的话就会再次启动。
路径:\packages\providers\DownloadProvider\src\com\android\providers\downloads\DownloadReciver.java

  public void onReceive(final Context context, final Intent intent) {
        if (mSystemFacade == null) {
            mSystemFacade = new RealSystemFacade(context);
        }
        String action = intent.getAction();
        if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
            if (Constants.LOGVV) {
                Log.v(Constants.TAG, "Received broadcast intent for " +
                        Intent.ACTION_BOOT_COMPLETED);
            }
            startService(context);
        } else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
            if (Constants.LOGVV) {
                Log.v(Constants.TAG, "Received broadcast intent for " +
                        Intent.ACTION_MEDIA_MOUNTED);
            }
            startService(context);
        } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
            final ConnectivityManager connManager = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            final NetworkInfo info = connManager.getActiveNetworkInfo();
            if (info != null && info.isConnected()) {
                startService(context);
            }
        } else if (action.equals(Constants.ACTION_RETRY)) {
            startService(context);
        } else {
            ....
        }
   }


3、断点续传

        任务在每次下载的过程中每隔一定的数据便更新一下DownloadProvider,这样子就能够在重新开始的时候继续上次的任务了。
路径:\packages\providers\DownloadProvider\src\com\android\providers\downloads\DownloadThread.java

 private void reportProgress(State state) {
        ......
        if (state.mCurrentBytes - state.mBytesNotified > Constants.MIN_PROGRESS_STEP &&
            now - state.mTimeLastNotification > Constants.MIN_PROGRESS_TIME) {
            ContentValues values = new ContentValues();
            values.put(Downloads.Impl.COLUMN_CURRENT_BYTES, state.mCurrentBytes);
            mContext.getContentResolver().update(mInfo.getAllDownloadsUri(), values, null, null);
            state.mBytesNotified = state.mCurrentBytes;
            state.mTimeLastNotification = now;
        }
    }
      从上面的代码中可以看出,当时间超过Constants.MIN_PROGRESS_TIME,或者下载数据超过Constants.MIN_PROGESS_STEP的话就会进行一次记录。

    /** The minimum amount of progress that has to be done before the progress bar gets updated */
    public static final int MIN_PROGRESS_STEP = 4096;
    /** The minimum amount of time that has to elapse before the progress bar gets updated, in ms */
    public static final long MIN_PROGRESS_TIME = 1500;
       而在DownloadThread开始下载的时候会判断记录的已下载任务是否和整个包一致,如果是的话也会跳过。如果不一致的话则将其添加到Http请求的请求头中。
conn.addRequestProperty("Range", "bytes=" + state.mCurrentBytes + "-");


4、删除任务

        删除任务的时候因为修改了DownloadProvider中的数据,将其中的数据项标志位“删除”。这时候会触发该uri上的ContentOnserver,然后进入enqueueUpdate方法,在HandlerThread中如果发现数据项被标志位删除的话则会先删除下载的文件后再请求DownloadProvider删除该项数据。


5、查询任务

        因为DownloadService在下载的过程中将所有的信息都记录到了DownloadProvider上,所以可以直接通过DownloadManager中的ContentProvider来直接查询得到。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android系统是一个流行的移动操作系统,支持无线网络连接的设备非常普遍。然而,有时候用户可能希望使用有线以太网连接,以获得更稳定和快速的网络连接。那么,在Android系统中,有线以太网是如何与DownloadManager一起使用的呢? 首先,需要了解DownloadManager是什么。DownloadManagerAndroid系统提供的一个系统服务,用于处理网络下载任务。它可用于在后台异步下载各种文件,并提供了管理、监控和操作下载任务的功能。 要使用有线以太网连接与DownloadManager一起下载文件,首先要确保设备支持有线以太网连接功能。通常情况下,设备上会有一个USB Type-C或Ethernet接口,用于连接有线以太网适配器。通过将有线以太网适配器插入设备的接口上,并确保设置中启用了有线以太网选项,就可以实现有线以太网连接。 一旦设备与有线以太网连接成功,就可以使用DownloadManager来执行下载任务了。首先,需要创建一个DownloadManager.Request对象,并设置要下载的文件的URL、保存文件的目录路径、显示通知栏的标题等相关参数。然后,使用DownloadManager.enqueue()方法将请求加入下载队列,并返回一个下载任务的唯一ID。 下载任务加入队列后,DownloadManager会自动处理下载任务,包括断点续传、网络异常处理等。可以通过查询DownloadManager查询下载任务的状态,以及监听下载任务的进度和完成状态。 总之,Android系统支持有线以太网连接与DownloadManager一起使用,以实现稳定和快速的文件下载。通过连接有线以太网适配器,创建DownloadManager.Request对象,并加入下载队列,就可以轻松地进行有线以太网下载任务的管理和操作。 ### 回答2: AndroidDownloadManager类提供了一种方便的方式来通过有线以太网连接下载文件。有线以太网连接通常提供了更快的下载速度和更稳定的网络连接,而DownloadManager则是Android系统中的一个强大的下载管理器。 要使用有线以太网下载,首先需要确保设备连接到一个可用的有线以太网网络。然后,可以通过以下步骤使用DownloadManager进行下载: 1. 创建一个DownloadManager实例: DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); 2. 创建一个DownloadManager.Request对象来描述下载请求: DownloadManager.Request request = new DownloadManager.Request(Uri.parse("下载链接")); // 设置一些下载请求的参数,例如设置保存文件的路径、显示通知等 3. 将下载请求添加到下载队列中: long downloadId = downloadManager.enqueue(request); 4. 可以使用下载ID来查询下载状态或管理下载任务: DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadId); // 执行查询并获取下载任务的信息 Cursor cursor = downloadManager.query(query); // 根据需要处理下载任务的状态、进度等信息 5. 在下载完成后,可以通过监听下载完成的广播或查询下载状态来获取下载的文件路径等信息: String filePath = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); 通过上述步骤,我们可以在Android设备上使用有线以太网连接来下载文件。有线以太网相较于其他网络连接方式,可以提供更快速、稳定的下载体验,并且DownloadManager类可以帮助我们方便地管理和监控下载过程。 ### 回答3: Android的有线以太网DownloadManager是一个功能强大的下载管理器,它可以帮助用户在使用有线以太网连接时更有效地下载文件。 首先,有线以太网可以提供更稳定和快速的网络连接,相较于无线网络,它更适合下载大型文件和高速数据传输。有了有线以太网DownloadManager,用户可以更好地利用这种连接优势。 该下载管理器为用户提供了直观和简单的用户界面,用户只需点击链接或输入下载地址,便可将文件添加到下载队列中。用户可以随时暂停、恢复或取消下载过程,而不会中断其他正在进行的下载任务。这种灵活性使得用户能够更好地控制文件的下载进程。 有线以太网DownloadManager还支持多线程下载,这意味着它可以同时下载一个文件的不同部分,从而提高下载速度。此外,它利用了文件分片技术,将大文件分割成多个较小的块进行下载,从而提高整体下载效率。 另一个重要的功能是断点续传。如果下载过程中网络连接中断,用户只需重新连接到有线以太网并重新启动下载管理器,该管理器将自动检测到未完成的下载并继续下载。这样,用户不必重新下载整个文件,节省了时间和网络资源。 总之,Android的有线以太网DownloadManager为用户提供了更快速、稳定和灵活的下载体验。它利用有线以太网的网络连接和多线程下载技术,使用户能够更高效地下载大文件。此外,它还支持断点续传,确保用户可以在中断的情况下无缝恢复下载。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值