说明:
1.本文使用系统DownloadManager在通知栏更新下载进度
2.动态权限使用第三方库EasyPermissions(https://github.com/googlesamples/easypermissions)
3.下载完成的App安装适配7.0
4.提示下载框(AlertDialog)是依附于Activity(UpdateActivity)的,这样做是为了解决“进入首页后,开启自动检测升级,检测到有升级的版本就随时弹框提示用户,但此时用户可能已经在操作APP进入其他页面,怎么保证弹框可以正常弹出?”这一问题并适配各大机型
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 perms) {
}
@Override
public void onPermissionsDenied(int requestCode, List 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();
}
}
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();
}
}
});
}
}
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 perms) {
}
@Override
public void onPermissionsDenied(int requestCode, List 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);
}
}
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);
}
}
}
1.在manifest文件application标签中中加入
"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"> "android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> 2.res下的xml文件夹中的provider_paths源码
<paths> <external-path name="external_files" path="." /> paths>
参考:
1.https://github.com/oschina/android-app
2.http://www.jianshu.com/p/98ea7e866ffd