Android数据存储(六)

Android五种主要存储方式:共享参数SharedPreferences、数据库SQLite、SD卡文件、App的全局内存。

一、共享参数SharedPreferences:

       1.共享参数的基本用法

          SharedPreferences是Android的一个轻量级存储工具,采用的存储结构是Key-Value的键值对方式。

           共享参数的存储介质是符合XML规范的配置文件。

保存路径是:/data/data/应用包名/shared_prefs/文件名.xml

下面是一个共享参数的XML文件示例:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>

          <map>    

                   <string name="name">Mr Lee</string>    

                   <int name="age" value="30" />    

                  <boolean name="married" value="true" />    

                 <float name="weight" value="100.0" />

         </map>

2.SharedPreferences的使用场景

        共享参数主要适用于如下场合:

        (1)简单且孤立的数据。若是复杂且相互间有关的数据,则要保存在数据库中。

         (2)文本形式的数据。若是二进制数据,则要保存在文件中。

          (3)需要持久化存储的数据。在App退出后再次启动时,之前保存的数据仍然有效。

       实际开发中,共享参数经常存储的数据有App的个性化配置信息、用户使用App的行为信息、临时需要保存的片段信息等。 下面是使用共享参数的代码例子:

Android数据存储(六)

3.实现记住密码功能

        (1)声明一个SharedPreferences对象,并在onCreate函数中调用getSharedPreferences方法对该对象进行初始化操作。             (2)登录成功时,如果用户勾选了“记住密码”,就使用共享参数保存手机号码与密码。

          (3)在打开登录页面时,App从共享参数中读取手机号码与密码,并展示在界面上。

Android数据存储(六)

实现“记住密码”的界面效果

 

Android数据存储(六)

二、数据库SQLite

    1.SQLite的基本用法

        SQLite是一个小巧的嵌入式数据库,使用方便、开发简单,手机上最早由iOS运用,后来Android也采用了SQLite。

    使用SQLite需要注意以下几点:

         (1)建表时为避免重复操作,应加上IF NOT EXISTS关键词。

          (2)删表时为避免重复操作,应加上IF EXISTS关键词。

          (3)添加新列时使用ALTER TABLE table_name ADD COLUMN ...。

          (4)在SQLite中,ALTER语句每次只能添加一列。

          (5)SQLite不支持布尔类型。可通过整型数用0表示false,1表示true。

          (6)SQLite建表时需要一个唯一标识字段,字段名为_id。

          (7)条件语句等号后面的字符串值要用单引号括起来。

  2.数据库管理器SQLiteDatabase(上)

          SQLiteDatabase是SQLite的数据库管理类,它提供了若干操作数据表的API,常用的方法有3类:

          1). 管理类,用于数据库层面的操作。

                            openDatabase:打开指定路径的数据库。

                             isOpen:判断数据库是否已打开。

                             close:关闭数据库。

                             getVersion:获取数据库的版本号。

                             setVersion:设置数据库的版本号。

   3.数据库管理器SQLiteDatabase(下)

            2).事务类,用于事务层面的操作。

                   beginTransaction:开始事务。

                   setTransactionSuccessful:设置事务的成功标志。

                   endTransaction:结束事务。

             3). 数据处理类,用于数据表层面的操作。

                    execSQL:执行拼接好的SQL控制语句。

                    delete:删除符合条件的记录。

                    update:更新符合条件的记录。

                    insert:插入一条记录。

                    query:执行查询操作,返回结果集的游标。

                    rawQuery:执行拼接好的SQL查询语句,返回结果集的游标。

三、数据库帮助器SQLiteOpenHelper

           SQLiteOpenHelper是Android提供的数据库辅助工具,用于指导开发者进行SQLite的合理使用。

 1.SQLiteOpenHelper的具体使用步骤如下:、           

                (1)新建一个继承自SQLiteOpenHelper的数据库操作类,提示重写onCreate和onUpgrade两个方法。

                (2)封装保证数据库安全的必要方法,包括以下三种。 获取单例对象:

                                  确保App运行时数据库只被打开一次,避免重复打开引起错误。

                         打开数据库连接:

                                   读连接可调用SQLiteOpenHelper的getReadableDatabase方法获得,写连接可调用getWritableDatabase获得。

                         关闭数据库连接:

                                    数据库操作完了,调用SQLiteDatabase对象的close方法关闭连接。

                 (3)提供对表记录进行增加、删除、修改、查询的操作方法。

  2.游标Cursor(上)

            调用SQLiteDatabase的query和rawQuery方法时,返回的都是Cursor对象,因此获取查询结果要根据游标的指示一条一条遍历结果集合。

            Cursor的常用方法可分为3类:

            1). 游标控制类方法,用于指定游标的状态。

                    close:关闭游标。 

                    isClosed:判断游标是否关闭。

                    isFirst:判断游标是否在开头。

                    isLast:判断游标是否在末尾。

  3.游标Cursor(中)

            2). 游标移动类方法,把游标移动到指定位置。

                    moveToFirst:移动游标到开头。

                    moveToLast:移动游标到末尾。

                    moveToNext:移动游标到下一条记录。

                     moveToPrevious:移动游标到上一条记录。

                     move:往后移动游标若干条记录。

                     moveToPosition:移动游标到指定位置的记录。

 4.游标Cursor(下)

             3). 获取记录类方法,可获取记录的数量、类型以及取值。

                      getCount:获取结果记录的数量。

                      getInt:获取指定字段的整型值。

                       getFloat:获取指定字段的浮点数值。

                       getString:获取指定字段的字符串值。

                       getType:获取指定字段的字段类型。

 5.优化记住密码功能

                 利用共享参数实现记住密码,只能记住一个用户的登录信息,并且手机号码跟密码不存在从属关系,如果换个手机号              码登录,前一个用户的登录信息就被覆盖了。

                 真正的记住密码功能是先输入手机号码,然后根据手机号匹配保存的密码,一个密码对应一个手机号码,从而实现具               体手机号码的密码记忆功能。

                 运用SQLite技术分条存储不同用户的登录信息,并提供根据手机号码查找登录信息的方法,这样可以同时记住多个手               机号码的密码。

