我在去年写过一篇博客,叫《Android实用功能分享——应用版本的更新实例》,那个时候APP限制很少,不需要手动申请文件权限和安装的权限,随着Android系统的安全性的提高,这篇博客下面的技术就显得不够用了,因为我决定更新一下技术,以适应时代发展。
众所周知,在Anroid 6.0以后,对于操作文件的权限已经变了,需要我们在应用运行时手动请求以下权限
另外,在Anroid 7.0以后,安装外部APP也需要申请权限
大方向说完以后,咱们来具体实施一下项目,首先在build.gradle文件下配置两个外部依赖
//权限申请框架
implementation 'pub.devrel:easypermissions:0.3.0'
//APP注解框架
implementation 'org.xutils:xutils:3.3.36'
然后在自定义MyAPP里面初始化下xutils的框架
/**
*@Description
* @author 小瓶盖
* @return
* @createTime 2019/9/16 0016 16:38
* 更新日志
*/
public class MyAPP extends Application {
@Override
public void onCreate() {
super.onCreate();
//网络注解框架
x.Ext.init(this);
}
}
接下来就在Activity里面编写程序
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import com.ltl.update.base.BasePermissionActivity;
import com.ltl.update.utils.UpDateStatus;
import com.ltl.update.utils.UpdateHelper;
import org.xutils.view.annotation.ContentView;
import org.xutils.view.annotation.Event;
import org.xutils.x;
import java.util.List;
@ContentView(R.layout.activity_main)
public class MainActivity extends BasePermissionActivity implements UpDateStatus {
private UpdateHelper mUpdateHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
x.view().inject(this);
mUpdateHelper = new UpdateHelper(this);
mUpdateHelper.setUpDateStatus(this);
}
@Override
public void updateNewVersion(boolean needUpdate) {
//是否强更
}
@Override
public void cancelUpdate() {
//取消更新
}
@Override
public void cancelJurisdiction() {
//取消授权
}
@Event(R.id.id_update)
private void onClick(View view) {
//申请文件权限
onRequestPermissions(file, RC_FILE, FILE_TIPS);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
boolean b = mUpdateHelper.onRequestPermissionsResult(requestCode, grantResults);
if (!b) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
/**
* 文件操作权限获取成功
* @param requestCode 申请权限时的请求码
* @param perms 申请成功的权限集合
*/
@Override
public void onPermissionsGranted(int requestCode, @NonNull List perms) {
super.onPermissionsGranted(requestCode, perms);
mUpdateHelper.getVersion();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == UpdateHelper.INSTALL_APK_REQUESTCODE) {
//打开授权设置返回的时候检查一下权限
mUpdateHelper.onActivityResult(requestCode, resultCode, data);
}
}
}
代码其实比较好懂,重点讲一下我自定义的一个版本更新工具类,代码如下
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import com.ltl.update.UpdataDialog;
import org.json.JSONObject;
import org.xutils.http.RequestParams;
import org.xutils.x;
import java.io.File;
import java.util.Map;
/**
* @author 小瓶盖
* @Description
* @return
* @createTime 2019/9/16 0016 16:38
* 更新日志
*/
public class UpdateHelper extends UpDateVersionImpl {
public static final int INSTALL_APK_REQUESTCODE = 1531;
private UpdateModel mModel;
public void setUpDateStatus(UpDateStatus upDateStatus) {
mUpDateStatus = upDateStatus;
}
private UpDateStatus mUpDateStatus;
public static boolean isLoaded = false;
public static boolean HAVE_NEW_VERSION = false;
private String path = "";
public UpdateHelper(Context mContext) {
super(mContext);
mModel = new UpdateModel();
File pathFile = new File(FileUtils.writeApkSaveList(), "update" + (AppUtils.getVersionCode(mContext) + 1) + ".apk");
path = pathFile.getPath();
}
@Override
public void downloadAPK(String url) {
RequestParams requestParams = new RequestParams(url);
requestParams.setSaveFilePath(path);
x.http().get(requestParams, new DownFile());
}
class DownFile extends DownFileImpl {
UpdataDialog progressDialog;
public DownFile() {
progressDialog = new UpdataDialog(mContext);
}
@Override
public void onStarted() {
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("亲,努力下载中。。。");
progressDialog.show();
}
@Override
public void onLoading(long total, long current, boolean isDownloading) {
if (isDownloading) {
progressDialog.setMax((int) (total / 1024f / 1024f));
progressDialog.setProgress((int) ((current / 1024f / 1024f)));
}
}
@Override
public void onSuccess(File result) {
try {
installApk(result);
} catch (Exception e) {
e.printStackTrace();
toastMsg("安装失败,请前往应用市场更新");
}
progressDialog.dismiss();
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
ex.printStackTrace();
toastMsg("下载失败,请检查网络和SD卡");
progressDialog.dismiss();
}
}
@Override
public void installApk(File file) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本
Uri apkUri = FileUtils.getUriForFile(mContext, file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
mContext.startActivity(intent);
((Activity) mContext).finish();
}
@Override
public void getVersion() {
mModel = getModel(new JSONObject());
if (mModel.getVersionNo() > getVersionCode() && !TextUtils.isEmpty(mModel.getUrl())) {
//要更新了
HAVE_NEW_VERSION = true;
judgePermissions(mModel);
}
}
@Override
public void judgePermissions(UpdateModel model) {
if (model != null) {
if (Build.VERSION.SDK_INT >= 26) {
//来判断应用是否有权限安装apk
boolean installAllowed = mActivity.getPackageManager().canRequestPackageInstalls();
//有权限
if (installAllowed) {
//直接前往下载
onVersionUpdate(model);
} else {
//无权限 申请权限
ActivityCompat.requestPermissions(mActivity, new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, INSTALL_APK_REQUESTCODE);
}
} else {
//直接前往下载
onVersionUpdate(model);
}
}
}
@Override
public void onVersionUpdate(final UpdateModel model) {
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setCancelable(false);
dialog.setTitle("温馨提示");
dialog.setMessage(model.getInstructions());
dialog.setPositiveButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if (mUpDateStatus != null) {
mUpDateStatus.cancelUpdate();
}
if (model.isForceUpdate()) {
isLoaded = false;
mActivity.finish();
System.exit(0);
}
}
});
dialog.setNeutralButton("马上更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
isLoaded = false;
downloadAPK(model.getUrl());
}
});
dialog.show();
}
@Override
public UpdateModel getModel(JSONObject object) {
//模拟数据
UpdateModel mModel = new UpdateModel();
//拼多多的下载链接
mModel.setUrl("https://imtt.dd.qq.com/16891/apk/3BB6A311C926A75A15FBCF29BC3FF2CF.apk");
mModel.setVersionNo(2);
mModel.setInstructions("新版本更新啦");
return mModel;
}
@Override
public int getVersionCode() {
return AppUtils.getVersionCode(mContext);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == INSTALL_APK_REQUESTCODE) {
judgePermissions(mModel);
}
}
@Override
public boolean onRequestPermissionsResult(int requestCode, @NonNull int[] grantResults) {
if (requestCode == INSTALL_APK_REQUESTCODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
onVersionUpdate(mModel);
} else {
//将用户引导至安装未知应用界面。
showInstallApkTips();
}
return true;
}
return false;
}
/**
*没有权限需要提示更新
*/
public void showInstallApkTips() {
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
dialog.setCancelable(false);
dialog.setTitle("温馨提示");
dialog.setMessage("有新的版本要更新,请打开允许安装外部应用权限后重试");
dialog.setPositiveButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if (mUpDateStatus != null) {
mUpDateStatus.cancelJurisdiction();
}
if (mModel.isForceUpdate()) {
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
Uri content_url = Uri.parse(mModel.getUrl());
intent.setData(content_url);
mActivity.startActivity(intent);
isLoaded = false;
mActivity.finish();
System.exit(0);
}
}
});
dialog.setNeutralButton("前往设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
isLoaded = false;
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
intent.setData(Uri.parse("package:" + mContext.getPackageName()));
mActivity.startActivityForResult(intent, INSTALL_APK_REQUESTCODE);
}
});
dialog.show();
}
}
具体的,代码的注释已经很清楚,主要是权限申请和处理,这里面每一步都至关重要,其中 installApk(File file) 方法,是调起系统安装APP的方法,到此,基本上就完成操作了。如果没有权限,调起是会报严重错误的。
public void installApk(File file) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0+以上版本
Uri apkUri = FileUtils.getUriForFile(mContext, file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
mContext.startActivity(intent);
((Activity) mContext).finish();
}
以上就是部分代码,有些细节的接口类会在下面的源码中贴出,欢迎下载源码体验。