目录
[TOC]
个人认为热更新虽然是好 但是 像ios直接拒绝使用热更新 也不无道理 本人一直使用传统的版本升级方式 请求接口 返回下载链接 然后下载文件 用Intent吊起安装 android7.0之前是没有问题的 也没有遇到三星note系列的问题(测试机是三星note4 5.0的机器好像。。。) 后来使用note系列的更新版本都有问题 这里分享给大家 避免大家踩坑
- 使用系统自带的Api(DownloadManager)
- 请求服务器接口然后配置一些相关的参数(网上百度有很多关于这个Api的介绍)
- 设置File路径(这里坑坑的 基本上主要的问题都在这)
- 判断版本>=7.0必须自定义FileProvider(网上解释好像说7.0为了什么什么安全 具体没有了解)
- <7.0正常使用Intent安装即可(这里的坑就是三星note系列 其他的比如vivo,魅族,华为,非三星note系 列,小米。。没有遇到此类问题)
代码块
/**
* 2017/3/9
* 版本更新下载类
*/
public class DownloadController {
private DownloadManager mDownManager;
private Context mCtx;
private String substr;
private static DownloadController instance;
String path_apk = "";
private List<Long> mIdList = new ArrayList<Long>();
private DownloadController(Context context) {
this.mCtx = context;
mDownManager = (DownloadManager) mCtx.getSystemService(Context.DOWNLOAD_SERVICE);
}
public static DownloadController getInstance(Context context) {
if (instance == null) {
instance = new DownloadController(context);
}
return instance;
}
public long startLauncherDownLoader(String url, long downId) {
if (TextUtils.isEmpty(url)) {
return -1;
}
if (mIdList.contains(downId)) {
Toast.makeText(mCtx, "正在下载...", Toast.LENGTH_SHORT).show();
return downId;
}
Toast.makeText(mCtx, "开始下载", Toast.LENGTH_SHORT).show();
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
request.setVisibleInDownloadsUi(true);
String appNmae = mCtx.getResources().getString(R.string.app_name);
request.setTitle(appNmae);
request.setDescription(appNmae + "正在下载...");
request.setAllowedOverRoaming(true);
// 设置文件存放目录
substr = url.substring(url.lastIndexOf("/") + 1);
File file = getDownloadFile();
path_apk = file.getPath() + "/" + substr;
Log.i("apkpatch", substr + "================" + path_apk);
if (file != null) {
request.setDestinationUri(Uri.fromFile(new File(file.getPath() + "/" + substr)));
} else {
request.setDestinationInExternalFilesDir(mCtx, "downloadqzxq",substr);
}
long id = mDownManager.enqueue(request);
mIdList.add(id);
return id;
}
/**
* 安装apk
*
* @param id
*/
public void installApk(Context context, Long id) {
try {
Intent install = new Intent(Intent.ACTION_VIEW);
Uri downloadFileUri = mDownManager.getUriForDownloadedFile(id);
install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(install);
} catch (Exception e) {
}
}
/**
* 安装apk
*
* @paramid
*/
public void installApkByFile(File file,Context mContext) {
try {
Intent install = new Intent(Intent.ACTION_VIEW);
//7.0系统的安装
if (Build.VERSION.SDK_INT>=24){
Uri apkUri = FileProvider.getUriForFile(mContext, "com.qizi.yqxq.fileprovider", file);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
install.setDataAndType(apkUri, "application/vnd.android.package-archive");
}
//<24的安装
else {
Uri downloadFileUri = Uri.fromFile(file);
install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
mCtx.startActivity(install);
} catch (Exception e) {
e.printStackTrace();
}
}
public File isExistFile(String url) {
substr = url.substring(url.lastIndexOf("/") + 1);
File file = getDownloadFile();
if (file != null && !TextUtils.isEmpty(url)) {
File apkFile = new File(file.getPath() + "/" + url.substring(url.lastIndexOf("/") + 1));
if (apkFile.exists()) {
return apkFile;
} }
return null;
}
public void installAPK(Context mContext) {
installApkByFile(new File(path_apk),mContext);
}
public File getDownloadFile() {
File files = new File(Environment.getExternalStorageDirectory() + "/" + "downloadqzxq");
File[] ffs = files.listFiles();
File file2 = null;
if (ffs != null) {
for (int i = 0; i < ffs.length; i++) {
if (ffs[i].getName().equals(substr)) {
file2 = new File(substr);
}
}
}
if (file2 != null) {
if (!file2.exists()) {
file2 = files;
}
} else {
file2 = files;
}
return file2;
}
}
以上是我自己的一个下载安装类 咱们先看一下startLauncherDownLoader这个方法 代码中具体的设置 自己可以百度看一下配置 网上有很多介绍 我只选择重要的去解释
File file = getDownloadFile();
这个方法就是我先去检测我的下载目录是否有我需要更新的apk包,很多人会问 为什么要去检测,这里给大家解释一下 如果正常的安装当弹出安装窗口的时候 用户点击安装就行了 但是如果用户点击取消呢 下一次进来的时候 我们还是提示用户更新 当用户点击更新按钮的时候 我们难道还要让用户去下载吗 这个是肯定不行的 如果之前用户点击取消 第二次再进app的时候点击更新按钮 我们只要吊起本地我们之前下载好的apk安装就行了 因为用户第一次已经下载了 只不过是取消了安装。。。这个解释够清楚了吧接下来看一行代码
File files = new File(Environment.getExternalStorageDirectory() + "/" + "downloadqzxq");
我们看这个 downloadqzxq 这个目录是我手动设置的一个名字(之前用的Environment.DIRECTORY_DOWNLOADS 内存自带的Downloads文件夹) 这个地方就有坑了 三星note系列的手机直接提示我没有权限 可是我明明在minefist里面加内存的读取权限 也申请运行时权限了 结果是不行 换其他的手机都可以 只要三星note系列就不行 有兴趣的可以自己试一下 。。。。(这是一个坑)
7.0的适配
- 自定义fileProvider
- 安装的时候去判断版本
<!--适配版本升级>=7.0的情况-->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<!--元数据-->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
这里的最好写包名 容易区分 (以上代码在minefest里面配置)
exported:要求必须为false,为true则会报安全异常。grantUriPermissions:true,表示授予 URI 临时访问权限
file_paths如下
<resources>
<paths>
<external-path
name="mydemo"
path="downloadqzxq" />
</paths>
</resources>
这段代码指定共享的目录(在res下面新建一个xml文件夹并且新建文件就行 这里的downloadqzxq 必须必须必须跟咱们之前设置的文件夹名字一样 否则会找不到对应的文件 进而抛出异常)
- 代表的根目录: Context.getFilesDir()
- 代表的根目录: Environment.getExternalStorageDirectory()
- 代表的根目录: getCacheDir()
public void installApkByFile(File file,Context mContext) {
try {
Intent install = new Intent(Intent.ACTION_VIEW);
//7.0系统的安装
if (Build.VERSION.SDK_INT>=24){
Uri apkUri = FileProvider.getUriForFile(mContext, "com.qizi.yqxq.fileprovider", file);
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
install.setDataAndType(apkUri, "application/vnd.android.package-archive");
}
//<24的安装
else {
Uri downloadFileUri = Uri.fromFile(file);
install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
mCtx.startActivity(install);
} catch (Exception e) {
e.printStackTrace();
}
}
这里的名字com.qizi.yqxq.fileprovider 必须有我们之前配置的名字一样
这里我们去判断版本更新就行了 下面的代码是我自己的广播
public class DownloadCompleteReceiver extends BroadcastReceiver {
DownloadController downloadController;
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
downloadController = DownloadController.getInstance(context);
downloadController.installAPK(context);
}
}
}
//这是我在main里面自己的方法 弹窗去提示版本升级 主要是点击里面的代码 拿到下载的链接 然后调用封装好的下载类去下载
public void showUpdatDialog(final Boolean isForce, String version, final String url, String desc) {
updateDialog = new VersionUpdateDialog(mContext);
if (!MainActivity.this.isFinishing()) {
flaotDialog.dismiss();
updateDialog.show(version, desc, isForce, new View.OnClickListener() {
@Override
public void onClick(View v) {
downloadController = DownloadController.getInstance(mContext);
File apkFile = downloadController.isExistFile(url);
if (apkFile != null) {
downloadController.installApkByFile(apkFile, mContext);
} else {
downloadController.startLauncherDownLoader(url, -1);
}
updateDialog.dismiss();
if (isForce) {
finish();
// exitMainApp();
}
}
}, new View.OnClickListener() {
@Override
public void onClick(View v) {
updateDialog.dismiss();
if (isForce) {
exitMainApp();
}
}
});
}
}
总结
- 7.0适配必须自定义FileProvider
- 三星note系列手机适配 不要使用系统自带的目录 为了统一我们就自定义一个目录
- 大家多多踩坑才能有收获啊 !!!!!!!!!!!!!! 大家有问题 可以留言一起交流