




已阅读5页,还剩14页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Android官方开发教程中文版维信科技保存数据保存数据大部分Android应用都需要保存数据,即使只是在onPause()期间不让用户进度丢失而保存状态信息。大部分有意义的App还需要保存用户设置,某些App必须管理大量来自于文件和数据库的信息(维信科技提供)。本课程介绍Android中保存数据的主要方式。包括:l 简单数据保存到参数文件中的键值对。l 保存到Android文件系统的任意文件中。l 使用SQLite数据库管理。保存键值对集合如果你有一个想保存的、相对较小的键值对集合,你应用使用SharedPreferences API。SharedPreferences对象指向一个包含了键值对的文件并提供一些简单的方法以读写它们。每个SharedPreferences文件由框架管理,可以设为私有或共享。本课程展示如何使用SharedPreferences API储存相对简单的数据。注意:SharedPreferences API只读写键值对,不要把它和Preference API混淆,Preference API是为App设置构建用户界面(尽管在界面中使用SharedPreferences API来做为保存设置数据的实现)。有关Preference的更多信息,请参见“设置”指南。获取SharedPreferences的句柄你可以创建新的参数文件或通过以下两个方法之一访问一个存在的参数文件:l getSharedPreferences()如果你需要从由名称标识的多个共享参数文件中,用方法的第一个参数指定一个,那么用这个。你可以从App的任意上下文中调用它。l getPreferences()如果你需要为Activity使用仅有的参数文件,那么用这个。因为这会检索Activity默认的共享参数文件,你不需要提供名称。例如,下面的代码在一个Fragment中执行,它访问了一个由资源字符串R.string.preference_file_key标识的参数文件,并且用私有模式打开它,因此这个文件只能由你的App访问(维信科技提供)。Context context = getActivity();SharedPreferences sharedPref = context.getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE);命名你的共享参数文件时,应该使用唯一的、可识别的名称。比如“com.example.myapp.PREFERENCE_FILE_KEY”。或者,如果你的Activity就一个共享参数文件,你可以使用getPreference()方法:SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);警告:如果你MODE_WORLD_READABLE或MODE_WORLD_WRITEABLE创建共享参数文件,那么任何知道文件标识符的其它App都可以访问你的数据。写入共享参数写入共享参数文件,用SharedPreferences对象的edit()方法创建一个SharedPreference.Editor对象。把你要写入的键和值传递给putInt()或putString()之类的方法,然后调用commit()方法提交改变。如:SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);SharedPreferences.Editor editor = sharedPref.edit();editor.putInt(getString(R.string.saved_high_score), newHighScore);mit();读取共享参数调用getInt()或getString()之类的方法从共享参数文件中检索值,提供一个你想要得到值的键,以及一个可选的、当键不存在时返回的默认值。如:SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);int defaultValue = getResources().getInteger(R.string.saved_high_score_default);long highScore = sharedPref.getInt(getString(R.string.saved_high_score), defaultValue);保存文件Android使用的文件系统类似其它平台上的基于磁盘的文件系统。本节内容描述了File API如何工作于Android文件系统以及读写文件。File对象适合按从头到尾的、不跳过任何内容的顺序,读写大量数据。例如,图像文件或任何通过网络交换的数据(维信科技提供)。本节内容展示了如何在你的App中执行文件相关的基本任务。本节课假设你熟悉基本的Linux文件系统以及Java.io中标准的文件输入输出API。选择内部或外部存储所有Android系统都有两个文件存储区:“内部”或“外部”。这些叫法来自早期Android,大部分Android设备提供了内建的、非易失性的存储器(内部储存),加上一个可移动的存储介质如微型SD卡(外部储存)。某些设备将永久储存空间划分为“内部”和“外部”分区,因此,就算没有可移动存储介质,也同样有两种存储空间和API行为,无论外部储存是否可移动。下表是对两种储存空间的总结:内容存储:l 总是可用的。l 存放在这里的文件默认情况只能被你的App访问。l 当用户卸载你的App时,系统会在内部存储中删除所有你的App的文件。内部存储区最好用来存放你即不想让用户,也不想让其它App访问的文件。外部存储:l 不总是可用的,用户可以把外部存储器做为USB设备插入,也可以从设备上移除。l 它是“全世界可读的”(公开的),文件存放在这里会在你控制之外被读取。l 当用户卸载你的App时,只有保存在通过getExternalFilesDir()方法获得的目录中的App文件才会被系统删除。外部存储区最好用来存放不需要限制访问,或是你想共享给其它App,或是允许用户通过电脑来访问的文件。提示:虽然App默认情况下是安装在内部存储区的,但你可以在清单文件中指定android:installLocation属性让你的App安装在外部存储区上。用户会喜欢这个选项的,当APK的大小非常大,而用户有一个比内部存储区大得多的外部存储区时。详情请参考“应用安装位置”。获得外部存储区权限要写入外部存储区,你必须在清单文件中请求WRITE_EXTERNAL_STORAGE权限: .警告:目前,所有的App都可以读取外部存储区而不需要指定权限。无论如何,这将在未来的版本中改变。如果你的App需要读取外部存储区(但不写入),你需要声明READ_EXTERNAL_STORAGE权限(维信科技提供)。要确保你的应用如期望中的那样继续工作,在更改生效之前,你应该立即声明这个权限。把文件保存到内部存储区中不需要任何权限,应用程序任何时候在自己的内部存储区目录中都有权限读写文件。把文件保存到内部存储区当把文件保存到内部存储区时,你可以通过下列两个方法之一获得文件对应的目录:getFilesDir()为App返回表示内部目录的文件对象getCacheDir()为App的临时缓存文件返回表示内部目录的文件对象。确保文件不再使用时删除它,并且在任何时候都为它执行一个合理的大小限制,比如1MB。如果系统检查到存储空间过低,它会删除你的缓存文件而不做任何警告。在这些目录中创建新的文件,你可以使用File()构造函数,传递通过上面的方法之一提供的,指定了内部存储目录的文件对象。如:File file = new File(context.getFilesDir(), filename);或者,你也可以调用openFileOutput()获得一个FileOutputStream来写入到你的内部目录中的文件。如:String filename = myfile;String string = Hello world!;FileOutputStream outputStream;try outputStream = openFileOutput(filename, Context.MODE_PRIVATE); outputStream.write(string.getBytes(); outputStream.close(); catch (Exception e) e.printStackTrace();又或者,如果你需要缓存某些文件,你应该使用createTempFile()(维信科技提供)。下例中的方法从URL中提取了文件名,并用这个文件名在内部缓存目录中创建了一个文件:public File getTempFile(Context context, String url) File file; try String fileName = Uri.parse(url).getLastPathSegment(); file = File.createTempFile(fileName, null, context.getCacheDir(); catch (IOException e) / Error while creating file return file;注意:App的内部存储目录是在Android文件系统的特定位置由App的包名指定的。从技术上说,如果你把文件设置为可读,其它App可以读取你的内部文件。但无论如何,其它App还需要知道你的包名和文件名,其它App不能浏览你的内部目录也没读写权限,除非你显示地把文件设为可读或可写(维信科技提供)。所以只你对内部存储的文件使用MODE_PRIVATE,它们永远不可能被其它App访问。把文件保存到外部存储区因为外部存储区可能是不可用的(比如用户把存储器安装到电脑上,或把提供外部存储的SD卡移除了),你应该总是在访问前验证卷(意指磁盘的卷)是否允许。你可调用getExternalStorageState()方法查询外部存储区的状态,如果返回状态值是MEDIA_MOUNTED,那么你可以读写文件。下例的方法可用于判断外部存储是否可用:/* 检查外部存储是否允许读写 */public boolean isExternalStorageWritable() String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) return true; return false;/* 检查外部存储是否可读 */public boolean isExternalStorageReadable() String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state) | Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) return true; return false;尽管外部存储可以被用户和其它App更改,你可以在上面保存两类文件:公共文件:文件对用户和其它App是自由的,当用户卸载你的App时,这些文件允许用户保留。例如,你的App拍摄的相片或其它下载的文件。私有文件:文件当然属于你的App,用户卸载App时会删除它们。由于它们在外部存储区上,这些文件从技术上说可以被用户和其它App访问,但实际上不会为你的App之外的用户提供价值。当用户卸载你的App时,系统删除所有外部私有目录中的文件。如被你的App或临时媒体文件下载的额外资源。如果你想在外部存储区中保存公共文件,使用getExternalStoragePublicDirectory()方法获得表示外部存储区正确目录的文件对象。这个方法有一个指定要保存的文件类型的参数,这样它们可以和其它公共文件逻辑上组织到一起,比如DIRECTORY_MUSIC或DIRECTORY_PICTURES(维信科技提供)。如:public File getAlbumStorageDir(String albumName) / 获得用户公共的图片目录 File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs() Log.e(LOG_TAG, Directory not created); return file;如果你想把文件保存为App私有的,你可调用getExternalFilesDir()方法并且给它传递一个表示目录类型的名称。这种方式创建的目录添加到父目录中,并封装了所有App的外部存储文件,当用户卸载App时,它们被系统删除。例如,下面的方法可以用来创建个人相册的目录:public File getAlbumStorageDir(Context context, String albumName) / 获得App的私有图片目录 File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (!file.mkdirs() Log.e(LOG_TAG, Directory not created); return file;如果没预定义的子目录名称适合你的文件,你可以调用getExternalFilesDir()方法传递null值来代替,这会获得外部存储区中App私有目录的根目录。记住getExternalFilesDir()是在一个目录里面创建一个目录,当用户卸载App时,这个目录会被删除(维信科技提供)。如果App卸载后保存的文件仍然保留可用(比如你的App是一个相机应用,并且用户希望能保留相片),你应该用getExternalStoragePublicDirectory()代替它。无论App是为共享文件而使用getExternalStoragePublicDirectory()还是为私有文件而使用getExternalFilesDir(),重要的是你使用API常量提供的目录名,如DIRECTORY_PICTURES。这些目录名能确保文件被系统正确地对待。例如,文件保存在DIRECTORY_RINGTONES将被系统媒体扫描分类为铃声而不是音乐。查询可用空间如果你提前知道要保存多少数据,你可以调用getFreeSpace()或getTotalSpace()获得是否有足够的空间供你保存而不会产生IOException。这些方法分别提供了存储卷上当前可用空间和总空间。对避免填充存储卷超过一定阈值,这些信息也是有用的。无论如何,系统并不能保证你可以写入和getFreeSpace()指明的一样多的字节。如果返回的数量比你想要保存的数据大小多几个MB,或者文件系统已用的空间小于90%,那么你的继续可能是安全的。否则,你可能不应该写入存储区。注意:你不需要在保存文件之前检查可有空间数量,你可以试着立即写入文件,然后捕获IOException(维信科技提供)。如果你不知道需要多少空间,你可以这么做。比如,把PNG图片转换成JPEG图片,在保存之前改变了文件编码,你无法预先知道文件大小。删除文件你应该总是在不需要文件的时候把它删除,多数情况下,直接通过打开的文件引用调用delete()方法:myFile.delete();如果文件储存在内部存储区,你也可以请求Context调用deleteFile()来定位和删除文件:myContext.deleteFile(fileName);注意:当用户卸载你的App时,Android系统会删除:1. 所有保存在内部存储区的文件。2. 所有用getExternalFilesDir()保存的外部存储区的文件。无论如何,你应该定期手动删除所有通过getCacheDir()创建的缓存文件,以及定期删除其它不再需要的文件。在SQL数据库中保存数据把重复或结构化的数据保存到数据库中是理想的。这节课假设你已经熟悉一般的SQL数据库,并帮助你开始了解Android系统中的SQLite数据库。你需要用到的数据库API在android.database.sqlite包中。定义架构和合约数据库的主要原则之一是架构:数据库是如何组织的正式声明。架构被体现在你用来创建数据库的SQL语句中。你可能会发现创建一个同伴类(称为合约类)是很有用的,它以系统化和自我文档化的方式明确规定架构布局。合约类是一个为URI、表、列定义名称的常量容器。合约类允许你在同一个包中交差使用同一个常量。这可以让你在一个地方修改列名而影响你所有的代码。组织合约类的好办法是把数据库的全局定义放在类的根级别上,然后为每个表创建内部类来列举它的列。注意:实现BaseColumns接口,你的内部类会继承一个主键字段:_ID,某些Android类,如游标适配器可能会期望有它。这不是必需的,但这可以帮助你的数据库更好地工作在Android框架中。下面的代码片断为单表定义了表名和列名:public final class FeedReaderContract / 防止有人意外地实例化合约类 / 给它一个空的构造函数 public FeedReaderContract() /* 定义表的内部类 */ public static abstract class FeedEntry implements BaseColumns public static final String TABLE_NAME = entry; public static final String COLUMN_NAME_ENTRY_ID = entryid; public static final String COLUMN_NAME_TITLE = title; public static final String COLUMN_NAME_SUBTITLE = subtitle; . 使用SQL Helper创建数据库定义数据库之后,你应该实现创建与维护数据库和表的方法,下面是一些典型的创建和删除表的语句:private static final String TEXT_TYPE = TEXT;private static final String COMMA_SEP = ,;private static final String SQL_CREATE_ENTRIES = CREATE TABLE + FeedEntry.TABLE_NAME + ( + FeedEntry._ID + INTEGER PRIMARY KEY, + FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + . / CREATE命令的其它选项 );private static final String SQL_DELETE_ENTRIES = DROP TABLE IF EXISTS + FeedEntry.TABLE_NAME;就象保存在内部存储区的文件一样,Android系统把数据库储存到应用程序关联的私有磁盘空间中。你的数据是安全的,因为默认情况下,其它应用程序不能访问这个区域(维信科技提供)。SQLiteOpenHelper类提供了一些有用的API。当你使用这个类得到对数据库的引用时,系统只在需要时并且不是App启动期间,才会执行可能需要较长时间的创建及更新数据库的操作。你要做的就是调用getWritableDatabase()和getReadableDatabase()方法。注意:因为它们可能需要较长时间运行,确保你在后台线程中调用getWritableDatabase()和getReadableDatabase(),如AsyncTask或IntentService。要使用SQLiteOpenHelper,创建它的子类并重写onCreate()、onUpdate()和onOpen()回调方法。你可能还想实现onDowngrade(),但是没必要。下例是使用了上面看到的某些命令实现的SQLiteOpenHelper子类:public class FeedReaderDbHelper extends SQLiteOpenHelper / 如果你改变了数据库架构,你必须升级数据库的版本 public static final int DATABASE_VERSION = 1; public static final String DATABASE_NAME = FeedReader.db; public FeedReaderDbHelper(Context context) super(context, DATABASE_NAME, null, DATABASE_VERSION); public void onCreate(SQLiteDatabase db) db.execSQL(SQL_CREATE_ENTRIES); public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) / 这个数据库仅是一个在线数据的缓存,所以更新策略 / 是简单地删除数据并重新开始 db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) onUpgrade(db, oldVersion, newVersion); 要访问你的数据库,实例化SQLiteOpenHelper子类:FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext();把信息放入数据传递一个ContentValues对象给insert()方法,把数据插入到数据库:/ 获取可写模式的数据库对象SQLiteDatabase db = mDbHelper.getWritableDatabase();/ 创建值的映射,用列名做键ContentValues values = new ContentValues();values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);values.put(FeedEntry.COLUMN_NAME_TITLE, title);values.put(FeedEntry.COLUMN_NAME_CONTENT, content);/ 插入新行并返回新行的主键值long newRowId;newRowId = db.insert( FeedEntry.TABLE_NAME, FeedEntry.COLUMN_NAME_NULLABLE, values);insert()方法的第一个参数是表名,第二个参数是当ContentValues为空时,框架可以插入null值的列名(如果这个参数用“null”替代,那么当没有数据时,框架不会插入新行)。从数据库中读取信息使用query()方法从数据库中读取,查询条件和输出列做为参数传递给它。除了用想要获取的数据列名列表替代要插入的数据之外,这个方法组合了insert()和update()方法的元素(维信科技提供)。查询结果通过游标(Cursor)对象返回。SQLiteDatabase db = mDbHelper.getReadableDatabase();/ 声明projection 变量指定哪些列出现在查询结果中/ you will actually use after this query.String projection = FeedEntry._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_UPDATED, . ;/ 如何对游标结果集进行排序String sortOrder = FeedEntry.COLUMN_NAME_UPDATED + DESC;Cursor c = db.query( FeedEntry.TABLE_NAME, / The table to query projection, /
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025福建三明尤溪县总医院医学人才校园专场公开招聘5人考前自测高频考点模拟试题及答案详解(各地真题)
- 2025年度周口西华县人民医院校园招聘33人模拟试卷及参考答案详解一套
- 2025年临沂莒南县教体系统部分事业单位公开招聘教师(1名)考前自测高频考点模拟试题及答案详解(必刷)
- 2025湖南株洲市茶陵县卫生健康局所属事业单位就业见习岗位招聘10人模拟试卷及答案详解参考
- 二手房交易资金监管协议6篇
- 2025广东广州市中山大学孙逸仙纪念医院肿瘤科放疗专科科研助理招聘1人考前自测高频考点模拟试题及答案详解(考点梳理)
- 2025福建福州文教职业中专学校招聘1人考前自测高频考点模拟试题及答案详解(新)
- 2025年福建省福州市罗源县招聘教师40人模拟试卷及一套完整答案详解
- 2025广西玉林市北流生态环境局招聘公益性岗位模拟试卷附答案详解(典型题)
- 2025广东惠州市博罗县罗浮山文化旅游投资集团有限公司所属企业管理岗位遴选拟聘用模拟试卷附答案详解(考试直接用)
- 2023年石油天然气集团公司固井技术规范试行
- 物业经理竞聘演讲稿课件
- 《老年护理学》教学大纲全套
- 静脉用药安全输注药护专家指引
- 绘本IntotheAmazonRainforest(课件)译林版英语六年级上册
- 全国高中数学联赛
- 动画概论教程课件 第10章 动画视听语言
- GB/T 18742.2-2017冷热水用聚丙烯管道系统第2部分:管材
- 犯罪概念及犯罪构成课件
- 人教版培智学校生活数学一年级上册认识1课件
- DBJ53-T-40-2011 云南省城镇园林工程施工质量验收规程
评论
0/150
提交评论