android菜鸟学习笔记17----Android数据存储(一)文件读写
假如有如下需求,要求能够记录用户输入的用户名和密码,下次登录时,能直接获取之前保存的用户名密码,并在相应的EditText中显示。
要保存用户输入的数据,最先想到的应该就是文件读写了。
通过对android应用打包安装过程的观察,发现每个应用安装之后,都会在/data/data/下新建一个与应用包名相同的目录,该应用的所有文件都存放在该目录下。
例如:
main_layout.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25sp"
android:textColor="#00ff00"
android:text="@string/tv_info_text"/>
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_name_text"/>
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_password_text"
android:inputType="textPassword"/>
<CheckBox
android:id="@+id/cb_reme"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cb_reme_text"
/>
<Button
android:id="@+id/btn_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/btn_login_text"/>
</LinearLayout>
FileUtils.java代码:
package cn.csc.utils;
import java.io.FileOutputStream;
public class FileUtils {
public static boolean writeFile(String data){
String path = "/data/data/cn.csc.file/userinfo.txt";
try {
FileOutputStream fos = new FileOutputStream(path);
fos.write(data.getBytes());
fos.close();
return true;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
}
MainActivity.java:
public class MainActivity extends Activity {
private EditText et_username;
private EditText et_password;
private CheckBox cb_reme;
private Button btn_login;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
et_username = (EditText) findViewById(R.id.et_username);
et_password = (EditText) findViewById(R.id.et_password);
cb_reme = (CheckBox) findViewById(R.id.cb_reme);
btn_login = (Button) findViewById(R.id.btn_login);
btn_login.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
String username = et_username.getText().toString();
String password = et_password.getText().toString();
boolean checked = cb_reme.isChecked();
if(TextUtils.isEmpty(username)||TextUtils.isEmpty(password)){
Toast.makeText(MainActivity.this, "用户名密码不能为空",Toast.LENGTH_SHORT).show();
return;
}
if(checked){
if(username.equals("dqrcsc")&&password.equals("abcdef")){
if(FileUtils.writeFile(username+"#"+password)){
Toast.makeText(MainActivity.this, "保存成功",Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(MainActivity.this, "保存失败",Toast.LENGTH_SHORT).show();
}
Toast.makeText(MainActivity.this, "登录成功",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(MainActivity.this, "登录失败",Toast.LENGTH_SHORT).show();
}
}
}
});
}
}
运行结果:


