最近项目中使用到的自动更新功能,可是却碰上了小米1手机,这个手机既没有外置存储卡,也没有内置存储卡,只有机器本身的存储,于是乎各种下载都不起作用,因为之前是下载到外置存储卡中的,怎么办呢,东西还要做呀,想想办法吧!
然后想到一个解决办法,不是存储卡都没有么,那我就将东西下载到缓存文件夹下,/data/data/<package name>/cache下面,结果发现真的能下载,于是调用系统的安装,问题又来了,调用系统安装不能解析,原来是文件在cache目录下的只有本身的应用程序才可以访问,其他程序不能访问,看来不能放在cache目录下;于是继续研究,发现可以使用activity的openFileOutput设置文件的可读可写,这样一来文件就不会存放在cache文件夹下,而是存在于/data/data/<package name>/files目录下,并且调用系统安装提示可以安装了。说了这么多,还是快上代码吧,下面是apk的下载以及安装代码,有需求的可以相互交流下:
/**
* @Package com.g3.technology.util.download
* @Description: TODO(该类是用于下载apk文件,下载完并进行安装的专用类)
* Company: LastTNT
* @author 翟昆
* @date 2014-8-4 下午02:04:45
*/
public class UpdateAPKManager {
/* 随机生成文件名 */
private String apkName;
/* 下载地址 */
private String apkUrl;
/* 下载中 */
private static final int DOWNLOAD = 1;
/* apk下载结束 */
private static final int DOWNLOAD_FINISH = 2;
/* 记录进度条数量 */
private int progress;
/* 是否取消更新 */
private boolean cancelUpdate = false;
private Activity mContext;
/* 更新进度条 */
private Dialog mDownloadDialog;
private NumberProgressBar bnp;
/**
* 指定下载的文件夹
*/
private String downloadpath;
private boolean isExterUse = false;//外存储卡是否可用
public UpdateAPKManager(Activity context, String apkUrl, String versionName,
String FileName, String downloadpath) {
this.mContext = context;
this.apkUrl = apkUrl;
this.downloadpath = downloadpath;
this.apkName = FileName.toLowerCase() + versionName.toLowerCase() + ".apk";
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
isExterUse = true;
}
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DOWNLOAD:
// 设置进度条位置
bnp.setProgress(progress);
break;
case DOWNLOAD_FINISH:
// 安装文件
installApk();
break;
default:
break;
}
};
};
/**
* 安装APK文件
*/
private void installApk() {
File apkfile = null;
if(isExterUse){//如果外存储卡可用
apkfile = new File(this.downloadpath, apkName);
}else{
apkfile = new File(mContext.getFilesDir().getPath()+"/"+ apkName);
}
if (!apkfile.exists()) {
return;
}
//创建URI
Uri uri=Uri.fromFile(apkfile);
//创建Intent意图
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//启动新的activity
intent.setDataAndType(uri, "application/vnd.android.package-archive");
//执行安装
mContext.startActivity(intent);
mContext.finish();
}
/**
* 显示软件下载对话框
*/
private int counter = 0;
public void showDownloadDialog() {
File apkfile = null;
if(isExterUse){//如果外存储卡可用
apkfile = new File(this.downloadpath, apkName);
}else{
apkfile = new File(mContext.getFilesDir().getPath()+"/"+ apkName);
}
if (apkfile.exists()) {
installApk();
return;
}
// 给下载对话框增加进度条
final LayoutInflater inflater = LayoutInflater.from(mContext);
View v = inflater.inflate(R.layout.softupdate_progress, null);
bnp = (NumberProgressBar) v.findViewById(R.id.numberbar1);
counter = 0;
// 创建对话框
CustomDialog.Builder customBuilder = CustomDialog
.CreateDownloadDialogWithCal(this.mContext, "下载新版本",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
dialog.dismiss();
// 设置取消状态
cancelUpdate = true;
File apkfile = new File(mContext.getFilesDir().getPath()+"/"+ apkName);
if(isExterUse){//如果外存储卡可用
apkfile = new File(downloadpath, apkName);
}else{
apkfile = new File(mContext.getFilesDir().getPath()+"/"+ apkName);
}
if (apkfile.exists()) {
apkfile.delete();
}
}
}, v);
mDownloadDialog = CustomDialog.creatDialog(customBuilder);
mDownloadDialog.show();
// 下载文件
downloadApk();
}
/**
* 下载apk文件
*/
private void downloadApk() {
// 启动新线程下载软件
new downloadApkThread().start();
}
/**
* 下载文件线程
*/
private class downloadApkThread extends Thread {
@Override
public void run() {
try {
URL url = new URL(apkUrl);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setConnectTimeout(10 * 1000); // 超时时间
connection.connect(); // 连接
int length = connection.getContentLength();
if (connection.getResponseCode() == 200) { // 返回的响应码200,是成功.
InputStream inputStream = connection.getInputStream();
FileOutputStream outputStream = null; // 缓存
if(isExterUse){//如果外存储卡可用
File file = new File(downloadpath);
// 判断文件目录是否存在
if (!file.exists()) {
file.mkdir();
}
File apkFile = new File(downloadpath, apkName);
outputStream = new FileOutputStream(
apkFile); // 缓存
}else{
outputStream = mContext.getApplicationContext().openFileOutput(apkName, Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
}
byte[] buffer = new byte[1024*5];
int count = 0;
// 写入到文件中
do {
int numread = inputStream.read(buffer);
count += numread;
// 计算进度条位置
progress = (int) (((float) count / length) * 100);
// 更新进度
mHandler.sendEmptyMessageDelayed(DOWNLOAD, 2000);
if (numread <= 0) {
// 下载完成
mHandler.sendEmptyMessage(DOWNLOAD_FINISH);
break;
}
// 写入文件
outputStream.write(buffer, 0, numread);
} while (!cancelUpdate);// 点击取消就停止下载.
outputStream.flush();
outputStream.close();
inputStream.close();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 取消下载对话框显示
mDownloadDialog.dismiss();
}
};
}
openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,如果文件不存在,Android 会自动创建它。创建的文件保存在/data/data/<package name>/files目录,如: /data/data/cn.itcast.action/files/itcast.txt ,通过点击Eclipse菜单“Window”-“Show View”-“Other”,在对话窗口中展开android文件夹,选择下面的File Explorer视图,然后在File Explorer视图中展开/data/data/<package name>/files目录就可以看到该文件。
openFileOutput()方法的第二参数用于指定操作模式,有四种模式,分别为: Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件。
MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。
如果希望文件被其他应用读和写,可以传入:
openFileOutput(“itcast.txt”, Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);
android有一套自己的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其他资源比如文件的时候,就需要userid匹配。默认情况下,任何应用创建的文件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package name>/files),其他程序无法访问。除非在创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其他程序才能正确访问。