这是一个使用DownloadManager在三星手机引发的血案。
定义:
DownloadManager是从API 9开始加入的,用来处理http长连接的一个系统服务,我们可以使用它请求一个URI来下载到一个目标文件。DownloadManager会把下载放在后台执行,并且会在http连接失败后一旦网络恢复或者系统重启时自动尝试重新下载。我们可以通过contex.getSystemService(DOWNLOAD_SERVICE)获得一个DownloadManager实例。
场景:
每个app里面都会用到下载功能,谷歌也考虑到了这个问题,所以给我们提供了DownloadManager这个下载器来进行下载管理。下面是我用到的主要跟下载有关的代码(完整代码网上很多这里不提供了,主要说说遇到的问题):
public static void installUpdateApk(final String url,
final String fileName, final String description) {
new Thread() {
public void run() {
Looper.prepare();
try {
download(url, fileName, description);
} catch (Exception e) {
APK_DOWNLOADING = false;
e.printStackTrace();
}
}
@SuppressWarnings("deprecation")
@SuppressLint({ "InlinedApi", "NewApi" })
private void download(final String url, String fileName,
String description) {
File d = new File(AppConfig.DOWNLOAD_DIR);
if (!d.exists()) {
d.mkdirs();
}
File f = new File(AppConfig.DOWNLOAD_DIR + fileName);
if (f.exists()) {
f.delete();
}
APK_DOWNLOADING = true;
if (mContext == null) {
APK_DOWNLOADING = false;
return;
}
String downloadUrl = url;
if (downloadUrl == null) {
APK_DOWNLOADING = false;
Toast.makeText(mContext, "下载失败!", Toast.LENGTH_LONG).show();
return;
}
// check if support download manager
if (!isDownloadManagerAvailable(mContext)) {
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
Uri content_url = Uri.parse(downloadUrl);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(content_url);
mContext.startActivity(intent);
APK_DOWNLOADING = false;
return;
}
manager = (DownloadManager) mContext
.getSystemService(Context.DOWNLOAD_SERVICE);
// 创建下载请求
DownloadManager.Request down = new DownloadManager.Request(
Uri.parse(downloadUrl));
// 设置允许使用的网络类型,这里是移动网络和wifi都可以
// down.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
down.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE
| DownloadManager.Request.NETWORK_WIFI);
down.setMimeType("application/vnd.android.package-archive");
down.setDescription(description);
// 禁止发出通知,既后台下载
down.setShowRunningNotification(true);
// 不显示下载界面
down.setVisibleInDownloadsUi(true);
down.setDestinationInExternalPublicDir(
AppConfig.PROJECT_FOLDER_NAME + "/download",
fileName);
// 将下载请求放入队列
manager.enqueue(down);
}
}.start();
}
public static boolean isDownloadManagerAvailable(Context context) {
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
遇到的问题:
既然知道是从api 9 后才加入的所以用isDownloadManagerAvailable()
方法做下判断如果是低版本则采用浏览器的方式下载。一般这么做判断后大部分手机是没有问题的。可是实际使用总会带给我们惊喜,某一天一个同事拿着一个三星手机来告诉我下载不好用了,无奈经过调试后发现报如标题所示的错误了,最后经过查找才发现原来它手机里面装了手机卫士,系统自带的下载管理器被禁用了(这里我就**了)!
解决方案:
还好办法总会多过问题,为了防止其他手机会出现类似的问题,这里采用的是判断系统的下载器是否可用进而选择下载方式。通过context.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads")
这个方法我们能得到系统反馈的信息有:0、1、2、3、4。 通过查找源码具体如下:
在PackageManager类里:
/**
* Flag for {@link #setApplicationEnabledSetting(String, int, int)}
* and {@link #setComponentEnabledSetting(ComponentName, int, int)}: This
* component or application is in its default enabled state (as specified
* in its manifest).
*/
public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0;
/**
* Flag for {@link #setApplicationEnabledSetting(String, int, int)}
* and {@link #setComponentEnabledSetting(ComponentName, int, int)}: This
* component or application has been explictily enabled, regardless of
* what it has specified in its manifest.
*/
public static final int COMPONENT_ENABLED_STATE_ENABLED = 1;
/**
* Flag for {@link #setApplicationEnabledSetting(String, int, int)}
* and {@link #setComponentEnabledSetting(ComponentName, int, int)}: This
* component or application has been explicitly disabled, regardless of
* what it has specified in its manifest.
*/
public static final int COMPONENT_ENABLED_STATE_DISABLED = 2;
/**
* Flag for {@link #setApplicationEnabledSetting(String, int, int)} only: The
* user has explicitly disabled the application, regardless of what it has
* specified in its manifest. Because this is due to the user's request,
* they may re-enable it if desired through the appropriate system UI. This
* option currently <strong>cannot</strong> be used with
* {@link #setComponentEnabledSetting(ComponentName, int, int)}.
*/
public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3;
/**
* Flag for {@link #setApplicationEnabledSetting(String, int, int)} only: This
* application should be considered, until the point where the user actually
* wants to use it. This means that it will not normally show up to the user
* (such as in the launcher), but various parts of the user interface can
* use {@link #GET_DISABLED_UNTIL_USED_COMPONENTS} to still see it and allow
* the user to select it (as for example an IME, device admin, etc). Such code,
* once the user has selected the app, should at that point also make it enabled.
* This option currently <strong>can not</strong> be used with
* {@link #setComponentEnabledSetting(ComponentName, int, int)}.
*/
public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4;
这里简单说下 0:只要在AndroidManifest里面开启了网络访问权限则为可用状态。
1:不管是否在AndroidManifest开启了权限都为可用状态。
2:不管是否在AndroidManifest开启了权限都为不可用状态。
3:用户禁止使用状态。
4:用户不选择允许使用之前都不可用状态。
由此可以看出只有状态为0 或者1 的时候我们的app才能正常使用DownloadManager,这样我们就可以做判断了,只需要改下isDownloadManagerAvailable
方法 如下:
public static boolean isDownloadManagerAvailable(Context context) {
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD
|| context.getPackageManager()
.getApplicationEnabledSetting(
"com.android.providers.downloads") == context
.getPackageManager().COMPONENT_ENABLED_STATE_DISABLED_USER
|| context.getPackageManager()
.getApplicationEnabledSetting(
"com.android.providers.downloads") == context
.getPackageManager().COMPONENT_ENABLED_STATE_DISABLED
|| context.getPackageManager()
.getApplicationEnabledSetting(
"com.android.providers.downloads") == context
.getPackageManager().COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED ) {
return false;
}
return true;
} catch (Exception e) {
return false;
}
}
好了以上就是本人在使用DownloadManager的时候遇到的问题,特此在这里做下记录。 (-_-) !