假如有如下需求,要求能够记录用户输入的用户名和密码,下次登录时,能直接获取之前保存的用户名密码,并在相应的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数据存储(一)文件读写的更多相关文章

  1. android菜鸟学习笔记18----Android数据存储(二)SharedPreferences

    数据存储的方式,有比直接文件读写更加简便的方式,那就是操作SharedPreferences. SharedPreferences一般用于存储用户的偏好设定,暂时不支持多进程操作. SharedPre ...

  2. android菜鸟学习笔记24----与服务器端交互(一)使用HttpURLConnection和HttpClient请求服务端数据

    主要是基于HTTP协议与服务端进行交互. 涉及到的类和接口有:URL.HttpURLConnection.HttpClient等 URL: 使用一个String类型的url构造一个URL对象,如: U ...

  3. android菜鸟学习笔记25----与服务器端交互(二)解析服务端返回的json数据及使用一个开源组件请求服务端数据

    补充:关于PHP服务端可能出现的问题: 如果你刚好也像我一样,用php实现的服务端程序,采用的是apache服务器,那么虚拟主机的配置可能会影响到android应用的调试!! 在android应用中访 ...

  4. android菜鸟学习笔记23----ContentProvider(三)利用内置ContentProvider监听短信及查看联系人

    要使用一个ContentProvider,必须要知道的是它所能匹配的Uri及其数据存储的表的结构. 首先想办法找到访问短信及联系人数据的ContentProvider能接受的Uri: 到github上 ...

  5. android菜鸟学习笔记21----ContentProvider(一)ContentProvider的简单使用

    ContentProvider是Android四大组件之一,它用来封装数据,并通过ContentResolver接口将数据提供给其他应用.只有当需要在多个应用之间共享数据时才会用到ContentPro ...

  6. android菜鸟学习笔记16----Android项目打包安装过程(Run as Android Application)

    右击项目名称,Run as Android Appication之后,Android项目打包安装过程: 1.打包生成.apk文件: 1)把源码中的.java文件编译生成.class文件 2)将所有的. ...

  7. android菜鸟学习笔记14----Android控件(三) ListView的简单使用

    MVC模式: MVC的基本原理就是通过Controller连接View和Model.当View中所显示的数据发生变化时,会通知Controller,然后由Controller调用Model中的相关方法 ...

  8. android菜鸟学习笔记10----Intent及<intent-filter>

    关于Bundle: 注意到Activity的onCreate()方法的签名是protected void onCreate(Bundle savedInstanceState),其参数是一个Bundl ...

  9. android菜鸟学习笔记31----Android使用百度地图API(二)获取地理位置及地图控制器的简单使用

    1.获取当前地理位置: Android中提供了一个LocationManager的类,用于管理地理位置.不能通过构造函数获取该类的实例,而是通过Context的getSystemService(): ...

随机推荐

  1. js上传Excel文件

    一.问题 需要在项目里添加一个上传excel文件的功能,因为其他同样的后台里面有上传文件的功能,第一反应就是想着直接用.了解了一下发现它是利用bootstrap的fileinput实现的,但是我怎么都 ...

  2. Network| ICMP

    Internet Control Message Protocol,ICMP是网路协议族的核心协议之一.它用于TCP/IP网络中发送控制消息,提供可能发生在通信环境中的各种问题反馈,通过这些信息,令管 ...

  3. Write a function that generates one of 3 numbers according to given probabilities

    You are given a function rand(a, b) which generates equiprobable random numbers between [a, b] inclu ...

  4. springboot 2.0.8 跳转html页面

    springboot项目创建链接 https://blog.csdn.net/q18771811872/article/details/88126835 springboot2.0 跳转jsp教程 h ...

  5. OBS插件开发以及OBS插件的选择(obs直播插件)研究思路

    obs版本的选择: 工作室版,优化了很多东西,缺点是不能用插件,在部分机型不稳定,因为更新的很频繁.不过这个插件不能用的说法还是停留在早起,截至到今天已经完美支持,所以在不久的将来会越来越好,如果是开 ...

  6. 任务驱动,对比式学习.NET开发系列之开篇------开源2个小框架(一个Winform框架,一个Web框架)

    一 源码位置 1. Winform框架 2. web框架 二 高效学习编程的办法 1 任务驱动方式学习软件开发 大部分人学习软件开发技术是通过看书,看视频,听老师上课的方式.这些方式有一个共同点即按知 ...

  7. 记录下我的阿里云centos服务器之路

    以下内容都已经过试验,边走边记,懒得排版 安装aphach yum install -y httpd systemctl start httpd netstat -tulp    安装桌面 尽量不用桌 ...

  8. 记一个发HTML格式邮件的问题

    很早做了一个自动发邮件的程序,前一向发现Notes升级后反而CSS样式都没有了. 起初不以为意,反正格式数据在,客户也没说啥,后来觉得这样的态度要不得,小洞不补,大洞吃苦. 于是查查资料,发现浏览器里 ...

  9. 浅谈PHP与手机APP开发即API接口开发

    API(Application Programming Interface,应用程序接口)架构,已经成为目前互联网产品开发中常见的软件架构模式,并且诞生很多专门API服务的公司,如:聚合数据(http ...

  10. 【LeetCode】Search in Rotated Sorted Array——旋转有序数列找目标值

    [题目] Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7 ...