app在线更新是一个比较常见需求,新版本发布时,用户进入咱们的app,就会弹出更新提示框,第一时间更新新版本app。在线更新分为如下几个步骤:java
1, 经过接口获取线上版本号,versionCode
2, 比较线上的versionCode 和本地的versionCode,弹出更新窗口
3, 下载APK文件(文件下载)
4,安装APK
复制代码
在线更新就上面几个步骤,前2步比较简单,重要的就是后2个步骤,而因为Android 各个版本对权限和隐私的收归和保护,所以,会出现各类的适配问题,所以本文就总结一下app 在线更新方法和遇到的一些适配问题。android
apk下载其实就是文件下载,而文件下载有不少方式:api
1,不少三方框架都有文件上传下载功能,能够借助三方框架(好比Volley,OkHttp)
2,也能够开启一个线程去下载,(能够用IntentService)
3,最简单的一种方式:Android SDK 其实给咱们提供了下载类DownloadManager,只须要简单的配置项设置,就能轻松实现下载功能。
复制代码
本文就用第三种方式,用 DownloadManager
来下载apk。app
1. 使用DownloadManager
下载apk框架
DownloadManager 是SDK 自带的,大概流程以下:ide
(1)建立一个Request,进行简单的配置(下载地址,和文件保存地址等) (2)下载完成后,系统会发送一个下载完成的广播,咱们须要监听广播。 (3)监听到下载完成的广播后,根据id查找下载的apk文件 (4)在代码中执行apk安装。ui
public void downloadApk(String apkUrl, String title, String desc) {
// fix bug : 装不了新版本,在下载以前应该删除已有文件
File apkFile = new File(weakReference.get().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "test.apk");
if (apkFile != null && apkFile.exists()) {
apkFile.delete();
}
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
//设置title
request.setTitle(title);
// 设置描述
request.setDescription(desc);
// 完成后显示通知栏
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalFilesDir(weakReference.get(), Environment.DIRECTORY_DOWNLOADS, "test.apk");
//在手机SD卡上建立一个download文件夹
// Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdir() ;
//指定下载到SD卡的/download/my/目录下
// request.setDestinationInExternalPublicDir("/codoon/","test.apk");
request.setMimeType("application/vnd.android.package-archive");
//记住reqId
mReqId = mDownloadManager.enqueue(request);
}
复制代码
如上代码所示,首先构建一个Request,设置下载地址,标题、描述、apk存放目录等,最后,调用mDownloadManager.enqueue(request)
开始下载。this
注意:这里咱们须要记住这个
mReqId
,由于下载完成以后,咱们须要根据这个ID 去查找apk文件,而后安装apk.url
2.更新下载进度spa
下载文件,咱们通常须要知道下载的进度,在界面给用户一个友好的提示,app 更新也是同样,咱们须要在界面上显示当前下载进度和总进度,让用户知道大概会等待多久。那么若是获取下载进度呢?
在下载以前,咱们须要在Activity 中注册一个Observer,就是一个观察者,当下载进度变化的时候,就会通知观察者,从而更新进度。步骤以下:
1, 首先咱们先定义一个观察者DownloadChangeObserver来观察下载进度
2,在DownloadChangeObserver 中更新UI进度,给用户提示
3,下载以前,在Activity 中注册Observer
复制代码
具体代码以下:
DownloadChangeObserver.class
:
class DownloadChangeObserver extends ContentObserver {
/** * Creates a content observer. * * @param handler The handler to run {@link #onChange} on, or null if none. */
public DownloadChangeObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
updateView();
}
}
复制代码
在updateView()
方法中,查询下载进度。
private void updateView() {
int[] bytesAndStatus = new int[]{0, 0, 0};
DownloadManager.Query query = new DownloadManager.Query().setFilterById(mReqId);
Cursor c = null;
try {
c = mDownloadManager.query(query);
if (c != null && c.moveToFirst()) {
//已经下载的字节数
bytesAndStatus[0] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
//总需下载的字节数
bytesAndStatus[1] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
//状态所在的列索引
bytesAndStatus[2] = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
}
} finally {
if (c != null) {
c.close();
}
}
if (mUpdateListener != null) {
mUpdateListener.update(bytesAndStatus[0], bytesAndStatus[1]);
}
Log.i(TAG, "下载进度:" + bytesAndStatus[0] + "/" + bytesAndStatus[1] + "");
}
复制代码
根据前面咱们记录的ID去查询进度,代码中已经注释了,再也不多讲。
要想获取到进度,在下载以前,还得先注册DownloadChangeObserver,代码以下:
weakReference.get().getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), true,
mDownLoadChangeObserver);
复制代码
3. 获取下载结果
DownloadManager
在下载完成以后,会发送一个下载完成的广播DownloadManager.ACTION_DOWNLOAD_COMPLETE
,咱们只须要监听这个广播,收到广播后, 获取apk文件安装。
定义一个广播DownloadReceiver
。
class DownloadReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
// 安装APK
long completeDownLoadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
Logger.e(TAG, "收到广播");
Uri uri;
Intent intentInstall = new Intent();
intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentInstall.setAction(Intent.ACTION_VIEW);
if (completeDownLoadId == mReqId) {
uri = mDownloadManager.getUriForDownloadedFile(completeDownLoadId);
}
intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
context.startActivity(intentInstall);
}
}
复制代码
在下载以前注册广播
// 注册广播,监听APK是否下载完成
weakReference.get().registerReceiver(mDownloadReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
复制代码
经过上面的几个步骤,基本上就完成app在线更新功能,在Android 6.0如下能够正常运行。可是别忙,本文尚未结束,Android每个版本都有一些改动,致使咱们须要适配不一样的版本,否则的话,就会出问题,结下来就看一下Android 6.0,7.0,8.0 的相关适配。
经过前面讲的几个步骤,app 在线更新在6.0如下已经能够正常运行,在Android6.0上,安装的时候会报出如下错误:
Caused by:
5 android.content.ActivityNotFoundException:No Activity found to handle Intent { act=android.intent.action.VIEW typ=application/vnd.android.package-archive flg=0x10000000 }
复制代码
为何会报上面的错误,通过debug发现,在Android6.0如下和Android6.0上,经过DownloadManager 获取到的Uri不同。
区别以下:(1)Android 6.0,getUriForDownloadedFile获得 值为: content://downloads/my_downloads/10 (2) Android6.0如下,getUriForDownloadedFile获得的值为:file:///storage/emulated/0/Android/data/packgeName/files/Download/xxx.apk
能够看到,Android6.0获得的apk地址为:content:// 开头的一个地址,安装的时候就会报上面的错误。怎么解决呢?通过查找资料找到了解决办法:
//经过downLoadId查询下载的apk,解决6.0之后安装的问题
public static File queryDownloadedApk(Context context, long downloadId) {
File targetApkFile = null;
DownloadManager downloader = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (downloadId != -1) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cur = downloader.query(query);
if (cur != null) {
if (cur.moveToFirst()) {
String uriString = cur.getString(cur.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (!TextUtils.isEmpty(uriString)) {
targetApkFile = new File(Uri.parse(uriString).getPath());
}
}
cur.close();
}
}
return targetApkFile;
}
复制代码
代码如上所示,不经过getUriForDownloadedFile
去获取Uri,经过DownloadManager.COLUMN_LOCAL_URI
这个字段去获取apk地址。
适配Android 6.0后,安装apk 的代码以下:
/** * @param context * @param intent */
private void installApk(Context context, Intent intent) {
long completeDownLoadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
Logger.e(TAG, "收到广播");
Uri uri;
Intent intentInstall = new Intent();
intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentInstall.setAction(Intent.ACTION_VIEW);
if (completeDownLoadId == mReqId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 6.0如下
uri = mDownloadManager.getUriForDownloadedFile(completeDownLoadId);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { // 6.0 - 7.0
File apkFile = queryDownloadedApk(context, completeDownLoadId);
uri = Uri.fromFile(apkFile);
}
// 安装应用
Logger.e("zhouwei", "下载完成了");
intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
context.startActivity(intentInstall);
}
}
复制代码
刚适配完6.0,在7.0以上的机子上又出问题了,为何呢?由于在Android 7.0上,对文件的访问权限做出了修改,不能在使用file://
格式的Uri 访问文件 ,Android 7.0提供 FileProvider,应该使用这个来获取apk地址,而后安装apk。以下进行简单的适配:
(1) 在res 目录下,新建一个xml
文件夹,在xml
下面建立一个文件provider_paths
文件:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="" />
<external-files-path
name="Download"
path="" />
</paths>
复制代码
(2) 在AndroidManifest.xml
清单文件中申明Provider:
<!-- Android 7.0 照片、APK下载保存路径-->
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="packgeName.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
复制代码
(3) Android 7.0上的文件地址获取:
uri = FileProvider.getUriForFile(context,
"packageNam.fileProvider",
new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "xxx.apk"));
复制代码
好了,就这样7.0适配工做就完成了,适配后的安装代码以下:
/** * @param context * @param intent */
private void installApk(Context context, Intent intent) {
long completeDownLoadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
Logger.e(TAG, "收到广播");
Uri uri;
Intent intentInstall = new Intent();
intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentInstall.setAction(Intent.ACTION_VIEW);
if (completeDownLoadId == mReqId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 6.0如下
uri = mDownloadManager.getUriForDownloadedFile(completeDownLoadId);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { // 6.0 - 7.0
File apkFile = queryDownloadedApk(context, completeDownLoadId);
uri = Uri.fromFile(apkFile);
} else { // Android 7.0 以上
uri = FileProvider.getUriForFile(context,
"packgeName.fileProvider",
new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "xxx.apk"));
intentInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
// 安装应用
Logger.e("zhouwei", "下载完成了");
intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
context.startActivity(intentInstall);
}
}
复制代码
注意:把上面的packageNam 换成你本身的包名,把xxx.apk 换成你本身的apk的名字。 关于更多FileProvider的东西,这儿就不展开讲了,想要了解的能够看一下鸿洋的文章:[Android 7.0 行为变动 经过FileProvider在应用间共享文件吧 ](https://blog.csdn.net/lmj623565791/article/details/72859156),讲的很清楚。
好特么累,继续适配Android 8.0, 因为没有Android 8.0的手机,一直没有注意,前些天一个华为用户反馈在线更新不了新版本,具体表现就是:apk下载完成,一闪而过,没有跳转到apk安装界面。通过排查,肯定了是Android 8.0权限问题。
Android8.0以上,未知来源的应用是不能够经过代码来执行安装的(在sd卡中找找到apk,手动安装是能够的),未知应用安装权限的开关被除掉,取而代之的是未知来源应用的管理列表,须要列表里面开启你的应用的未知来源的安装权限。Google这么作是为了防止一开始正经的应用后来开始经过升级来作一些不合法的事情,侵犯用户权益。
知道问题了,咱们就适配吧:
(1) 在清单文件中申明权限:REQUEST_INSTALL_PACKAGES
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
复制代码
(2) 在代码中判断用户是否已经受过权限了,若是已经受权,能够直接安装,若是没有受权,则跳转到受权列表,让用户开启未知来源应用安装权限,开启后,再安装应用。
在监听apk下载状态的广播中添加以下代码:
boolean haveInstallPermission;
// 兼容Android 8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//先获取是否有安装未知来源应用的权限
haveInstallPermission = context.getPackageManager().canRequestPackageInstalls();
if (!haveInstallPermission) {//没有权限
// 弹窗,并去设置页面受权
final AndroidOInstallPermissionListener listener = new AndroidOInstallPermissionListener() {
@Override
public void permissionSuccess() {
installApk(context, intent);
}
@Override
public void permissionFail() {
ToastUtils.shortToast(context, "受权失败,没法安装应用");
}
};
AndroidOPermissionActivity.sListener = listener;
Intent intent1 = new Intent(context, AndroidOPermissionActivity.class);
context.startActivity(intent1);
} else {
installApk(context, intent);
}
} else {
installApk(context, intent);
}
复制代码
由于受权时须要弹框提示,咱们用一个Activity来代理建立了一个Activity:AndroidOPermissionActivity
来申请权限,用户点击设置后,跳转到权限设置界面,而后咱们再onActivityResult
里判断是都受权成功。
AndroidOPermissionActivity
代码以下:
/** * 兼容Android 8。0 APP 在线更新,权限申请界面 * Created by zhouwei on 2018/3/23. */
public class AndroidOPermissionActivity extends BaseActivity {
public static final int INSTALL_PACKAGES_REQUESTCODE = 1;
private AlertDialog mAlertDialog;
public static AppDownloadManager.AndroidOInstallPermissionListener sListener;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 弹窗
if (Build.VERSION.SDK_INT >= 26) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, INSTALL_PACKAGES_REQUESTCODE);
} else {
finish();
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case INSTALL_PACKAGES_REQUESTCODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (sListener != null) {
sListener.permissionSuccess();
finish();
}
} else {
//startInstallPermissionSettingActivity();
showDialog();
}
break;
}
}
private void showDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.app_name);
builder.setMessage("为了正常升级 xxx APP,请点击设置按钮,容许安装未知来源应用,本功能只限用于 xxx APP版本升级");
builder.setPositiveButton("设置", new DialogInterface.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onClick(DialogInterface dialogInterface, int i) {
startInstallPermissionSettingActivity();
mAlertDialog.dismiss();
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if (sListener != null) {
sListener.permissionFail();
}
mAlertDialog.dismiss();
finish();
}
});
mAlertDialog = builder.create();
mAlertDialog.show();
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void startInstallPermissionSettingActivity() {
//注意这个是8.0新API
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1 && resultCode == RESULT_OK) {
// 受权成功
if (sListener != null) {
sListener.permissionSuccess();
}
} else {
// 受权失败
if (sListener != null) {
sListener.permissionFail();
}
}
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
sListener = null;
}
}
复制代码
注意:当经过Intent 跳转到未知应用受权列表的时候,必定要加上包名,这样就能直接跳转到你的app下,否则只能跳转到列表。
![]()
好了,这样Android 8.0 上也能够在线更新了。
AppDownloadManager
为了避免依赖于某个Activity ,所以封装了一个AppDownloadManager
,少许几行代码就能够实如今线更新,给出完整代码:
public class AppDownloadManager {
public static final String TAG = "AppDownloadManager";
private WeakReference<Activity> weakReference;
private DownloadManager mDownloadManager;
private DownloadChangeObserver mDownLoadChangeObserver;
private DownloadReceiver mDownloadReceiver;
private long mReqId;
private OnUpdateListener mUpdateListener;
public AppDownloadManager(Activity activity) {
weakReference = new WeakReference<Activity>(activity);
mDownloadManager = (DownloadManager) weakReference.get().getSystemService(Context.DOWNLOAD_SERVICE);
mDownLoadChangeObserver = new DownloadChangeObserver(new Handler());
mDownloadReceiver = new DownloadReceiver();
}
public void setUpdateListener(OnUpdateListener mUpdateListener) {
this.mUpdateListener = mUpdateListener;
}
public void downloadApk(String apkUrl, String title, String desc) {
// fix bug : 装不了新版本,在下载以前应该删除已有文件
File apkFile = new File(weakReference.get().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app_name.apk");
if (apkFile != null && apkFile.exists()) {
apkFile.delete();
}
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
//设置title
request.setTitle(title);
// 设置描述
request.setDescription(desc);
// 完成后显示通知栏
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalFilesDir(weakReference.get(), Environment.DIRECTORY_DOWNLOADS, "app_name.apk");
//在手机SD卡上建立一个download文件夹
// Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).mkdir() ;
//指定下载到SD卡的/download/my/目录下
// request.setDestinationInExternalPublicDir("/codoon/","codoon_health.apk");
request.setMimeType("application/vnd.android.package-archive");
//
mReqId = mDownloadManager.enqueue(request);
}
/** * 取消下载 */
public void cancel() {
mDownloadManager.remove(mReqId);
}
/** * 对应 {@link Activity } */
public void resume() {
//设置监听Uri.parse("content://downloads/my_downloads")
weakReference.get().getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), true,
mDownLoadChangeObserver);
// 注册广播,监听APK是否下载完成
weakReference.get().registerReceiver(mDownloadReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
/** * 对应{@link Activity#onPause()} ()} */
public void onPause() {
weakReference.get().getContentResolver().unregisterContentObserver(mDownLoadChangeObserver);
weakReference.get().unregisterReceiver(mDownloadReceiver);
}
private void updateView() {
int[] bytesAndStatus = new int[]{0, 0, 0};
DownloadManager.Query query = new DownloadManager.Query().setFilterById(mReqId);
Cursor c = null;
try {
c = mDownloadManager.query(query);
if (c != null && c.moveToFirst()) {
//已经下载的字节数
bytesAndStatus[0] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
//总需下载的字节数
bytesAndStatus[1] = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
//状态所在的列索引
bytesAndStatus[2] = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
}
} finally {
if (c != null) {
c.close();
}
}
if (mUpdateListener != null) {
mUpdateListener.update(bytesAndStatus[0], bytesAndStatus[1]);
}
Log.i(TAG, "下载进度:" + bytesAndStatus[0] + "/" + bytesAndStatus[1] + "");
}
class DownloadChangeObserver extends ContentObserver {
/** * Creates a content observer. * * @param handler The handler to run {@link #onChange} on, or null if none. */
public DownloadChangeObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
updateView();
}
}
class DownloadReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
boolean haveInstallPermission;
// 兼容Android 8.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//先获取是否有安装未知来源应用的权限
haveInstallPermission = context.getPackageManager().canRequestPackageInstalls();
if (!haveInstallPermission) {//没有权限
// 弹窗,并去设置页面受权
final AndroidOInstallPermissionListener listener = new AndroidOInstallPermissionListener() {
@Override
public void permissionSuccess() {
installApk(context, intent);
}
@Override
public void permissionFail() {
ToastUtils.shortToast(context, "受权失败,没法安装应用");
}
};
AndroidOPermissionActivity.sListener = listener;
Intent intent1 = new Intent(context, AndroidOPermissionActivity.class);
context.startActivity(intent1);
} else {
installApk(context, intent);
}
} else {
installApk(context, intent);
}
}
}
/** * @param context * @param intent */
private void installApk(Context context, Intent intent) {
long completeDownLoadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
Logger.e(TAG, "收到广播");
Uri uri;
Intent intentInstall = new Intent();
intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentInstall.setAction(Intent.ACTION_VIEW);
if (completeDownLoadId == mReqId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { // 6.0如下
uri = mDownloadManager.getUriForDownloadedFile(completeDownLoadId);
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { // 6.0 - 7.0
File apkFile = queryDownloadedApk(context, completeDownLoadId);
uri = Uri.fromFile(apkFile);
} else { // Android 7.0 以上
uri = FileProvider.getUriForFile(context,
"package_name.fileProvider",
new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app_name.apk"));
intentInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
// 安装应用
Logger.e("zhouwei", "下载完成了");
intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
context.startActivity(intentInstall);
}
}
//经过downLoadId查询下载的apk,解决6.0之后安装的问题
public static File queryDownloadedApk(Context context, long downloadId) {
File targetApkFile = null;
DownloadManager downloader = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (downloadId != -1) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cur = downloader.query(query);
if (cur != null) {
if (cur.moveToFirst()) {
String uriString = cur.getString(cur.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (!TextUtils.isEmpty(uriString)) {
targetApkFile = new File(Uri.parse(uriString).getPath());
}
}
cur.close();
}
}
return targetApkFile;
}
public interface OnUpdateListener {
void update(int currentByte, int totalByte);
}
public interface AndroidOInstallPermissionListener {
void permissionSuccess();
void permissionFail();
}
}
复制代码
使用就很简单了,以下:
(1) 弹出更新提示框:提示用户更新
private void showUpdateDialog(final AppUpdateInfo updateInfo) {
AppUpdateDialog dialog = new AppUpdateDialog(getContext());
dialog.setAppUpdateInfo(updateInfo);
dialog.setOnUpdateClickListener(new AppUpdateDialog.OnUpdateClickListener() {
@Override
public void update(final AppUpdateDialog updateDialog) {
String title = "app name";
String desc = "版本更新";
mDownloadManager.setUpdateListener(new AppDownloadManager.OnUpdateListener() {
@Override
public void update(int currentByte, int totalByte) {
updateDialog.setProgress(currentByte, totalByte);
if ((currentByte == totalByte) && totalByte != 0) {
updateDialog.dismiss();
}
}
});
mDownloadManager.downloadApk(updateInfo.download_url, title, desc);
}
});
dialog.setCanceledOnTouchOutside(false);
dialog.setCancelable(false);
dialog.show();
}
复制代码
(2) 注意在 onResume
和 onPause 调用对应方法:
@Override
public void onResume() {
super.onResume();
if (mDownloadManager != null) {
mDownloadManager.resume();
}
}
复制代码
@Override
public void onPause() {
super.onPause();
if (mDownloadManager != null) {
mDownloadManager.onPause();
}
}
复制代码
本文总结了项目中app在线更新遇到的一些适配问题,关于Android 6.0 的适配,若是你没有使用DownloadManager,可能不会遇到这个问题。7.0 和 8.0 的适配无论用哪一种方式,都会有。
关于app在线更新版本适配就此结束,若是有啥问题,欢迎指出。
更多Android干货文章,关注公众号 【Android技术杂货铺】