6.利用SQLite记住密码的步骤

                利用SQLite记住密码有以下三处改造:

                   (1)声明一个UserDBHelper对象,然后在活动页面的onResume方法中打开数据库连接,在onPasue方法中关闭数                据库连接

                    (2)登录成功时,如果用户勾选了“记住密码”,就使用数据库保存手机号码与密码在内的登录信息。

                    (3)再次打开登录页面,用户输入手机号完毕后点击密码输入框时,App到数据库中根据手机号查找登录记录,并                将记录结果中的密码填入密码框。

优化“记住密码”的界面效果

Android数据存储(六)

四、SD卡文件操作

    1 .SD卡的基本操作

          手机的存储空间一般分为两块,一块用于内部存储,另一块用于外部存储(SD卡)。

           获取手机上的SD卡信息通过Environment类实现,该类是App获取各种目录信息的工具,主要方法有以下7种。 

               getRootDirectory:获得系统根目录的路径。

               getDataDirectory:获得系统数据目录的路径。

               getDownloadCacheDirectory:获得下载缓存目录的路径。

               getExternalStorageDirectory:获得外部存储(SD卡)的路径。

                getExternalStorageState:获得SD卡的状态。

                getStorageState:获得指定目录的状态。

                getExternalStoragePublicDirectory:获得SD卡指定类型目录的路径。

  2.使用SD卡的权限配置

           为正常操作SD卡,需要在AndroidManifest.xml中声明SD卡的权限,具体配置如下:  

            <!-- SD卡读写权限 -->

           <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  

             <uses-permission android:name="android.permission.READ_EXTERNAL_STORAG" />  

            <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

3.  公有存储空间与私有存储空间

             Android把外部存储分成了两块区域,一块是所有应用均可访问的公共空间,另一块是只有应用自己才可访问的专享空间。

            Android在SD卡的“Android/data”目录下给每个应用又单独建了一个文件目录,用于给应用保存自己需要处理的临时文件。这个给每个应用单独建立的文件目录,只有当前应用才能够读写文件,其它应用是不允许进行读写的,故而“Android/data”目录算是外部存储上的私有空间。

            Android从7.0开始加强了SD卡的权限管理,App使用SD卡的公共控件需要在设置页面开启权限,使用私有空间无需另外设置权限。

4.获取公共空间和私有空间的存储路径

            获取公共空间的存储路径,调用的是Environment类的getExternalStoragePublicDirectory方法;获取应用私有空间的存储路径,调用的是getExternalFilesDir方法。

         代码如下:    

         // 获取系统的公共存储路径    

          String publicPath = Environment.getExternalStoragePublicDirectory(             Environment.DIRECTORY_DOWNLOADS).toString();  

        // 获取当前App的私有存储路径    

         String privatePath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();  

          TextView tv_file_path = findViewById(R.id.tv_file_path);    

          String desc = "系统的公共存储路径位于" + publicPath +    "\n\n当前App的私有存储路径位于" + privatePath;   

           tv_file_path.setText(desc);

5.文本文件读写

      文本文件的读写一般借助于FileOutputStream和FileInputStream。

          (1)FileOutputStream用于写文件。

           (2)FileInputStream用于读文件。 读写文本文件的具体代码参见storage模块的FileUtil.java的saveText方法和openText方法。

文本文件的读写效果

Android数据存储(六)

6.图片文件读写

        Android的图片处理类是Bitmap,App读写Bitmap可以使用性能更好的BufferedOutputStream和BufferedInputStream。

        读写图片文件的具体代码参见storage模块的FileUtil.java的saveImage方法和openImage方法。

        Android还提供了BitmapFactory工具用于读取各种来源的图片,相关方法如下:

               decodeStream:该方法从输入流中读取位图数据。

               decodeFile:该方法可将指定路径的图片读取到Bitmap对象。

               decodeResource:该方法可从资源文件中读取图片信息。

