一.内部存储数据
- 存储数据方法:
- 使用 内部存储器 存储数据
- 使用 外部存储器 存储数据
- 使用 共享首选项 存储数据
- 从 远程位置访问 数据
1.使用 内部存储器 存储数据
- 内部存储 ≠ 内存,内部存储特点:
- 只能被创建自己的应用访问,数据信息私有
- 应用卸载后,内部存储中的文件也被删除
- 内部存储空间耗尽,手机就无法使用
- Android 的 java.io 包 提供各种类 访问本地文件,实现 将数据写入文件 和 从文件中读取数据
- I/O 数据流分类:
- input 输入流:读取 和应用关联的私有文件,使用 openFileInput() 方法
- output 输出流:写入 和应用关联的私有文件,使用 openFileOutput() 方法
- 文件保存在 /data/data/<package name>/files/内部存储文件,与其他资源(如图像)分组在一起
2.output 输出流
- 将数据从应用中输出,执行过程:
- 使用 android.content.Context类的 openFileOutput() 方法 打开或创建文件
- 使用 java.io.FileOutputStream类的 write() 方法 将数据写入文件中
- 使用 java.io.FileOutputStream类的 close() 方法 关闭文件(一定要记得关闭!)
- 文件创建模式:
- MODE_PRIVATE: 文件不存在,则创建文件,文件存在,则写入内容覆盖源文件(默认模式)
- MODE_APPEND: 文件不存在,则创建文件,文件存在,则写入内容追加在原来的后面
- FileOutputStream类 包含如下用于管理文件的公共方法:
- void close() :关闭输出流
- void write(byte[] buffer, int offset, int byteCount) 写入一个字节组,从第几个字节开始写,一共写几个字节
- void write(byte[] buffer) 写入一个字节组,全部内容
- void write(int oneByte) 写入一个字节,字节所在的序列号
3.input 输入流
- 写入 /data/data/<package name>/files文件夹 的文件,可供 对此文件夹 具有 写访问权 的应用 访问
- 打开内部存储文件读取数据,执行步骤:
- 使用 android.content.Context类的 openFileInput() 方法 打开文件
- 使用 java.io.FileInputStream类的 read() 方法 从文件读取数据
- 使用 java.io.FileInputStream类的 close() 方法 关闭文件(一定要记得关闭!)
- FileInputStream类 包含特定的公共方法,它们是:
- int available()
- int read(byte[] buffer)
- int read(byte[] buffer, int offset, int byteCount)
- long skip(long byteCount)
4.将静态文件作为资源使用
- android.content.Context 类提供方法,提高文件操作效率:
- getFilesDir() 路径:data/data/包名
- getDir(String dirname, int mode)
- deleteFile(String filename)
- fileList()
- 静态文件: 应用程序包中的 只读文件,一旦创建就无法更改,位置: res/raw
- openRawResource() 方法:调用静态文件
5.实现文件的写入、读取、清空
public class MainActivity extends Activity { Button read, write, clear; TextView tvDisplay; EditText etFile, etContent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); read = (Button) findViewById(R.id.btnRead); // 读取、书写、清空文件按钮 write = (Button) findViewById(R.id.btnWrite); clear = (Button) findViewById(R.id.btnClear); tvDisplay = (TextView) findViewById(R.id.tvDisplay); // 文件内容显示区域 etFile = (EditText) findViewById(R.id.etFile); // 输入文件名、文件内容 etContent = (EditText) findViewById(R.id.etContent); ButtonListener listener = new ButtonListener(); read.setOnClickListener(listener); write.setOnClickListener(listener); clear.setOnClickListener(listener); } class ButtonListener implements OnClickListener { @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnWrite: // 根据文件名 文件内容 实现写入操作 writeToFile(etFile.getText().toString(), etContent.getText().toString()); break; case R.id.btnRead: // 读取操作 if ("".equals(etFile.getText().toString())) { // 如果文件名为空 读取静态资源 BufferedInputStream bis = null; // 创建一个缓冲流用于实现读取 try { // 静态资源:在res/raw文件夹下,没有指定文件名时,直接读取的资源 // getResources():用于获取静态资源,获取后调用 openRawResource()读取该文件的流 InputStream is = getResources().openRawResource(R.raw.introduction); bis = new BufferedInputStream(is); // 实例化缓冲流,实现读取 byte[] data = new byte[bis.available()]; // 存储流中可读的字节 bis.read(data); // 将字节读取并存储到 data数组中 String s = new String(data); tvDisplay.setText(s); // 读取到的内容设置到显示内容上 } catch (Exception e) { Log.e("MainActivity", e.getMessage()); } finally { // 关闭流 不可或缺的部分!! try { if (bis != null) bis.close(); } catch (IOException e) { Log.e("MainActivity", e.getMessage()); }} }else { // 如果文件名不为空,根据文件名读取文件 readFromFile(etFile.getText().toString()); } break; case R.id.btnClear: // 清空 = 把内容给设置为空 etFile.setText(""); etContent.setText(""); break; }}} void readFromFile(String filename) { FileInputStream fis = null; // FileInputStream 输入流 读取 try { fis = openFileInput(filename); byte[] data = new byte[fis.available()]; // 定义字节数组 用于存放待会读取的数据 fis.read(data); // 读取多个字节到 data数组 读取的字节数目是 data数组大小 String s = new String(data); // 将 字节数组data 转换成一个字符串 tvDisplay.setText(s); // 展示读取到的文本 Log.e("file", "readFromFile读取成功"); } catch (Exception e) { Log.e("MainActivity", e.getMessage()); } finally { // 关闭流,不可以省略!! try { if (fis != null) fis.close(); } catch (IOException e) { Log.e("MainActivity", e.getMessage()); }}} void writeToFile(String filename, String content) { FileOutputStream fos = null; // FileOutputStream 输出流 写入 try { // 1. MODE_PRIVATE: 文件不存在,则创建文件,文件存在,则写入内容覆盖源文件 // 2. MODE_APPEND: 文件不存在,则创建文件,文件存在,则写入内容追加在原来的后面 fos = openFileOutput(filename, MODE_APPEND); fos.write(content.getBytes()); // Content内容转换为字节数组 写入文件 Toast.makeText(MainActivity.this, "写入成功!", Toast.LENGTH_LONG).show(); Log.e("file", "writeToFile写入成功"); } catch (Exception e) { Log.e("MainActivity", e.getMessage()); // 若发生异常 获取异常信息写入日志 } finally { // 正常或不正常都要执行finally ,目的是为了关闭流 try { fos.close(); // 使用完流记得关闭流 } catch (IOException e) { Log.e("MainActivity", e.getMessage()); }}}}
- 写入文件:
- 读取自己写入的文件:
- 读取静态文件:
二.外部存储数据
1.使用 外部存储器 存储数据
- SD 卡:外部存储设备,存储容量 远高于 内部存储设备,并且 可扩展,其所有文件都是 公共的
- 下图显示了 DDMS 透视图的 File Explorer 选项卡中可用的 sdcard 文件夹。/mnt/sdcard/fileName
2.检查外部存储的可用性
- android.os.Environment 类 定义了描述 外部存储设备状态 的 常量
- 通过 String getExternalStorageState() 获取常量:
常量
描述
MEDIA_MOUNTED
明确说明介质已经存在并加载,并提供读/写访问。
MEDIA_REMOVED
明确说明介质不存在。
MEDIA_UNMOUNTED
明确说明介质已存在但没有加载。
MEDIA_MOUNTED_READ_ONLY
明确说明介质已经存在并加载,并且只能对其进行只读访问。
- Environment 类提供 获取外部存储设备信息 的方法有:
- getDataDirectory()
- getExternalStorageDirectory()
- getExternalStoragePublicDirectory(String type)
- getExternalStorageState()
- getRootDirectory()
- isExternalStorageEmulated()
- isExternalStorageRemovable()
3.对外部存储设备进行数据读取写入
- 要对 SD 卡进行读取和写入,需要用 getExternalStorageDirectory() 方法 获取 外部存储设备的 路径
- 数据读取和写入过程 与 内部存储文件读取和写入过程 相同
- 对外部存储设备读取或写入前,要在 AndroidManifest.xml 文件 中指定权限:
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE"> </uses-permission>
- SD卡读写步骤:
- 判断 外部存储设备 是否加载成功 并可 访问
- 获取 外部存储器的 存储路径
- 使用 输入/输出流 进行 文件读写
- 在 AndroidManifest.xml 中 添加权限
4.实现文件的写入、读取、清空
public class MainActivity extends Activity { Button b1, b2, b3; TextView tvDisplay; EditText etFile, etContent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 查找按钮 TextView 文本框 b1 = (Button) findViewById(R.id.btnRead); b2 = (Button) findViewById(R.id.btnWrite); b3 = (Button) findViewById(R.id.btnClear); tvDisplay = (TextView) findViewById(R.id.tvDisplay); etFile = (EditText) findViewById(R.id.etFile); etContent = (EditText) findViewById(R.id.etContent); // 注册监听器 ButtonListener listener = new ButtonListener(); b1.setOnClickListener(listener); b2.setOnClickListener(listener); b3.setOnClickListener(listener); } void readFromFile(String filename) { // 自定义方法,用于读取文件 String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) { String path = Environment.getExternalStorageDirectory() + "/" + filename; FileInputStream fis =null; try { fis = new FileInputStream(path); byte[] data=new byte[fis.available()]; //创建一个字节数组,用于存储读取的数据 fis.read(data); //读取多个字节,放到data数组,读取大小为data数组大小 String s=new String(data); //将字节数组转成一个字符串 tvDisplay.setText(s); //设置TextView的显示文本 } catch (Exception e) { Log.e("MainActivity", e.getMessage()); }finally{ try { if (fis != null) fis.close(); } catch (IOException e) { Log.e("MainActivity", e.getMessage()); }}}} // 需要在AndroidManifest.xml里加入写入外部存储的权限才能写入 // <uses-permission // android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> void writeToFile(String filename, String content) { // 自定义方法,用于写入文件 String state = Environment.getExternalStorageState(); // 获取外部设备的状态 File dataDirectory = Environment.getDataDirectory(); File externalStorageDirectory = Environment.getExternalStorageDirectory(); File externalStoragePublicDirectory = Environment.getExternalStoragePublicDirectory(ACTIVITY_SERVICE); File rootDirectory = Environment.getRootDirectory(); boolean externalStorageEmulated = Environment.isExternalStorageEmulated(); boolean externalStorageRemovable = Environment.isExternalStorageRemovable(); boolean externalStorageEmulated2 = Environment.isExternalStorageEmulated(); if (state.equals(Environment.MEDIA_MOUNTED)) { // 判断状态是否是MEDIA_MOUNTED String path = Environment.getExternalStorageDirectory() // 获取外部设备的存储路径 + "/" + filename; // /mnt/sdcard/andy.txt FileOutputStream fos= null; try { // 创建输出流对象,第二个true说明以追加模式写入文件 fos = new FileOutputStream(path, true); fos.write(content.getBytes()); // 将content的内容转成字节数组写入文件 Toast.makeText(MainActivity.this, "写入成功!", Toast.LENGTH_LONG) .show(); } catch (Exception e) { Log.e("MainActivity", e.getMessage()); }finally{ try { if (fos != null) fos.close();// 关闭流 } catch (IOException e) { Log.e("MainActivity", e.getMessage()); }}}} class ButtonListener implements OnClickListener { @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnWrite: // 从第一个文本框获取文件名,第二个获取内容,调用writeToFile方法写入文件 writeToFile(etFile.getText().toString(), etContent.getText() .toString()); break; case R.id.btnRead: // 从第一个文本框获取文件名,调用readFromFile方法读取 readFromFile(etFile.getText().toString()); break; case R.id.btnClear: etFile.setText(""); etContent.setText(""); break; }}}}
效果展示:
5.内部存储器 VS 外部存储器
- 内部存储器
- 文件是应用专用
- 卸载应用时文件自动移除
- 存储空间有限
- 存储路径:/data/data/程序包名/files文件夹
- 外部存储器
- 文件是公共的
- 卸载引用时SD卡数据保留
- SD 卡的存储容量 远高于 内部存储设备 并且可扩展
- 存储路径:/mnt/sdcard文件夹
6.总结
- 存内存中的文件保存在 /data/data/<package name>/files 文件夹中,并与其他资源分组在一起
- FileInputStream 类和 FileOutputStream 类分别从内部存储器 读取文件 和 将文件写入
- SD 卡读写之前,用 Environment 类的 getExternalStorageDirectory() 方法 获取 外部存储设备的 路径
三.共享偏好
- 共享首选项: 使应用中的更改持久不变
- Android 提供两种类型的偏好设置: 活动级首选项 应用程序级首选项
- Android 提供 android.content.SharedPreferences 接口,用于 保存和检索 内置数据类型的 持久键 - 值对
- SharedPreferences 接口 支持的 数据类型 有: boolean、 float、 int、 long、 String
- 检索共享偏好设置:
- getSharedPreferences()
- getPreferences()
- 共享偏好设置信息 存储在 Android 设备数据文件夹中 的 XML 文件中,并将 应用软件包名 作为 子文件夹的 名称
- 修改共享首选项,需要使用 SharedPreferences.Editor 接口,如下图所示
- SharedPreferences.Editor 接口在共享首选项中放置和存储值的一些方法有:
- commit()
- putXXX(String key, XXX value)
- 想要将整数数据写入共享首选项。使用以下哪个选项? putInteger() setInteger() putInt() setInt()
- 共享首选项不需要提供权限设置
public class MainActivity extends Activity { //1:声明组件对象 Button login; EditText etUser,etPwd; CheckBox cb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //2:获取组件对象 login=(Button)findViewById(R.id.login); etUser=(EditText)findViewById(R.id.etUser); etPwd=(EditText)findViewById(R.id.etPwd); cb=(CheckBox)findViewById(R.id.cbRemPwd); //因为下次需要登录界面显示的时候就把记住的用户名和密码显示,所以需要在onCreate方法里就把数据读出来显示 //获取一个SharedPreferences对象,调用getXXX方法,根据键获取值 SharedPreferences sp1=getSharedPreferences("login",MODE_PRIVATE); String user=sp1.getString("username",null); //第二个参数表示若没有该项信息,返回的替换值 String pwd=sp1.getString("password",null); etUser.setText(user); etPwd.setText(pwd); //android.content.SharedPreferences(共享偏好) 接口 //它有公开方法操作偏好设置 login.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(cb.isChecked()){ //判断记住密码这个复选框是否选中 //创建一个SharedPreferences对象 SharedPreferences sp=getSharedPreferences("login",MODE_PRIVATE); //通过edit方法获取一个编辑器对象,用于编辑偏好设置 Editor editor=sp.edit(); //以键-值得形式写入数据到文件 editor.putString("username",etUser.getText().toString()); editor.putString("password",etPwd.getText().toString()); editor.commit(); //提交写入数据 Toast.makeText(MainActivity.this,"记住成功!", Toast.LENGTH_SHORT).show(); }}});}}
四.远程位置访问数据
- 用 java.net.URL 类 访问 因特网上提供的资源
- 从 URL 类指定的资源读取数据,需要创建对 URLConnection 类的引用
- 从 URLConnection 类的对象读取数据过程的其余部分 与 从其他输入流中读取数据 的过程相同
- 创建访问 Web 上存储的数据的 Android 应用,就要在 AndroidManifest.xml 文件中指定权限,以下哪个选项来实现此需求? <uses-permission> <uses-permissions> <use-permission> <use-permissions>
public class MainActivity extends Activity { Button download; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); download=(Button)findViewById(R.id.download); download.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { //单开一个子线程,执行下载任务 Toast.makeText(MainActivity.this,"开始下载!",Toast.LENGTH_SHORT).show(); DownloadThread t=new DownloadThread(); t.start(); }});} //该线程类封装一个任务,实现下载和写入sd卡 class DownloadThread extends Thread{ @Override public void run() { String urlpath ="http://img.jk51.com/img_jk51/78037076.jpeg"; try{ URL url=new URL(urlpath); //指定一个网址,创建一个URL对象,用于操作该网址 URLConnection con=url.openConnection(); //打开一个到该网址的连接 InputStream is=con.getInputStream(); //获取一个输入流用于读取/下载数据 BufferedInputStream bis=new BufferedInputStream(is); //效率上,缓冲流好些 String state=Environment.getExternalStorageState(); if(state.equals(Environment.MEDIA_MOUNTED)){ // /mnt/sdcard/xwz.jpg String path=Environment.getExternalStorageDirectory()+"/dog.jpg"; FileOutputStream fos=new FileOutputStream(path); //输出流对象,用于写入文件 int i; while((i=bis.read())!=-1){ //每次读取单个字节,存储到变量i fos.write(i); //将该字节写入文件 } //记得在AndroidManifest.xml文件里添加权限设置 // <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> // <uses-permission android:name="android.permission.INTERNET"/> } }catch(Exception e){ Log.e("MainActivity",e.getMessage()); }}}}
总结:
- 为将数据写入文件和从文件中读取数据,Android 提供了通过 java.IO 类访问本地文件系统的功能。
- 存储在设备内存中的文件保存在 /data/data/<package name>/files 文件夹中,并与其他应用资源分组在一起。
- 在 Android 中,可以将文件直接保存到设备的内部存储中。保存在内部存储器上的文件是应用专用的,其他应用或用户都不能访问它们。
- 您可以使用 FileInputStream 类和 FileOutputStream 类分别从设备的内部存储器读取文件和将文件写入其中。
- 要从 SD 卡读取数据和将数据写入到 SD 卡,必须使用 Environment 类的 getExternalStorageDirectory() 方法获取外部存储设备的路
- Android 提供两种类型的偏好设置:活动级偏好设置和应用程序级偏好设置。
- 使用共享首选项存储偏好设置
- 应用级共享首选项:getSharedPreferences()-自定义xml文件名
- 活动级共享首选项:getPreferences()-以活动名作为xml文件名
- SharedPreferences.Editor接口可以使用 putXXX(String key, XXX value) 方法来创建和保存共享首选项,其中 XXX 指定了要保存到共享首选项的值的数据类型。
- SharedPreferences接口可以使用 getXXX(String key, XXX value) 方法来检索共享首选项,其中 XXX 指定了要从共享首选项读取的值的数据类型。
- 在 Android 平台上,您可以使用 java.net.URL 类来访问因特网上提供的资源。
- 要从 URL 类指定的资源读取数据,您需要创建对 URLConnection 类的引用。
- 从 URLConnection 类的对象读取数据过程的其余部分与从其他输入流中读取数据的过程相同