注意到该文件的权限,默认仅能被当前用户读写。
保存用户名,密码成功,还需要实现下次打开时,自动读取保存的用户名密码,对应的方法可以在onCreate()中调用,已实现打开时自动加载。
在FileUtils中添加读取文件的方法:
public static String readFile(){
String path = "/data/data/cn.csc.file/userinfo.txt";
try {
BufferedReader br = new BufferedReader(new FileReader(path));
String line = br.readLine();
br.close();
return line;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
修改MainActivity.java中onCreate(),添加如下代码:
String info = FileUtils.readFile();
if(TextUtils.isEmpty(info)){
return;
}
if(!info.contains("#")){
return;
}else{
String[] strs = info.split("#");
et_password.setText(strs[1]);
et_username.setText(strs[0]);
cb_reme.setChecked(true);
}
上述直接操作写死的文件路径,是存在问题的,如果我修改了当前应用的包名,然后去操作写死的文件,就会出现权限被拒绝的错误:
如Manifest.xml中package修改为cn.csc.file1,注意要修改Activity节点,name使用全限定名:cn.csc.file.MainActivity。
运行程序,出现如下错误:
java.io.FileNotFoundException: /data/data/cn.csc.file/userinfo.txt (Permission denied)

文件仍然存在,但是相对与当前应用,已属于别的用户所有,而该文件只能被所属用户进行读写操作。
当然,如果把userinfo.txt的权限修改下,还是可以读写的。
adb shell挂载linux文件系统,然后进入/data/data/file目录
然后chmod 666 userinfo.txt

然后,就可以直接让cn.csc.file1应用直接读写了。
其实,Context有可以获取当前包文件存储路径的API:
getFilesDir();//返回/data/data/包名/files目录
getCacheDir();//返回/data/data/包名/cache目录
由于是在FileUtils类中读写文件,要使用Context的API,则需要把Context作为参数传递:
在FileUtils中添加对原有读写文件方法的重载:
public static boolean writeFile(Context context, String data){
File filesDir = context.getFilesDir();
try {
FileOutputStream fos = new FileOutputStream(new File(filesDir, "userInfo.txt"));
fos.write(data.getBytes());
fos.close();
return true;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
public static String readFile(Context context){
File filesDir = context.getFilesDir();
try {
BufferedReader br = new BufferedReader(new FileReader(new File(filesDir, "userInfo.txt")));
String line = br.readLine();
br.close();
return line;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
修改MainActivity.java,调用新添加的读写文件方法,把当前Activity实例作为Context参数传递:
FileUtils.writeFile(MainActivity.this,username+"#"+password)
String info = FileUtils.readFile(this);
重新运行程序:

cn.csc.file1目录下多出了一个空的目录files
然后,填写信息,点登录,保存数据

files目录下,便多出了userInfo.txt的文件
文件的默认权限总是-rw- --- ---,即只有该文件所有者可以读写,其他用户均不具有对该文件的读写权限,如何设置文件权限呢?
Context提供了两个方法:
abstract FileInputStream openFileInput(String name)
返回指向/data/data/包名/files/name文件的FileInputStream实例
abstract FileOutputStream openFileOutput(String name, int mode)
返回指向/data/data/包名/files/name文件的FileOutputStream实例,文件不存在时,创建该文件,并根据mode参数,设置文件的权限。
mode的取值可以为:
Context.MODE_PRIVATE:(实际值:0x00000000)只有所有者具有读写权限,文件已存在直接覆盖
Context.MODE_APPEND:(实际值:0x00008000)文件已存在,则追加
Context.MODE_WORLD_READABLE:(实际值:0x00000001)其他所有用户具有读权限
Context.MODE_WORLD_WRITEABLE:(实际值:0x00000002)其他所有用户具有写权限
可以将这些取值位或操作,然后传递给openFileOutput()方法,设置同时具有多个属性。
修改FileUtils中的两个文件读写方法:
public static boolean writeFile(Context context, String data){
try {
FileOutputStream fos = context.openFileOutput("userInfo.txt", Context.MODE_PRIVATE);
fos.write(data.getBytes());
fos.close();
return true;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
public static String readFile(Context context){
try {
BufferedReader br = new BufferedReader(new InputStreamReader(context.openFileInput("userInfo.txt")));
String line = br.readLine();
br.close();
return line;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
修改mode字段,查看对应文件权限:
MODE_PRIVATE:

MODE_APPEND:

此时,点击登陆按钮,文件大小变成了26,存放了两条用户名及密码信息,可见,是在已存在文件中,追加内容。并且,文件权限变味了同组用户也可读写。
MODE_WORLD_READABLE:

所有用户均可读
MODE_WORLD_WRITEABLE:

所有用户均可写
MODE_WORLD_READABLE| MODE_WORLD_WRITEABLE:

所有用户均可读可写
MODE_APPEND| MODE_WORLD_READABLE| MODE_WORLD_WRITEABLE:

所有用户均可读可写,并且是追加模式,当文件存在时,向已存在文件中追加内容,而非覆盖操作。
注意:由于MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE这两种模式过于危险,易产生安全问题,在Android4.2中已然废弃。
20150709补充:
还有一个模式:Context.MODE_MULTI_PROCESS

一般用于多个进程对同一个文件的读写,同组用户对其都有读写权限,与MODE_APPEND不同的是每次都会覆盖已存在的文件,而不追加。
读写缓存中的文件:
利用Context的getCacheDir();//返回/data/data/包名/cache目录,获取缓存的存放路径。
在FileUtils中添加两个方法:
public static boolean writeCache(Context context, String data){
File filesDir = context.getCacheDir();
try {
FileOutputStream fos = new FileOutputStream(new File(filesDir, "userInfo.txt"));
fos.write(data.getBytes());
fos.close();
return true;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
public static String readCache(Context context){
File filesDir = context.getCacheDir();
try {
BufferedReader br = new BufferedReader(new FileReader(new File(filesDir, "userInfo.txt")));
String line = br.readLine();
br.close();
return line;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
在MainActivity调用这两个读写Cache的方法:
运行结果:

注意:文件和缓存的区别:

Clear cache会直接清空cache目录,并且不会弹出警告信息
Clear data则会先弹出警告窗口,确认之后,才会清空files目录。

一般优化工具,都会清空Cache目录,而不会直接清空files目录,所以常用的文件要保存在files目录中,而临时文件则可以考虑放在cache目录中。
读写SD卡中的文件:
Environment类提供了几个很实用的方法:
static File getDataDirectory() /data/data/包名/files
static File getDownloadCacheDirectory() 下载缓存路径
static File getExternalStorageDirectory() SD卡路径
static String getExternalStorageState() SD卡状态
static File getRootDirectory() 根目录
测试上述方法输出:
Log.i("Environment","getDataDirectory:"+Environment.getDataDirectory().getPath());
Log.i("Environment","getDownloadCacheDirectory:"+Environment.getDownloadCacheDirectory().getPath());
Log.i("Environment","getExternalStorageDirectory:"+Environment.getExternalStorageDirectory().getPath());
Log.i("Environment","getRootDirectory:"+Environment.getRootDirectory().getPath());
Log.i("Environment","getExternalStorageState:"+Environment.getExternalStorageState());

修改FileUtils添加两个读写SD卡的方法:
public static boolean writeSD(String data){
try {
String state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){
File ext = Environment.getExternalStorageDirectory();
FileOutputStream fos = new FileOutputStream(new File(ext,"userInfo.txt"));
fos.write(data.getBytes());
fos.close();
return true;
}
return false;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
public static String readSD(){
try {
String state = Environment.getExternalStorageState();
if(state.equals(Environment.MEDIA_MOUNTED)){
File ext = Environment.getExternalStorageDirectory();
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(ext.getPath()+"/userInfo.txt")));
String line = br.readLine();
br.close();
return line;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
此外,写SD需要配置权限: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
运行结果:

注意:在读写SD卡之前,需要先检查SD卡的状态,已挂载才能进行读写操作。此外,读写SD卡需要在Manifest.xml中配置相关的读写权限。在4.0之前读取SD卡是不需要配置读SD卡的权限的,4.0之后,添加了SD卡读取保护的设置,如果用户设置了SD卡读取保护,则读取SD卡中的文件也需要配置权限的。所以,最好同时在Manifest.xml中配置上读写SD卡的权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
android菜鸟学习笔记17----Android数据存储(一)文件读写的更多相关文章
- android菜鸟学习笔记18----Android数据存储(二)SharedPreferences
数据存储的方式,有比直接文件读写更加简便的方式,那就是操作SharedPreferences. SharedPreferences一般用于存储用户的偏好设定,暂时不支持多进程操作. SharedPre ...
- android菜鸟学习笔记24----与服务器端交互(一)使用HttpURLConnection和HttpClient请求服务端数据
主要是基于HTTP协议与服务端进行交互. 涉及到的类和接口有:URL.HttpURLConnection.HttpClient等 URL: 使用一个String类型的url构造一个URL对象,如: U ...
- android菜鸟学习笔记25----与服务器端交互(二)解析服务端返回的json数据及使用一个开源组件请求服务端数据
补充:关于PHP服务端可能出现的问题: 如果你刚好也像我一样,用php实现的服务端程序,采用的是apache服务器,那么虚拟主机的配置可能会影响到android应用的调试!! 在android应用中访 ...
- android菜鸟学习笔记23----ContentProvider(三)利用内置ContentProvider监听短信及查看联系人
要使用一个ContentProvider,必须要知道的是它所能匹配的Uri及其数据存储的表的结构. 首先想办法找到访问短信及联系人数据的ContentProvider能接受的Uri: 到github上 ...
- android菜鸟学习笔记21----ContentProvider(一)ContentProvider的简单使用
ContentProvider是Android四大组件之一,它用来封装数据,并通过ContentResolver接口将数据提供给其他应用.只有当需要在多个应用之间共享数据时才会用到ContentPro ...
- android菜鸟学习笔记16----Android项目打包安装过程(Run as Android Application)
右击项目名称,Run as Android Appication之后,Android项目打包安装过程: 1.打包生成.apk文件: 1)把源码中的.java文件编译生成.class文件 2)将所有的. ...
- android菜鸟学习笔记14----Android控件(三) ListView的简单使用
MVC模式: MVC的基本原理就是通过Controller连接View和Model.当View中所显示的数据发生变化时,会通知Controller,然后由Controller调用Model中的相关方法 ...
- android菜鸟学习笔记10----Intent及<intent-filter>
关于Bundle: 注意到Activity的onCreate()方法的签名是protected void onCreate(Bundle savedInstanceState),其参数是一个Bundl ...
- android菜鸟学习笔记31----Android使用百度地图API(二)获取地理位置及地图控制器的简单使用
1.获取当前地理位置: Android中提供了一个LocationManager的类,用于管理地理位置.不能通过构造函数获取该类的实例,而是通过Context的getSystemService(): ...
随机推荐
- Codeforces Round #466 (Div. 2) B. Our Tanya is Crying Out Loud[将n变为1,有两种方式,求最小花费/贪心]
B. Our Tanya is Crying Out Loud time limit per test 1 second memory limit per test 256 megabytes inp ...
- BZOJ3631 松鼠的新家(树链剖分)
题目链接 松鼠的新家 差不多可以说是树链剖分的模板题了,直接维护即可. #include <bits/stdc++.h> using namespace std; #define REP( ...
- How To Commit Just One Data Block Changes In Oracle Forms
You have an Oracle Form in which you have multiple data blocks and requirement is to commit just one ...
- VS2010 MFC中 在FormView派生类里获取文档类指针的方法
经过苦苦调试,今晚终于解决了一个大问题. 我想要实现的是:在一个FormView的派生类里获取到文档类的指针. 但是出现问题:试了很多办法,始终无法获取到. 终于,此问题在我不懈地调试加尝试下解决了. ...
- [java面试]关于多态性的理解
执行时多态性是面向对象程序设计代码重用的一个最强大机制.Java多态性的概念也能够被说成"一个接口.多个方法".Java实现执行时多态性的基础是动态方法调度,它是一种在执行时而不是 ...
- 微信小程序 - 单个题目
后端传过来的数据,如果通过wx:for遍历出来那就是一个页面全部排下来.... 我的想法就是,页面初始化时设置一个默认值 1/50 就是 index+1 / 50(后端传过来的数组长度),通过控制in ...
- vue2.0 仿手机新闻站(三)通过 vuex 进行状态管理
1.创建 store 结构 2.main.js 引入 vuex 3. App.vue 组件使用 vuex <template> <div id="app"&g ...
- js执行顺序总结
参考博文:http://www.2cto.com/kf/201401/273825.html http://www.jb51.net/article/44123.htm http://zhidao.b ...
- hdfs笔记
Distributed File System 数据量越来越多,在一个操作系统管辖的范围存不下了,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,因此迫切需要一种系统来管理多台机器上的文 ...
- Oracle TNS路径
修改tnsnames.oRA,监听文件 Oracle TNS路径 G:\Oracle\product\11.2.0\client_1\network\admin\tnsnames.oRA