1、下载apk创建流时出现错误:java.io.FileNotFoundException: /storage/emulated/0/app/update/main.apk (Permission denied)
public static File getFileFromServer(String path, ProgressDialog pd) throws Exception {
//如果相等的话表示当前的sdcard挂载在手机上并且是可用的
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
//获取到文件的大小
pd.setMax(conn.getContentLength());
InputStream is = conn.getInputStream();
File file = new File(Environment.getExternalStorageDirectory() + app/update + "/", "main.apk");
FileOutputStream fos = new FileOutputStream(file);//写入字节流
BufferedInputStream bis = new BufferedInputStream(is);
byte[] buffer = new byte[1024];
int len;
int total = 0;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
total += len;
//获取当前下载量
pd.setProgress(total);
}
fos.close();
bis.close();
is.close();
return file;
} else {
return null;
}
}
这个报错出现在以上代码中的写入字节流步骤。导致这个的原因是android6.0以后对权限的控制更严格了,我们仅在Manifest文件中添加这两个权限是不够的,需要动态申请权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
a、一种方法 在读写之前加入以下代码
int REQUEST_EXTERNAL_STORAGE = 1;
String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
int permission = ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
MainActivity.this,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
2、7.0安装apk时报错提示:android.os.FileUriExposedException: file:///storage/emulated/0/app/update/main.apk exposed beyond app through Intent.getData()
intent.setAction(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//执行的数据类型
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");//
context.startActivity(intent);
报错在Uri.fromFile这个方法上。7.0为了提高安全性,添加了这种私有目录访问限制,此举是为了提高私有文件的安全性,面向 Android N 或更高版本的应用私有目录将被限制访问。这里就需要了解FileProvider这个类了。provider是android的一大组件,FileProvider顾名思义就是类似一个文件共享接口。还是在此代码基础上我们来用这个实现7.0+的安装
a.首先 增加一个xml文件夹在res下,然后新建一个xml文件,名字如pathlist.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path
name="external_storage_root"
path="."/>
</paths>
</resources>
其中paths节点就是可访问的路径列表,external-path是路径,是一个关键字。这里的.代表了 Environment.getExternalStorageDirectory()。如果你将path设为path="app",那么它代表着根目录下的app目录(/storage/emulated/0/app)
b.接着 在AndroidManifest.xml清单文件中注册provider。
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.kz.target.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/pathlist"/>
</provider>
这里的resource就是上面增加的pathlist.xml文件,名字注意一致。这里面重要的是authorities标识,名字你可以换成任意字符串。
c.使用FileProvider
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String authority = context.getPackageName() + ".provider"; //注意名字和注册的authorities一致
Uri contentUri = FileProvider.getUriForFile(context, authority, file);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android" +
".package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);