android app版本升级(DownloadManager、适配6.0、7.0)
2017-06-20 18:11
726 查看
说明:
1.本文使用系统DownloadManager在通知栏更新下载进度
2.动态权限使用第三方库EasyPermissions(https://github.com/googlesamples/easypermissions)
3.下载完成的App安装适配7.0
4.提示下载框(AlertDialog)是依附于Activity(UpdateActivity)的,这样做是为了解决“进入首页后,开启自动检测升级,检测到有升级的版本就随时弹框提示用户,但此时用户可能已经在操作APP进入其他页面,怎么保证弹框可以正常弹出?”这一问题并适配各大机型
2.res下的xml文件夹中的provider_paths源码
参考:
1.https://github.com/oschina/android-app
2.http://www.jianshu.com/p/98ea7e866ffd
1.本文使用系统DownloadManager在通知栏更新下载进度
2.动态权限使用第三方库EasyPermissions(https://github.com/googlesamples/easypermissions)
3.下载完成的App安装适配7.0
4.提示下载框(AlertDialog)是依附于Activity(UpdateActivity)的,这样做是为了解决“进入首页后,开启自动检测升级,检测到有升级的版本就随时弹框提示用户,但此时用户可能已经在操作APP进入其他页面,怎么保证弹框可以正常弹出?”这一问题并适配各大机型
升级流程图
MianActivity源码
public class MainActivity extends BaseActivity implements EasyPermissions.PermissionCallbacks, CheckUpdateManager.RequestPermissions { //... private Version mVersion;//版本控制bean private static final int RC_EXTERNAL_STORAGE = 0x04;//存储权限 //... //BaseActivity方法,返回布局文件 @Override protected int getContentView() { return R.layout.activity_main_ui; } //BaseActivity方法,初始化data @Override protected void initData() { super.initData(); // 检查版本升级 checkUpdate(); } //CheckUpdateManager回调接口 CheckUpdateManager封装了网络请求 @Override public void call(Version version) { this.mVersion = version; requestExternalStorage(); } @AfterPermissionGranted(RC_EXTERNAL_STORAGE) public void requestExternalStorage() { if (EasyPermissions.hasPermissions(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { DownloadService.startService(this, mVersion.getDownloadUrl()); } else { EasyPermissions.requestPermissions(this, "", RC_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); } } @Override public void onPermissionsGranted(int requestCode, List<String> perms) { } @Override public void onPermissionsDenied(int requestCode, List<String> perms) { for (String perm : perms) { if (perm.equals(Manifest.permission.READ_EXTERNAL_STORAGE)) { //DialogHelper:Alerdialog分装类 DialogHelper.getConfirmDialog(this, "温馨提示", "需要开启您手机的存储权限才能下载安装,是否现在开启", "去开启", "取消", true, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startActivity(new Intent(Settings.ACTION_APPLICATION_SETTINGS)); } }, null).show(); } else { //SharedPreferences中保存授权状态 Setting.updateLocationPermission(getApplicationContext(), false); } } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } private void checkUpdate() { CheckUpdateManager manager = new manager.checkUpdate(); } }
CheckUpdateManager 源码
public class CheckUpdateManager { private ProgressDialog mWaitDialog; private Context mContext; private boolean mIsShowDialog; public CheckUpdateManager(Context context, boolean showWaitingDialog) { this.mContext = context; mIsShowDialog = showWaitingDialog; if (mIsShowDialog) { mWaitDialog = DialogHelper.getProgressDialog(mContext); mWaitDialog.setMessage("正在检查中..."); mWaitDialog.setCancelable(false); mWaitDialog.setCanceledOnTouchOutside(false); } } public void checkUpdate() { if (mIsShowDialog) { mWaitDialog.show(); } OSChinaApi.checkUpdate(new TextHttpResponseHandler() { @Override public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { if (mIsShowDialog) { DialogHelper.getMessageDialog(mContext, "网络异常,无法获取新版本信息").show(); } } @Override public void onSuccess(int statusCode, Header[] headers, String responseString) { //此处省略若干代码 int curVersionCode = TDevice.getVersionCode(AppContext .getInstance().getPackageName()); //version:服务器解析后实体bean if (curVersionCode < version.getCode()) { UpdateActivity.show((Activity) mContext, version); } else { if (mIsShowDialog) { DialogHelper.getMessageDialog(mContext, "已经是新版本了").show(); } } } @Override public void onFinish() { super.onFinish(); if (mIsShowDialog) { mWaitDialog.dismiss(); } } }); } }
UpdateActivity源码
public class UpdateActivity extends BaseActivity implements View.OnClickListener, EasyPermissions.PermissionCallbacks { @Bind(R.id.tv_update_info) TextView mTextUpdateInfo; private Version mVersion; private static final int RC_EXTERNAL_STORAGE = 0x04;//存储权限 public static void show(Activity activity, Version version) { Intent intent = new Intent(activity, UpdateActivity.class); intent.putExtra("version", version); activity.startActivityForResult(intent, 0x01); } @Override protected int getContentView() { return R.layout.activity_update; } @SuppressWarnings("deprecation") @Override protected void initData() { super.initData(); setTitle(""); getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); mVersion = (Version) getIntent().getSerializableExtra("version"); mTextUpdateInfo.setText(Html.fromHtml(mVersion.getMessage())); } @OnClick({R.id.btn_update, R.id.btn_close}) @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_update: if (!TDevice.isWifiOpen()) { DialogHelper.getConfirmDialog(this, "当前非wifi环境,是否升级?", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { requestExternalStorage(); } }, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }).show(); } else { requestExternalStorage(); } break; case R.id.btn_close: finish(); break; } } @AfterPermissionGranted(RC_EXTERNAL_STORAGE) public void requestExternalStorage() { if (EasyPermissions.hasPermissions(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { AppUpgradeManager.getInstance(this, mVersion).startDown(); finish(); } else { EasyPermissions.requestPermissions(this, "需要开启对您手机的存储权限才能下载安装", RC_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); } } @Override public void onPermissionsGranted(int requestCode, List<String> perms) { } @Override public void onPermissionsDenied(int requestCode, List<String> perms) { //当权限窗口不能弹出式调用-用户勾选了不再提醒 if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { DialogHelper.getConfirmDialog(UpdateActivity.this, "温馨提示", "需要开启对您手机的存储权限才能下载安装,是否现在开启", "去开启", "取消", true, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startActivity(new Intent(Settings.ACTION_APPLICATION_SETTINGS)); } }, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }).show(); } else { finish(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); } }
AppUpgradeManager源码
public class AppUpgradeManager { private volatile static AppUpgradeManager sAppUpgradeManager; private DownloadManager downloader; private Context appContext; private NotificationClickReceiver mNotificationClickReceiver; private DownloadReceiver mDownloaderReceiver; private String apkName = AppConfig.APP_NAME; //apk下载文件的路径 private String downloadApkPath; // 服务器返回的版本信息 private Version latestVersion; public AppUpgradeManager(Context context, Version version) { appContext = context.getApplicationContext(); latestVersion = version; mDownloaderReceiver = new DownloadReceiver(); mNotificationClickReceiver = new NotificationClickReceiver(); } public static AppUpgradeManager getInstance(Context context, Version version) { if (sAppUpgradeManager == null) { synchronized (AppUpgradeManager.class) { if (sAppUpgradeManager == null) { sAppUpgradeManager = new AppUpgradeManager(context, version); } } } return sAppUpgradeManager; } public void startDown() { //确定apk下载的绝对路径 String dirPath = AppConfig.DEFAULT_SAVE_FILE_PATH_PUBLIC; //AppConfig.DEFAULT_SAVE_FILE_PATH_PUBLIC=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); dirPath = dirPath.endsWith(File.separator) ? dirPath : dirPath + File.separator; downloadApkPath = dirPath + apkName; //先检查本地是否已经有需要升级版本的安装包,如有就不需要再下载 File targetApkFile = new File(downloadApkPath); if (targetApkFile.exists()) { PackageManager pm = appContext.getPackageManager(); PackageInfo info = pm.getPackageArchiveInfo(downloadApkPath, PackageManager.GET_ACTIVITIES); if (info != null) { String versionCode = String.valueOf(info.versionCode); //比较已下载到本地的apk安装包,与服务器上apk安装包的版本号是否一致 if (String.valueOf(latestVersion.getCode()).equals(versionCode)) { installApk(); return; } } } //要检查本地是否有安装包,有则删除重新下 File apkFile = new File(downloadApkPath); if (apkFile.exists()) { apkFile.delete(); } if (downloader == null) { downloader = (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE); } //开始下载 DownloadManager.Query query = new DownloadManager.Query(); long downloadTaskId = TDevice.getDownloadTaskId(appContext); query.setFilterById(downloadTaskId); Cursor cur = downloader.query(query); // 检查下载任务是否已经存在 if (cur.moveToFirst()) { int columnIndex = cur.getColumnIndex(DownloadManager.COLUMN_STATUS); int status = cur.getInt(columnIndex); if (DownloadManager.STATUS_PENDING == status || DownloadManager.STATUS_RUNNING == status || DownloadManager.STATUS_PAUSED == status) { cur.close(); Toast.makeText(appContext, "更新任务已在后台进行中,无需重复更新", Toast.LENGTH_LONG).show(); return; } } cur.close(); DownloadManager.Request task = new DownloadManager.Request(Uri.parse(latestVersion.getDownloadUrl())); //定制Notification的样式 String title = "最新版本:" + latestVersion.getCode(); task.setTitle(title); task.setDescription("本次更新:\n1.增强系统稳定性\n2.修复已知bug"); task.setVisibleInDownloadsUi(true); //设置是否允许手机在漫游状态下下载 //task.setAllowedOverRoaming(false); //限定在WiFi下进行下载 //task.setAllowedNetworkTypes(Request.NETWORK_WIFI); task.setMimeType("application/vnd.android.package-archive"); // 在通知栏通知下载中和下载完成 // 下载完成后该Notification才会被显示 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) { // 3.0(11)以后才有该方法 //在下载过程中通知栏会一直显示该下载的Notification,在下载完成后该Notification会继续显示,直到用户点击该Notification或者消除该Notification task.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); } // 可能无法创建Download文件夹,如无sdcard情况,系统会默认将路径设置为/data/data/com.android.providers.downloads/cache/xxx.apk if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { task.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkName); } // 自定义文件路径 //task.setDestinationUri() downloadTaskId = downloader.enqueue(task); //TDevice SharedPreferences封装类 TDevice.saveDownloadTaskId(appContext, downloadTaskId); //注册下载完成广播 appContext.registerReceiver(mDownloaderReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); appContext.registerReceiver(mNotificationClickReceiver, new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED)); } private void installApk() { if (TextUtils.isEmpty(downloadApkPath)) { Toast.makeText(appContext, "APP安装文件不存在或已损坏", Toast.LENGTH_LONG).show(); return; } File apkFile = new File(Uri.parse(downloadApkPath).getPath()); if (!apkFile.exists()) { Toast.makeText(appContext, "APP安装文件不存在或已损坏", Toast.LENGTH_LONG).show(); return; } Intent intent = new Intent(Intent.ACTION_VIEW); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(appContext, "net.xxx.app.provider", apkFile); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } appContext.startActivity(intent); } /** * 下载完成的广播 */ class DownloadReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (downloader == null) { return; } long completeId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0); long downloadTaskId = TDevice.getDownloadTaskId(context); if (completeId != downloadTaskId) { return; } DownloadManager.Query query = new DownloadManager.Query(); query.setFilterById(downloadTaskId); Cursor cur = downloader.query(query); if (!cur.moveToFirst()) { return; } int columnIndex = cur.getColumnIndex(DownloadManager.COLUMN_STATUS); if (DownloadManager.STATUS_SUCCESSFUL == cur.getInt(columnIndex)) { installApk(); } else { Toast.makeText(appContext, "下载App最新版本失败!", Toast.LENGTH_LONG).show(); } // 下载任务已经完成,清除 TDevice.removeDownloadTaskId(context); cur.close(); } } /** * 点击通知栏下载项目,下载完成前点击都会进来,下载完成后点击不会进来。 */ public class NotificationClickReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { long[] completeIds = intent.getLongArrayExtra( DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS); //正在下载的任务ID long downloadTaskId = TDevice.getDownloadTaskId(context); if (completeIds == null || completeIds.length <= 0) { openDownloadsPage(appContext); return; } for (long completeId : completeIds) { if (completeId == downloadTaskId) { openDownloadsPage(appContext); break; } } } /** * Open the Activity which shows a list of all downloads. * * @param context 上下文 */ private void openDownloadsPage(Context context) { Intent pageView = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); pageView.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(pageView); } } }
Android7.0适配
1.在manifest文件application标签中中加入<provider android:name="android.support.v4.content.FileProvider" android:authorities="net.xxx.app.provider" /**注意参数统一性 FileProvider.getUriForFile(appContext, "net.xxx.app.provider", apkFile);*/ android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider>
2.res下的xml文件夹中的provider_paths源码
<?xml version="1.0" encoding="utf-8"?> <paths> <!--path:需要临时授权访问的路径(.代表所有路径) name:就是你给这个访问路径起个名字--> <external-path name="external_files" path="." /> </paths>
参考:
1.https://github.com/oschina/android-app
2.http://www.jianshu.com/p/98ea7e866ffd
相关文章推荐
- Android调用系统相机、相册功能,适配6.0权限获取以及7.0以后获取URI(兼容多版本)
- 项目android 6.0,7.0 版本适配问题
- Android apk升级 兼容6.0 7.0 低版本 华为手机
- has text relocations 6.0 M android 环信 so 文件 sdk 版本过低 适配Android6.0
- Android N版本(7.0)适配
- Android 升级apk 兼容6.0 7.0
- Android 6.0 和 7.0 储存空间适配小结
- Android实现APP版本升级
- app在android 6.0或以上平台版本运行过程中请求权限
- Android实战之app版本更新升级全文章(三)
- Android 7.0版本升级解析包错误
- android app 更新下载安装 适配android 7.0
- android app版本更新升级
- Android 实现app的版本升级(迭代)
- Android调用系统 拍照 相册 适配所有版本 7.0 恢复自动旋转
- 开源 Android App 增量更新库 版本升级
- ionic APP 版本升级策略(IOS&ANDROID)
- Android实战之app版本更新升级全文章(二)
- android版本更新适配7.0,解决7.0手机无法更新安装apk
- 史前巨作之 Android 5.0 6.0 7.0 版本 调用相机闪退的完美解决方案