论Android的内置存储、外置存储和系统内存

最近项目中使用到的自动更新功能,可是却碰上了小米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 ,只有这样其他程序才能正确访问。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值