图片文件的读写效果

Android数据存储(六)

五、应用Application基础

  1. Application的生命周期

         Application是Android的一大组件,在App运行过程中有且仅有一个Application对象贯穿整个生命周期。

         在AndroidManifest.xml里面,activity节点的上级正是application节点。如果给application节点指定android:name属性,则表示App将运行自定义名称的Application代码。

         需要注意,Application的onCreate方法调用先于Activity的onCreate方法调用。但是,Application的onTerminate方法纯属摆设,永远不会被调用。

 2.利用Application操作全局变量

         全局的意思是其他代码都可以引用该变量,因此全局变量是共享数据和消息传递的好帮手。

         适合在Application中保存的全局变量主要有下面3类数据:

                 (1)会频繁读取的信息,如用户名、手机号等。

                  (2)从网络上获取的临时数据,为节约流量、减少用户等待时间,想暂时放在内存*下次使用,如logo、商品图片等。

                  (3)容易因频繁分配内存而导致内存泄漏的对象,如Handler对象等。

3.全局变量的实现

             Java代码可利用自定义Application的静态成员变量实现全局变量的功能。具体需要完成以下3项工作:

                   (1)写一个继承自Application的类MainApplication。该类要采用单例模式,内部声明自身类的一个静态成员对象,然后提供该静态对象的获取方法getInstance。

                   (2)在Activity中调用MainApplication的getInstance方法,获得MainApplication的一个静态对象,通过该对象访问MainApplication的公共变量和公共方法。

                    (3)不要忘了在AndroidManifest.xml中注册新定义的Application类名,即在application节点中增加android:name属性,值为.MainApplication。

六、内容提供与处理

   1. 内容提供器ContentProvider

          ContentProvider为App存取内部数据提供统一的外部接口,让不同的应用之间得以共享数据。

          SQLite操作的是应用自身的内部数据库,ContentProvider操作的是本设备其他应用的内部数据,使用内容提供器需要重写以下方法:

          onCreate:创建数据库并获得数据库连接。

           query:查询数据。

            insert:插入数据。

            update:更新数据。

             delete:删除数据。

             getType:获取数据类型。

  2.注册内容提供器组件

            既然内容提供器是四大组件之一,就得在AndroidManifest.xml中注册它的定义,并开放外部访问权限,注册代码如下:             <provider 

            android:name=".provider.UserInfoProvider"    

            android:authorities="com.example.storage.provider.UserInfoProvider"      

            android:enabled="true"          

            android:exported="true" />

注册完毕后就完成了服务端App的封装工作,接下来可由其他App进行数据存取。

  3.内容解析器ContentResolver

    利用ContentProvider只实现服务端App的数据封装,如果客户端App想访问对方的内部数据,就要通过内容解析器ContentResolver访问。

     内容解析器是客户端App操作服务端数据的工具,相对应的内容提供器是服务端的数据接口。

     ContentResolver提供的方法与ContentProvider是一一对应的,比如query、insert、update、delete、getType等方法

  4.利用内容解析器查询数据

       内容解析器通过query函数查询数据,调用该函数返回一个游标Cursor对象。

        下面是query方法的具体参数说明(依参数顺序排列)。

                     uri:Uri类型,可以理解为本次操作的数据表路径。

                     projection:字符串数组类型,指定将要查询的字段名称列表。

                     selection:字符串类型,指定查询条件。

                     selectionArgs:字符串数组类型,指定查询条件中的参数取值列表。

                     sortOrder:字符串类型,指定排序条件。 

  5.利用内容解析器插入数据

                     需要注意的是,使用ContentResolver每次只能插入一条记录,如果需要插入多条记录,那么一旦发生插入失败,已插入的数据将不会自动回滚。

                    为了避免这种情况的发生,Android提供了内容操作器ContentProviderOperation进行批量数据的处理,即在一个请求中封装多条记录的修改动作,然后一次性提交给服务端,从而实现在一个事务中完成多条数据的更新操作。

                   即使某条记录处理失败,ContentProviderOperation也能根据事务一致性原则自动回滚本事务已经执行的修改操作。

      6. 内容观察器ContentObserver

                   ContentResolver获取数据采用的是主动查询方式,有查询就有数据,没查询就没数据。

                   内容观察器ContentObserver给目标内容注册一个观察器,目标内容的数据一旦发生变化,观察器规定好的动作马上触发,从而执行开发者预先定义的代码。

                   下面是ContentResolver与观察器有关的方法说明。

                         registerContentObserver:注册内容观察器。

                         unregisterContentObserver:注销内容观察器。

                         notifyChange:通知内容观察器发生了数据变化。

   7.ContentObserver的应用例子

                       手机号码的每月流量限额一般由用户手动配置,但流量限额其实是由移动运营商指定的。

                       以中国移动为例,只要发送18到10086,就会收到运营商下发的流量短信,包括月流量额度、已使用流量、未使用流量等信息。 故而只需监控10086发送的短信内容,即可自动获取手机号码的月流量额度。

         流量校准的界面效果

             Android数据存储(六)