zoukankan      html  css  js  c++  java
  • android菜鸟学习笔记17----Android数据存储(一)文件读写

    假如有如下需求,要求能够记录用户输入的用户名和密码,下次登录时,能直接获取之前保存的用户名密码,并在相应的EditText中显示。

    要保存用户输入的数据,最先想到的应该就是文件读写了。

    通过对android应用打包安装过程的观察,发现每个应用安装之后,都会在/data/data/下新建一个与应用包名相同的目录,该应用的所有文件都存放在该目录下。

    例如:

    main_layout.xml代码:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 
     3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     4 
     5     android:layout_width="match_parent"
     6 
     7     android:layout_height="match_parent"
     8 
     9     android:layout_margin="10dp"
    10 
    11     android:orientation="vertical" >
    12 
    13     <TextView
    14 
    15         android:id="@+id/tv_info"
    16 
    17         android:layout_width="match_parent"
    18 
    19         android:layout_height="wrap_content"
    20 
    21         android:textSize="25sp"
    22 
    23         android:textColor="#00ff00"
    24 
    25         android:text="@string/tv_info_text"/>
    26 
    27     <EditText
    28 
    29         android:id="@+id/et_username"
    30 
    31         android:layout_width="match_parent"
    32 
    33         android:layout_height="wrap_content"
    34 
    35         android:hint="@string/et_name_text"/>
    36 
    37     <EditText
    38 
    39         android:id="@+id/et_password"
    40 
    41         android:layout_width="match_parent"
    42 
    43         android:layout_height="wrap_content"
    44 
    45         android:hint="@string/et_password_text"
    46 
    47         android:inputType="textPassword"/>
    48 
    49     <CheckBox
    50 
    51         android:id="@+id/cb_reme"
    52 
    53         android:layout_width="wrap_content"
    54 
    55         android:layout_height="wrap_content"
    56 
    57         android:text="@string/cb_reme_text"
    58 
    59         />
    60 
    61     <Button
    62 
    63         android:id="@+id/btn_login"
    64 
    65         android:layout_width="wrap_content"
    66 
    67         android:layout_height="wrap_content"
    68 
    69         android:text="@string/btn_login_text"/>
    70 
    71 </LinearLayout>

     FileUtils.java代码:

     1 package cn.csc.utils;
     2 
     3 import java.io.FileOutputStream;
     4 
     5 public class FileUtils {
     6 
     7       public static boolean writeFile(String data){
     8 
     9            String path = "/data/data/cn.csc.file/userinfo.txt";
    10 
    11            try {
    12 
    13                  FileOutputStream fos = new FileOutputStream(path);
    14 
    15                  fos.write(data.getBytes());
    16 
    17                  fos.close();
    18 
    19                  return true;
    20 
    21            } catch (Exception e) {
    22 
    23                  // TODO Auto-generated catch block
    24 
    25                  e.printStackTrace();
    26 
    27                  return false;
    28 
    29            }
    30 
    31       }
    32 
    33 }

     MainActivity.java:

     1 public class MainActivity extends Activity {
     2 
     3       private EditText et_username;
     4 
     5       private EditText et_password;
     6 
     7       private CheckBox cb_reme;
     8 
     9       private Button btn_login;
    10 
    11       @Override
    12 
    13       protected void onCreate(Bundle savedInstanceState) {
    14 
    15         super.onCreate(savedInstanceState);
    16 
    17         setContentView(R.layout.main_layout);
    18 
    19         et_username = (EditText) findViewById(R.id.et_username);
    20 
    21         et_password = (EditText) findViewById(R.id.et_password);
    22 
    23         cb_reme = (CheckBox) findViewById(R.id.cb_reme);
    24 
    25         btn_login = (Button) findViewById(R.id.btn_login);
    26 
    27         btn_login.setOnClickListener(new OnClickListener() {
    28 
    29                  @Override
    30 
    31                  public void onClick(View v) {
    32 
    33                       // TODO Auto-generated method stub
    34 
    35                       String username = et_username.getText().toString();
    36 
    37                       String password = et_password.getText().toString();
    38 
    39                       boolean checked = cb_reme.isChecked();
    40 
    41                       if(TextUtils.isEmpty(username)||TextUtils.isEmpty(password)){
    42 
    43                             Toast.makeText(MainActivity.this, "用户名密码不能为空",Toast.LENGTH_SHORT).show();
    44 
    45                             return;
    46 
    47                       }
    48 
    49                       if(checked){
    50 
    51                             if(username.equals("dqrcsc")&&password.equals("abcdef")){
    52 
    53                                   if(FileUtils.writeFile(username+"#"+password)){
    54 
    55                                        Toast.makeText(MainActivity.this, "保存成功",Toast.LENGTH_SHORT).show();
    56 
    57                                   }
    58 
    59                                   else{
    60 
    61                                        Toast.makeText(MainActivity.this, "保存失败",Toast.LENGTH_SHORT).show();
    62 
    63                                   }
    64 
    65                                   Toast.makeText(MainActivity.this, "登录成功",Toast.LENGTH_SHORT).show();
    66 
    67                             }else{
    68 
    69                                   Toast.makeText(MainActivity.this, "登录失败",Toast.LENGTH_SHORT).show();
    70 
    71                             }
    72 
    73                            
    74 
    75                       }
    76 
    77                  }
    78 
    79            });
    80 
    81     }
    82 
    83 }

     运行结果:

     

     注意到该文件的权限,默认仅能被当前用户读写。

    保存用户名,密码成功,还需要实现下次打开时,自动读取保存的用户名密码,对应的方法可以在onCreate()中调用,已实现打开时自动加载。

    在FileUtils中添加读取文件的方法:

     1 public static String readFile(){
     2 
     3            String path = "/data/data/cn.csc.file/userinfo.txt";
     4 
     5            try {
     6 
     7                  BufferedReader br = new BufferedReader(new FileReader(path));
     8 
     9                  String line = br.readLine();
    10 
    11                  br.close();
    12 
    13                  return line;
    14 
    15            } catch (Exception e) {
    16 
    17                  // TODO Auto-generated catch block
    18 
    19                  e.printStackTrace();
    20 
    21            }
    22 
    23            return null;
    24 
    25 }

     修改MainActivity.java中onCreate(),添加如下代码:

     1      String info = FileUtils.readFile();
     2 
     3         if(TextUtils.isEmpty(info)){
     4 
     5               return;
     6 
     7         }
     8 
     9         if(!info.contains("#")){
    10 
    11               return;
    12 
    13         }else{
    14 
    15               String[] strs = info.split("#");
    16 
    17             et_password.setText(strs[1]);
    18 
    19             et_username.setText(strs[0]);
    20 
    21             cb_reme.setChecked(true);
    22 
    23         }

    上述直接操作写死的文件路径,是存在问题的,如果我修改了当前应用的包名,然后去操作写死的文件,就会出现权限被拒绝的错误:

    如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中添加对原有读写文件方法的重载:

     1 public static boolean writeFile(Context context, String data){
     2 
     3            File filesDir = context.getFilesDir();
     4 
     5            try {
     6 
     7                  FileOutputStream fos = new FileOutputStream(new File(filesDir, "userInfo.txt"));
     8 
     9                  fos.write(data.getBytes());
    10 
    11                  fos.close();
    12 
    13                  return true;
    14 
    15            } catch (Exception e) {
    16 
    17                  // TODO Auto-generated catch block
    18 
    19                  e.printStackTrace();
    20 
    21                  return false;
    22 
    23            }
    24 
    25       }
    26 
    27       public static String readFile(Context context){
    28 
    29            File filesDir = context.getFilesDir();
    30 
    31            try {
    32 
    33                  BufferedReader br = new BufferedReader(new FileReader(new File(filesDir, "userInfo.txt")));
    34 
    35                  String line = br.readLine();
    36 
    37                  br.close();
    38 
    39                  return line;
    40 
    41            } catch (Exception e) {
    42 
    43                  // TODO Auto-generated catch block
    44 
    45                  e.printStackTrace();
    46 
    47            }
    48 
    49            return null;
    50 
    51 }

    修改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中的两个文件读写方法:

     1 public static boolean writeFile(Context context, String data){
     2 
     3            try {
     4 
     5                  FileOutputStream fos = context.openFileOutput("userInfo.txt", Context.MODE_PRIVATE);
     6 
     7                  fos.write(data.getBytes());
     8 
     9                  fos.close();
    10 
    11                  return true;
    12 
    13            } catch (Exception e) {
    14 
    15                  // TODO Auto-generated catch block
    16 
    17                  e.printStackTrace();
    18 
    19                  return false;
    20 
    21            }
    22 
    23       }
    24 
    25       public static String readFile(Context context){
    26 
    27            try {
    28 
    29                  BufferedReader br = new BufferedReader(new InputStreamReader(context.openFileInput("userInfo.txt")));
    30 
    31                  String line = br.readLine();
    32 
    33                  br.close();
    34 
    35                  return line;
    36 
    37            } catch (Exception e) {
    38 
    39                  // TODO Auto-generated catch block
    40 
    41                  e.printStackTrace();
    42 
    43            }
    44 
    45            return null;
    46 
    47       }

     修改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中添加两个方法:

     1 public static boolean writeCache(Context context, String data){
     2 
     3            File filesDir = context.getCacheDir();
     4 
     5            try {
     6 
     7                  FileOutputStream fos = new FileOutputStream(new File(filesDir, "userInfo.txt"));
     8 
     9                  fos.write(data.getBytes());
    10 
    11                  fos.close();
    12 
    13                  return true;
    14 
    15            } catch (Exception e) {
    16 
    17                  // TODO Auto-generated catch block
    18 
    19                  e.printStackTrace();
    20 
    21                  return false;
    22 
    23            }
    24 
    25       }
    26 
    27       public static String readCache(Context context){
    28 
    29            File filesDir = context.getCacheDir();
    30 
    31            try {
    32 
    33                  BufferedReader br = new BufferedReader(new FileReader(new File(filesDir, "userInfo.txt")));
    34 
    35                  String line = br.readLine();
    36 
    37                  br.close();
    38 
    39                  return line;
    40 
    41            } catch (Exception e) {
    42 
    43                  // TODO Auto-generated catch block
    44 
    45                  e.printStackTrace();
    46 
    47            }
    48 
    49            return null;
    50 
    51       }

     在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() 根目录

    测试上述方法输出:

    1               Log.i("Environment","getDataDirectory:"+Environment.getDataDirectory().getPath());
    2 
    3                        Log.i("Environment","getDownloadCacheDirectory:"+Environment.getDownloadCacheDirectory().getPath());
    4 
    5                        Log.i("Environment","getExternalStorageDirectory:"+Environment.getExternalStorageDirectory().getPath());
    6 
    7                       Log.i("Environment","getRootDirectory:"+Environment.getRootDirectory().getPath());
    8 
    9                       Log.i("Environment","getExternalStorageState:"+Environment.getExternalStorageState());

     

    修改FileUtils添加两个读写SD卡的方法:

     1 public static boolean writeSD(String data){
     2 
     3            try {
     4 
     5                  String state = Environment.getExternalStorageState();
     6 
     7                  if(state.equals(Environment.MEDIA_MOUNTED)){
     8 
     9                       File ext = Environment.getExternalStorageDirectory();
    10 
    11                       FileOutputStream fos = new FileOutputStream(new File(ext,"userInfo.txt"));
    12 
    13                       fos.write(data.getBytes());
    14 
    15                       fos.close();
    16 
    17                       return true;
    18 
    19                  }
    20 
    21                  return false;
    22 
    23                 
    24 
    25            } catch (Exception e) {
    26 
    27                  // TODO Auto-generated catch block
    28 
    29                  e.printStackTrace();
    30 
    31                  return false;
    32 
    33            }
    34 
    35 }
    36 
    37       public static String readSD(){
    38 
    39            try {
    40 
    41                  String state = Environment.getExternalStorageState();
    42 
    43                  if(state.equals(Environment.MEDIA_MOUNTED)){
    44 
    45                       File ext = Environment.getExternalStorageDirectory();
    46 
    47                       BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(ext.getPath()+"/userInfo.txt")));
    48 
    49                       String line = br.readLine();
    50 
    51                       br.close();
    52 
    53                       return line;
    54 
    55                  }
    56 
    57            } catch (Exception e) {
    58 
    59                  // TODO Auto-generated catch block
    60 
    61                  e.printStackTrace();
    62 
    63            }
    64 
    65            return null;
    66 
    67 }

     此外,写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卡的权限:

    1 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    2 
    3 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  • 相关阅读:
    SelectionKey理解
    redis3.0.3集群搭建
    Centos6.5环境下安装SVN 整合Apache+SSL
    没有注册类 。已加载,但找不到入口点 DllRegisterServer
    今日立秋
    35+开启忙而有序的日子
    jmeter的常用函数
    jmeter之java请求
    生成Webservice客户端的4种方法
    Pytest高级进阶之Fixture
  • 原文地址:https://www.cnblogs.com/dqrcsc/p/4630291.html
Copyright © 2011-2022 走看看