android7.0后文件的访问权限提高了,不能直接使用file://的方式来共享文件了,应该使用
content://URI的方式来共享文件,并使用FileProvider类来授权。
先在AndroidManifest.xml添加权限
首先新建在xml文件夹下新建一个文件,名字随意,我这边新建为file_paths.xml。里面这样写道
如果我下载的文件放在getFilesDir里,就用cache-path就行。 其中 name="cache"的cache是别名,可以随便自定义的,而path="/" 里的/则表示 允许访问getFilesDir下所有文件。这上面基本所有路径访问全加上去,反正也没什么不好的。接下来就在AndroidManifest.xml的添加application里添加如下内容
androidx的话换成"androidx.core.content.FileProvider"
其中${applicationId}是你app的包名id,android:resource="@xml/file_paths"中的file_paths是你刚才在xml文件夹下新建的文件。
接下来是获取uri
public static Uri getUri(Context context, File file) {
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//大于7.0时
uri = FileProvider.getUriForFile(context, context.getPackageName() + ".provider", file);
//context.getPackageName() + ".provider"就是你刚才加到AndroidManifest.xml文件里的provider里的android:authorities的值
} else {
uri = Uri.fromFile(file);
}
return uri;
}
不单单app升级安装,以后所有用到uri的地方以后都可以用getUri(Context context, File file) 获取到授权的正确的Uri。最后贴下app下载完后制动弹出安装的代码
private void isAPK(String filePath) {//filePath:安装包的本地地址链接
File file = new File(filePath);
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 = ContentUriUtil.getUri(activity, file);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
// 7.0以下
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
}
activity.startActivity(intent);
}
题外话,implementation 'com.liulishuo.filedownloader:library:1.6.8',一个挺好用的下载框架
本文参考:https://blog.csdn.net/MingHuang2017/article/details/82830727
用上述框架做的android升级的Helper
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.eluton.base.BaseApplication;
import com.eluton.bean.gson.UpdateGsonBean;
import com.eluton.url.Api;
import com.eluton.url.OkhttpUtil;
import com.eluton.util.ContentUriUtil;
import com.eluton.util.LogUtil;
import com.eluton.yltdemo.R;
import com.liulishuo.filedownloader.BaseDownloadTask;
import com.liulishuo.filedownloader.FileDownloadLargeFileListener;
import com.liulishuo.filedownloader.FileDownloadSampleListener;
import com.liulishuo.filedownloader.FileDownloader;
import java.io.File;
public class UpdateHelper {
private Activity activity;
private String downUrl;
private final String downPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/开课通.apk";
public UpdateHelper(Activity activity) {
this.activity = activity;
}
private AlertDialog updateDialog;
private TextView wantTv;
private void showUpdate() {
if (updateDialog == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.AlertDialog);
View view = LayoutInflater.from(activity).inflate(R.layout.dialog_update, null);
TextView ensure = view.findViewById(R.id.ensure);
TextView cancel = view.findViewById(R.id.cancel);
ImageView warnImg = view.findViewById(R.id.img);
wantTv = view.findViewById(R.id.notice);
if (updateGsonBean != null)
wantTv.setText("新版本:" + updateGsonBean.getAndroidVersionName());
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (updateDialog != null)
updateDialog.dismiss();
if (TaskId != 0) {
FileDownloader.getImpl().pause(TaskId);
}
}
});
ensure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
updateApk();
}
});
builder.setView(view);
builder.setCancelable(false);
updateDialog = builder.create();
}
if (!updateDialog.isShowing())
updateDialog.show();
}
private UpdateGsonBean updateGsonBean;
public void checkUpdate(final boolean needTip) {
new Api() {
@Override
public void bindView(OkhttpUtil.HttpBean httpBean, boolean state) {
if (state && httpBean.getCode() == 200) {
updateGsonBean = BaseApplication.gson.fromJson(httpBean.getContent(), UpdateGsonBean.class);
if (Integer.parseInt(updateGsonBean.getAndroidVersion()) > BaseApplication.versionCodes) {//BaseApplication.versionCodes
downUrl = updateGsonBean.getAndroidDownload();
LogUtil.i("url:" + downUrl);
showUpdate();
} else {
if (needTip)
Toast.makeText(activity, "当前已经是最新版本", Toast.LENGTH_SHORT).show();
}
} else {
if (needTip)
Toast.makeText(activity, httpBean.getContent() + "", Toast.LENGTH_SHORT).show();
}
}
}.Update();
}
private int TaskId;
private boolean isUpdate;//第一次更新时删掉旧安装包的标志符,保证下载最新的安装包
private void updateApk() {
if (!isUpdate) {
isUpdate = true;
File file = new File(downPath);
if (file.exists()) {
file.delete();
}
}
BaseDownloadTask task = FileDownloader.getImpl().create(downUrl).setPath(downPath).setListener(new FileDownloadSampleListener() {
@Override
protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {
int num = (int) (soFarBytes * 100 / totalBytes);
wantTv.setText("下载进度:" + num + "%");
}
@Override
protected void error(BaseDownloadTask task, Throwable e) {
Toast.makeText(activity, e.getMessage() + "", Toast.LENGTH_SHORT).show();
}
@Override
protected void completed(BaseDownloadTask task) {
wantTv.setText("下载完成");
installApk();
}
});
TaskId = task.getId();
task.start();
}
private void installApk() {
File file = new File(downPath);
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 = ContentUriUtil.getUri(activity, file);//
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
// 7.0以下
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
}
activity.startActivity(intent);
}
}
其中自定义了对话框,R.style.AlertDialog和xml视图的内容如下
视图的xml中很多属性没写明,反正知道自定义对话框用来进行更新提醒就是,视图什么的自己写个自己喜欢的吧