Android DownloadManager 更新应用
DownloadManager
是 Android
处理长期运行的 HTTP 下载的系统服务
客户端可以请求的 URI 被下载到一个特定的目标文件
客户端可以在后台与 http 交互进行下载,或者在下载失败,或者连接改变,重新启动系统后重新下载,还可以进入系统的下载管理界面查看进度
DownloadManager
DownloadManger 有两个内部类 Request
和 Query
Request
类可设置下载的一些属性Query
类可查询当前下载的进度,下载地址,文件存放目录等数据
所需权限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
获得对象,开始下载
DownloadManager dm = (DownloadManager)getSystemService(DOWNLOAD_SERVICE); DownloadManager.Request req = new DownloadManager.Request(Uri.parse(url)); long id = dm.enqueue(req);
每下载的一个文件对应一个 id
,通过此 id
可以查询数据
取消下载
dm.remove(id1, id2, id3);
返回成功取消的下载的个数,如果一个下载被取消了,所有相关联的文件,部分下载的文件和完全下载的文件都会被删除.
Request 类
Request
类用于设置下载的一些属性
指定下载位置,及文件名称
/** * 目录: Android -> data -> 包名 -> files -> Download -> xxx.mp3 * 这个文件是你的应用所专用的,软件卸载后,下载的文件将随着卸载全部被删除 */ req.setDestinationInExternalFilesDir( this , Environment.DIRECTORY_DOWNLOADS , "xxx.mp3" ); /** * 下载的文件存放地址 SD 卡 download 文件夹,xxx.mp3 * 软件卸载后,下载的文件会保留 */ //在 SD 卡上创建一个文件夹 req.setDestinationInExternalPublicDir("/epmyg/" , "xxx.mp3" ) ; /** * 如果下载的文件希望被其他的应用共享 * 特别是那些你下载下来希望被 Media Scanner 扫描到的文件(比如音乐文件) */ req.setDestinationInExternalPublicDir( Environment.DIRECTORY_MUSIC, "xxx.mp3" ); /** * 文件将存放在外部存储的确实 download 文件内,如果无此文件夹,创建之,如果有,下面将返回 false。 * 系统有个下载文件夹,比如小米手机系统下载文件夹 SD卡 --> Download 文件夹 */ //创建目录 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdir() ; //设置文件存放路径 req.setDestinationInExternalPublicDir( Environment.DIRECTORY_DOWNLOADS , "xxx.mp3" ) ; }
指定下载的网络类型
-
指定在 WIFI 状态下,执行下载操作
req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
-
指定在 MOBILE 状态下,执行下载操作
req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE);
-
是否允许漫游状态下,执行下载操作
req.setAllowedOverRoaming(boolean);
-
是否允许 "计量式的网络连接" 执行下载操作,默认是允许的
req.setAllowedOverMetered(boolean);
定制 Notification 样式
-
设置 Notification 的标题和描述
req.setTitle("标题"); req.setDescription("描述");
-
设置 Notification 的显示,和隐藏
request.setNotificationVisibility(visibility);
参数可选的值有
值 说明 VISIBILTY_HIDDEN Notification 将不会显示,如果设置该属性的话,必须要添加权限 VISIBILITY_VISIBLE Notification 显示,但是只是在下载任务执行的过程中显示,下载完成自动消失(默认值) VISIBILITY_VISIBLE_NOTIFY_COMPLETED Notification 显示,下载进行时,和完成之后都会显示 VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION 只有当任务完成时,Notification 才会显示 如果参数为
VISIBILTY_HIDDEN
则需要添加权限android.permission.DOWNLOAD_WITHOUT_NOTIFICATION
设置下载文件类型
req.setMimeType("application/vnd.android.package-archive");
这是安卓 .apk
文件的类型
有些机型必须设置此方法,才能在下载完成后,点击通知栏的 Notification 时,才能正确的打开安装界面
不然会弹出一个 Toast(can not open file)
添加请求下载的网络链接的 http 头,比如 User-Agent,gzip 压缩等
req.addRequestHeader(String header, String value);
Query 类
可能我们不仅想要在 Notification 中显示进度就好了,还想要在 APP 中也需要获取实时下载进度
这就会用到 Query
类
但 Query
只有两个方法,因为它把数据保存在数据库中去了
我们需要获得一个 Cursor
结果集,通过结果集获得我们想要的数据
DownloadManager.Query query = new DownloadManager.Query(); Cursor cursor = downloadManager.query(query.setFilterById(id)); if (cursor != null && cursor.moveToFirst()) { // 下载的文件到本地的目录 String address = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); //已经下载的字节数 int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); // 总需下载的字节数 int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); // Notification 标题 String title =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE)); // 描述 String description =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_DESCRIPTION)); // 下载对应 id long id =cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)); //下载文件名称 String filename =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME)); // 下载文件的URL链接 String url =cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_URI)); }
这只能获取一次,数据库中的信息
如果要时时获取,可以使用 Timer
类,每隔一段时间去查询数据库即可
使用 DownloadManager 更新应用并覆盖安装
-
创建一个 空的 Android 项目
cn.twle.android.DownloadManager
-
我们要更新的版本地址为 /static/i/android/downloadmanager_v2.0.apk
如果你改了包名等则需要你自己制作一个更高的版本
-
修改
activity_main.xml
创建下载界面<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:gravity="center" android:padding="16dp" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="downloadmanager_v2.0.apk"/> <ProgressBar android:id="@+id/pb_update" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="10dp" android:layout_gravity="center_horizontal" android:max="100" android:progress="0" /> <TextView android:id="@+id/progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:text="0%"/> <Button android:id="@+id/down" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="立即更新"/> </LinearLayout>
-
修改
AndroidManifest.xml
添加权限<uses-permission android:name="android.permission.INTERNET" />; <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>;
-
修改
AndroidManifest.xml
在<application>...</application>
之间注册一个Provider
<provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider>
-
然后在
res
下新建资源目录xml
,在res/xml
下新建文件provider_paths.xml
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
-
修改
MainActivity.java
package cn.twle.android.downloadmanager; import android.app.Activity; import android.app.DownloadManager; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.support.v4.content.FileProvider; import android.util.Log; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; import java.io.File; import java.util.Timer; import java.util.TimerTask; public class MainActivity extends Activity implements View.OnClickListener { private TextView down; private TextView progress; private ProgressBar pb_update; private DownloadManager downloadManager; private DownloadManager.Request request; public static String filename = "downloadmanager_v2.0.apk"; public static String downloadUrl = "http://192.168.0.105:4000/static/i/android/"; Timer timer; long id; TimerTask task; Handler handler =new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); Bundle bundle = msg.getData(); int pro = bundle.getInt("pro"); pb_update.setProgress(pro); progress.setText(String.valueOf(pro)+"%"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); down = (TextView) findViewById(R.id.down); down.setOnClickListener(this); progress = (TextView) findViewById(R.id.progress); pb_update = (ProgressBar) findViewById(R.id.pb_update); pb_update.setMax(100); downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE); request = new DownloadManager.Request(Uri.parse(downloadUrl + filename)); request.setTitle("简单教程"); request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI); request.setAllowedOverRoaming(false); request.setMimeType("application/vnd.android.package-archive"); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); //创建目录 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdir() ; //设置文件存放路径 request.setDestinationInExternalPublicDir( Environment.DIRECTORY_DOWNLOADS , filename) ; final DownloadManager.Query query = new DownloadManager.Query(); timer = new Timer(); task = new TimerTask() { @Override public void run() { Cursor cursor = downloadManager.query(query.setFilterById(id)); if (cursor != null && cursor.moveToFirst()) { if (cursor.getInt( cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) { pb_update.setProgress(100); install(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + filename ); task.cancel(); } String title = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE)); String address = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); int bytes_downloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)); int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)); int pro = (bytes_downloaded * 100) / bytes_total; Message msg =Message.obtain(); Bundle bundle = new Bundle(); bundle.putInt("pro",pro); bundle.putString("name",title); msg.setData(bundle); handler.sendMessage(msg); } cursor.close(); } }; timer.schedule(task, 0,1000); } @Override public void onClick(View v) { id = downloadManager.enqueue(request); task.run(); down.setClickable(false); } private void install(String path) { try { Log.d("download:",path); Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 4.0以上系统弹出安装成功打开界面 //设置intent的data和Type属性。android 7.0以上crash,改用provider if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Uri fileUri = FileProvider.getUriForFile(this, getPackageName()+".provider", new File(path)); intent.setDataAndType(fileUri, "application/vnd.android.package-archive"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); }else { intent.setDataAndType(Uri.parse("file://" + path), "application/vnd.android.package-archive"); } //跳转 startActivity(intent); }catch (Exception e){ e.printStackTrace(); } } }