美文网首页
Android数据存储(一)

Android数据存储(一)

作者: 不会敲代码的好代码 | 来源:发表于2018-07-27 13:30 被阅读11次

    一、SharedPreferences

    SharePreferences是用来存储一些简单配置信息的一种机制,使用Map数据结构算法与数据结构知识库")来存储数据,以键值对的方式存储,采用了XML格式将数据存储到设备中。例如保存登录用户的用户名和密码。只能在同一个包内使用,不能在不同的包之间使用,其实也就是说只能在创建它的应用中使用,其他应用无法使用。

    适用范围:保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等),解锁口令密码等

    核心原理:保存基于XML文件存储的key-value键值对数据,通常用来存储一些简单的配置信息。通过DDMS的File Explorer面板,展开文件浏览树,很明显SharedPreferences数据总是存储在/data/data/<package name>/shared_prefs目录下。

    SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现。 SharedPreferences本身是一 个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, int mode)方法来获取SharedPreferences实例,该方法中name表示要操作的xml文件名,mode参数具体如下:

    Context.MODE_PRIVATE: 指定该SharedPreferences数据只能被本应用程序读、写。
    
    Context.MODE_WORLD_READABLE:  指定该SharedPreferences数据能被其他应用程序读,但不能写。
    
    Context.MODE_WORLD_WRITEABLE:  指定该SharedPreferences数据能被其他应用程序读,写
    

    Editor有如下主要重要方法:

    SharedPreferences.Editor clear():清空SharedPreferences里所有数据
    
    SharedPreferences.Editor putXxx(String key , xxx value): 向SharedPreferences存入指定key对应的数据,其中xxx 可以是boolean,float,int等各种基本类型据
    
    SharedPreferences.Editor remove(): 删除SharedPreferences中指定key对应的数据项
    
    boolean commit(): 当Editor编辑完成后,使用该方法提交修改
    

    提交数据

    //步骤1:获取输入值
    String string= editText.getText().toString().trim();
    //步骤2-1:创建一个SharedPreferences.Editor接口对象,name表示要写入的XML文件名,MODE_WORLD_WRITEABLE写操作
    SharedPreferences.Editor editor = getSharedPreferences("name", MODE_WORLD_WRITEABLE).edit();
     //步骤2-2:将获取过来的值放入文件
    editor.putString("name", string);
    //步骤3:提交
    editor.commit();
    

    获取数据

    //步骤1:创建一个SharedPreferences接口对象
     SharedPreferences read = getSharedPreferences("name", MODE_WORLD_READABLE);
    //步骤2:获取文件中的值
     String value = read.getString("name", "");
    

    读写其他应用的SharedPreferences: 步骤如下

    1、在创建SharedPreferences时,指定MODE_WORLD_READABLE模式,表明该SharedPreferences数据可以被其他程序读取.
    2、创建其他应用程序对应的Context:
    Context pvCount = createPackageContext("com.xxx.app", Context.CONTEXT_IGNORE_SECURITY);这里的com.xxx.app就是其他程序的包名
    3、使用其他程序的Context获取对应的SharedPreferences
    SharedPreferences read = pvCount.getSharedPreferences("name", Context.MODE_WORLD_READABLE);
    4、如果是写入数据,使用Editor接口即可,所有其他操作均和前面一致。

    SharedPreferences对象与SQLite数据库相比,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。但是SharedPreferences也有其自身缺陷,比如其只能存储boolean,int,float,long和String五种简单的数据类型,比如其无法进行条件查询等。所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。


    二、文件存储

    核心原理: Context提供了两个方法来打开数据文件里的文件IO流 FileInputStream openFileInput(String name); FileOutputStream(String name , int mode),这两个方法第一个参数 用于指定文件名,第二个参数指定打开文件的模式。具体有以下值可选:

    MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,如果想把新写入的内容追加到原文件中。可 以使用Context.MODE_APPEND
    MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
    MODE_WORLD_READABLE:表示当前文件可以被其他应用读取;
    MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入。

    除此之外,Context还提供了如下几个重要的方法:

    getDir(String name , int mode):在应用程序的数据文件夹下获取或者创建name对应的子目录
    File getFilesDir():获取该应用程序的数据文件夹得绝对路径
    String[] fileList():返回该应用数据文件夹的全部文件               
    

    核心代码

    /**
         * 判断内部存储还是外部存储,带SD卡使用外部存储,不带SD卡使用内部存储。
         */
        public File getFileDir(Context context, String uniqueName) {
            String cachePath;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                    || !Environment.isExternalStorageRemovable()) {
                cachePath = context.getExternalFilesDir(uniqueName).getPath();
            } else {
                cachePath = context.getFilesDir().getPath();
            }
            return new File(cachePath);
        }
    
    

    写文件操作

    public void write(String msg){
            // 步骤1:获取输入值
            if(msg == null) return;
            try {
                // 步骤2:创建一个FileOutputStream对象,MODE_APPEND追加模式
                FileOutputStream fos = openFileOutput("message.txt",MODE_APPEND);
                // 步骤3:将获取过来的值放入文件
                fos.write(msg.getBytes());
                // 步骤4:关闭数据流
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    读取文件操作

    public String read() {
            try {
                FileInputStream fis= this.openFileInput("message.txt");
                byte[] buffer = new byte[1024];
                int hasRead = 0;
                StringBuilder sb = new StringBuilder();
                while ((hasRead = inStream.read(buffer)) != -1) {
                    sb.append(new String(buffer, 0, hasRead));
                }
                fis.close();
                return sb.toString();
            } catch (Exception e) {
                e.printStackTrace();
            } 
            return null;
        }
    

    三、ContentProvider存储

    ContentProvider的作用是为别的应用调用本应用中的数据或者文件提供接口,而它也是唯一的跨应用数据传递的接口。如果仅仅是同一个应用中的数据传递,则完全没有必要使用到自定义的ContentProvider。

    另一方面,虽然ContentProvider也能组织文件数据或者SharedPreferences(其实也是文件数据)这种数据,但大多数情况下ContentProvider是作为SQLite数据库的调用接口来被继承的。其原因大概是在于重写的query()方法始终需要返回Cursor,而Cursor作为数据库数据的容器,并没有提供直接往Cursor中写入数据的方法。

    实现步骤

    1. 创建一个数据源,例如继承SQLiteOpenHelper创建一个SQLite数据库;
    2. 创建一个继承自ContentProvider的类,并重写insert、delete、query、update、getType、onCreate方法,在这些方法中实现对数据源的操作;
    3. 在AndroidManifest.xml文件中添加<provider>标签,两个必写的属性是android:name和android:authorities;
    4. 在本应用或者其它应用的Activity、Service等组件中使用ContentResolver通过对应的URI来操作该自定义ContentProvider。

    注意

    1. onCreate() 默认执行在主线程,别做耗时操作,query() 也最好异步执行
    2. 上面的 4 个增删改查操作都可能会被多个线程并发访问,因此需要注意线程安全

    URI

    ContentProvider 使用 URI 标识要操作的数据,这里的内容 URI 主要包括两部分:

    authority:整个提供程序的符号名称
    path:指向表的名称/路径

    内容 URI 统一的形式就是:

    content://authority/path

    注意

    形如content://com.xxx.MyProvider/phone/1
    com.xxx.MyProvider表示authority,是AndroidManifest.xml文件中<provider>标签的android:authorities属性值,或者是远程数据源的主机名,必需;
    phone/1表示path,是数据源路径,非必需,其中的phone对于数据库来说可以视为表名,1表示的是该条数据的编号,如果没有则一般认为是返回当前路径(当前表)中的所有数据。

    当调用 ContentResolver 方法来访问 ContentProvider 中的表时,需要传递要操作表的 URI。
    在通过 ContentResolver 进行数据请求时(比如 contentResolver.insert(uri, contentValues);), 系统会检查指定 URI 的 authority 信息,然后将请求传递给注册监听这个 authority 的 ContentProvider 。这个 ContentProvider 可以监听 URI 想要操作的内容,Android 中为我们提供了 UriMatcher 来解析 URI。

    UriMatch对象

    该类主要是是对传入的Uri进行匹配,确保传来的Url对ContentProvider确实可以处理的。

    1. 通过new UriMatcher(UriMatcher.NO_MATCH); 实例化,常量NO_MATCH作为参数表示不匹配任何URI;
      该类主要提供了如下两个方法:

    2. 实例化后调用addURI(String authority, String path, int code)方法注册URI,该方法有三个参数,分别需要传入URI字符串的authority部分、path部分以及自定义的整数code三者;

    3. 在其它地方调用match(Uri uri) 方法匹配相应的URI,需要传入Uri作为唯一的参数,返回上述自定义的code值。根据前面注册的Uri返回其对应的标识码,如果在UriMatcher中找不到对应的Uri则返回-1。

    ContentUris类

    这个类是一个操作Uri字符串的工具类,主要是拼接Uri字符串用,它有两个方法:
    public static Uri withAppendedId(Uri uri, long id) 用于为路径加上id部分
    例如:

    Uri  uri = Uri.parse("content://com.lzb.provide.myContentProvide:200/students");
    Uri newUri = ContentUris.withAppendedId(uri,2);
    

    那么 newUri 就变成了content://com.lzb.provide.myContentProvide:200/students/2
    public static long parseId(Uri uri) 用于从指定的Uri中解析出所包含的id
    例如上述newUri解析出来的ID为2。

    ContentResolver类

    通过调用Content的getContentResolver()方法获取ContentResolver对象实例,其实ContentResolver的作用类似于HttpClient,获取对象后就可以根据Uri对应用的数据进行CRUD操作了。ContentResolver有如下方法:

     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
    说明:根据Uri查询出select条件所匹配的全部记录,projection表示一个列名列表,表明只选择指定的数据列
    public Uri insert(Uri uri, ContentValues values)
    说明:根据该Uri插入values对应的数据
    public int delete(Uri uri, String selection, String[] selectionArgs)
    说明:根据Uri,删除selection条件匹配全部记录。
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
    说明:根据Uri,更新selection条件匹配的
    
    ContentProvider、Uri、ContentResolver之间的关系

    相关文章

      网友评论

          本文标题:Android数据存储(一)

          本文链接:https://www.haomeiwen.com/subject/sgjwmftx.html