Android提供了多种方式(XML文件方式、文件方式、数据库方式等)进行数据的存储。开发者可以根据数据类型和设计的需要,选择适当的存储方式进行数据存储。
1.XML文件管理
XML文件更多的是用来维护应用或系统的配置文件。在Android中,goole提供了SharedPreferences这个轻型的存储类作为XML文件存储的上层接口,其本质上就是<ket, value>值对。
根据配置信息是否对其他应用开放,SharedPreferences提供了MODE_PRIVATE.、MODE_WORLD_READABLE两种权限。SharedPreferences的操作分为获取配置信息和存储配置信息两种方式。下面是获取配置信息的方法:
SharedPreferences settings=getSharedPreferences(PREFS_NAME,MODE_PRIVATE);
boolean silent=settings.getBoolean("silentMode", false);
存储配置信息和获取配置信息略有不同,下面是存储配置信息的方法:
SharedPreference settings=getShatedPreferences(PREFS_NAME, MODE_PRIVATE);
SharedPreference.Editor editor=settings.edit(); //获得编辑器
editor.putBoolean("silentMode", mSilentMode);
editor.commit(); //提交
如果希望配置信息对其他应用开发,在设置权限时,可使用MODE_WORLD_READABLE。在其他应用希望获取相应的配置信息时,必须先获取相应的上下文,方法如下:
context=createPackageContext("com.miaozl.text", Context.CONTEXT_IGNORE_SECURITY);
if(context != null){
SharedPreference settings=context.getSharedPreference(PREFS_NAME,Context.MODE_WORLD_READABLE);
mTest=settings.getString("text", null);
}
需要说明的是,在PreferenceActivity中内置了对SharedPreferences的支持。
如果希望ListPreference保存或查看当前的选择,可以调用的ListPreferences方法如下:
public void setValue(String value) //对应“android:entries”属性的值
public void setValueIndex(int index) //对应“android:entryValues”属性的值
public String getValue()
其他的Preference的操作方法类似于ListPreference。
2.内部文件管理
对于二进制数据,Android提供了内部存储的方式,开发者可以将数据存储在应用的私有空间中,避免其他程序的访问,内部存储的数据会在应用卸载时删除。
内部存储的实现非常简单,其权限包括MODE_PRIVATE、MODE_APPEND、MODE_WORLD_READABLE、MODE_WORLD_WRITEABLE等。
内部存储所在目录为\data\data\com.company.packagename\files。如果文件名为wors,其内部存储的目录为\data\data\com.android.mms\files\words。
(1)写入数据
写入字符串数据的方法:
FileOutputStream out=context.openFileOutput(file,Context.MODE_WORLD_WRITEABLE);
out.write(captureArray.toString().getBytes());
out.close();
写入结构体数据的方法:
private static void writeConfiguration(Context context, LocaleConfiguration configuration){
DataOutputStream out = null;
out=new DataOutputStream(context.openFileOutput(PREFERENCES, MODE_PRIVATE));
out.writeUTF(configuration.locale);
out.writeInt(configuration.mcc);
out.writeInt(configuration.mnc);
out.flush();
}
(2)读取数据
读取字符串数据的方法:
StringBuilder sb=new StringBuilder();
FileInputStream words=context.openFileInput("word");
int c;
while((c=words.read()) != -1){
if(c=='\r' || c=='\n'){
String word=sb.toString().trim();
if(word.length() > 0){
mWord.add(word);
}
sb.setLength(0);
}else{
sb.append((char)c);
}
}
words.close();
mWordCount=mWords.size();
读取数据结构数据的方法:
private static void readConfiguration(Context context, LocaleConfiguration configuration){
DataInputStream in=null;
in=new DataInputStream(context.openFileInput(PREFERENCES));
configuration.locale=in,readUTF();
configuration.mcc=in.readInt();
configuration.mnc=in.readInt();
in.close();
对于应用携带的静态数据,可以放置在应用的assets目录或res、raw目录下。
对于assets目录下的静态数据,存在单个文件最大仅支持1MB的局限,读取方式:InputStream is=getAssets().open("read_asset.txt");
对于res、raw目录下的静态数据,读取方式:InputStream.inputStream=resources.openRawResource(R.raw.definitions);
Android还对创建缓存提供了支持,通过getCacheDir()可以获取应用的缓存路径。在系统可用空间不足时,Android会清空缓存,但对于开发者而言,不应维护过多的缓存。和内部存储一样,在应用卸载时,会清空并删除缓存目录。
3.外部文件管理
为了便于进行媒体文件的扫描,Android为媒体文件设置了特定目录,目前Android支持DIRECTORY_MUSIC、DIRECTORY_PODCASTS、DIRECTORY_RINGTONES、DIRECTORY_ALARMS、DIRECTORY_NOTIFICATIONS、DIRECTORY_PICTURES、DORECTORY_MOVIES、DIRECTORY_DOWNLOADS等目录。
Android开始支持在SD卡上的应用私有目录,通过getExtenalFilesDir()可以获取具体路径。该具体路径依赖应用的包名,如com.miaozl.helle,其SD卡上的应用私有目录为\mnt\sdcard\Android\data\com.miaozl.hello\files\。下面是获取图片路径的方法:File path=getExternalFilesDir(Environment.DIRECTORY_PICTURES)。
如果希望存储在SD上的公共目录下,则可通过getExternalStoragePublicDirectory可以获得公共目录,但需要注意,公共目录的具体路径视需要存储的文件的类型而定。下面是获取公共目录的方法:
String sdroot=Environment.getExternalStorageDirectory().getAbsolutePath();
String dwndir=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
SD上的应用私有目录和内部存储一样,在应用卸载时,将会被清空并删除。
如果应用需要的缓存较大,也可以在SD卡上建立缓存目录,通过getExternalCacheDir()可以获取应用在SD卡上的缓存路径。
在使用SD卡目录时,需要注意SD卡是否挂载,可通过Environment.getExternalStorageState()方法进行判断。如果返回值为Environment.MEDIA_MOUNTED,表示SD卡处于挂载状态,可以放心使用。
4.数据库管理
通过数据库的方式可以存储复杂的数据,也可以方便对数据的操作,因此数据库存储在Android中非常有用。目前,Android采用SQLite3数据库。SQLite3数据库是一种适合嵌入式平台的轻量级数据库。但是开源版本的SQLite3没有对数据进行加密保护,因此存储敏感数据是一个十分头疼的问题。好在SQLite3在框架层提供了加密算法的接口,有需要的开发者可以自行添加相关的加密算法。
Android提供了两种SQLite的实现方法,一种是基于Android封装接口来实现的,适合对数据库要求不高或对SQLite不熟悉的开发者;另一种是基于原生SQLite的方法来实现的,可以完全发挥出SQLite的能力,性能稍好,但对SQLite的理解要求较高。至于采用何种实现方法,开发者可以根据应用场和自己对SQLite的理解程度自行把握。
对于数据读取频繁的场景,必须考虑SQLite的性能,在Android平台上,使用原始SQL语句的执行效率要比封装过的ContentProvider高;对于一次性进行多项处理的场景,应考虑通过事务处理的方式进行批量处理。
(1)Android封装接口
考虑到数据库的操作很频繁,在Android中提供了封装类,即SQLiteOpenHelper,通过集成SQLiteOpenHelper,开发者可以很容易地设计和操作数据库。需要注意的是,封装会使Android的性能降低。
在继承SQLiteOpenHelper时,必须实现onCreate和onUpgrade方法。下面是通过Android封装接口设计数据库的一般步骤:定义数据库名、数据库版本号、数据表名。实现onCreate和onUpgrade方法。实现插入、更新、删除和查询方法。
实现onCreate方法:
public void onCreate(SQLiteDatabase db){
db.execSQL("CREATE TABLE "+NOTES_TABLE_NAME+"(" + NoteColumns._ID+ " INTEGER PRIMARY KEY,"+NoteColumns.TITLE+" TEXT,"
+NoteColumns.NOTE+" TEXT,"+NoteColumns.CREATED_DATE+" INTEGER," +NoteColumns.MODIFIED_DATE+" INTEGER"+");");
}
实现onUpgrade方法:
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
事务处理的相关方法:
public void begintransaction()
public void beginTransactionWithListener(SQLiteTransactionListener transactionListener)
public void endTransaction()
public boolean inTreansaction()
public void setTransactionSuccessful()
(2)原生方法处理
采用raw方法来操作SQLite数据库,可以减少代码和提高处理效率,但必须做好SQL语句异常处理。
SQLiteDatabase db;
String args[]={id};
ContextValue cv=new ContextValues();
cv.put("miaozl", id);
Cursor c=db.rawQuery("SELECT * FROM table WHERE miaozl =?", args); //执行本地SQL语句
if(c.getCount() !=0){
ContextValues cv=new ContextValues();
cv.put("miaozl","cwj");
db.insert("table", "miaozl", cv); //插入数据
String args[]={id};
ContextValues cv2=new ContextValues();
cv2.put("miaozl", id);
db.delete("table", "miaozl=?", args);
5.数据处理
进行Android开发,尤其是开发涉及网络的应用时,对数据进行处理是个无法回避的问题。Android采用的是Java数据结构,且基本支持所有的Java数据结构。在本节中,除了讨论基本的Java数据类型外,还将讨论正则表达式、I/O管理、流、JSON等,这些内容对从C/C++迁移过来的开发者非常有用。Java数据结构之间的关系如下图:
Collection和Map是最基本的数据结构。
所有实现Collection接口的类都必须提供两个标准的构造函数:一个是无参数的构造函数,用于创建空白的Collection;另一个是Collection作为参数的构造函数,用于创建一个新的Collection,这个新的Collecntion与传入的Collection有相同的元素。第二个构造函数允许用户复制一个Collection。
遍历Collection可以通过iterator方法进行,该方法返回一个迭代因子,利用该迭代因子可逐一访问Collection中的元素。典型用法如下:
Iterator it=collection.iterator(); //获得一个迭代因子
while(it.hasNext()){
Object obj=it.next(); //得到下一个元素
}
由Collection接口派生的两个接口是List和Set。
(1)Set类
Set类保持的对象不可重复,Android目前支持的Set类主要包括HashSet、TreeSet、LinkedHashSet等。
1)HashSet
HashSet实现了Set接口和内置哈希表,它不保证集合的迭代顺序。
向HashSet中添加集合项的方法:
public void addChild(BuildStep child){
if(children == null){
children=new HashSet<BuildStep>();
}
children.add(child)
}
HashSet还支持清空、克隆、移除等操作,另外,HashSet还支持从资源文件中加载数组,具体如下:
HashSet<String>defaultCorpora=new HashSet<String>();
try{
String[] corpora=mContext.getResource().getStringArray(res);
for(String corpus : corpora){
defaultCorpora.add(corpus);
}
return defaultCorpora;
}
HashSet不支持线程安全,其集合项可以为null。
2)TreeSet
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排列状态。它是J2EE中唯一可实现自动排序的类,TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。向TreeSet中添加集合项的方法:
TreeSet smallSet=new TreeSet(); //自然排序
for(int i=0; i<50;i++){
smallSet.add(objArray[i]);
}
对于定制排序的情况,需要先构建一个Comparator类型的排序器,方法如下:
public class MyComparator<T> implements Comparator<T>{
public int compare(T arg0, Targ1){
if(arg0.equal(arg1)){
return 0;
}
return ((Comparable<T> arg0).compare(arg1)*-1;
}
}
TreeSet<String> treeSet=new TreeSet<String>(myConparator);
treeSet.add("A");
3)LinkedHashSet
和HashSet相比,LinkedHashSet中的数据严格按照插入的顺序存放,新增的数据放置在集合的末尾。LinkedHashSet内部通过LinkedHashMap实现,添加集合项的方法:
LinkedHashSet<String>ids = new LinkedHashSet<String>();
ids.add(TimeZone.getDefault().getID());
另外LinkedHashSet还支持清空、复制、移除等操作。具体方法如下:
void clear();
Object clone();
boolean remove(Object object);
LinkedHashSet还支持遍历操作,方法如下:
LinkedHashSet<String>addresses=new LinkedHashSet<String>;
Iterator<String> addressIterator=addresses.iterator();
while(addressIterator.hasNext()){
String assress=addressIterator.next();
addressIterator.remove();
}
LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是在进行插入曹组时,性能方面稍逊于HashSet。
(2)Map类
Map类保存着<key,value>的对应关系。Android目前支持的Map类主要有HashMap、LinkedHashMap和WeakHashMap等。
1)HashMap
HashMap的实现是基于哈希表的,创建HashMap非常简单,方法:Map<String, Boolean>entries=new HashMap<String, Boolean>();
如果希望在创建HashMap对象时声明HashMap的容量,方法:Map<AttributedCharcterIterator.Attribute, List<Range>>attributeMap=new HashMap<Attribute,List<Range>>(11);
考虑到迭代时间和HashMap的容量和大小成比例,HashMap的初始容量不易设置太大,毕竟当哈希表中的键值对数量超过HashMap的加载因子与HashMap的容量的乘积时,HashMap会自动扩展容量,不会出现容量不够的问题。在默认情况下,HashMap的加载因子为0.75。在创建HashMap对象时,可以自定义HashMap的加载因子。
向HashMap中添加键值对的方法:
private final Map<AudioStream, Integer> mStream;
mStreams=new HashMap<AudioStream, Integer>();
if(!mStreams.containsKey(stream)){ //判断key为“stream”的映射是否存在
int socket=stream.dup();
mStreams.put(stream,socket);
}
获得HashMap中键值对信息的方法:
private final HashMap<String, Integer> mMap;
Set<Entry<String, Integer>>mEntries=mMap.entrySet();
for(Entry<String, Integer>entry:mEntries){
Integer freg=entry.getValue();
db.delete(AUTODICT_TABLE_NAME, COLUMN_WORD+ "=? AND " + COLUMN_LOCALE+"=?", new String[]{entry.getKey(), mLocale});
if(freq!=null){
db.insert(AUTODICY_TABLE_NAME, null, getContentValues(entry.getKey(),freq, mLocale));
}
}
HashMap中的另外几个重要方法:
public boolean containsValue(Object value)
public int size()
public boolean isEmpty()
V get(Object key)
2)LinkedHasMap
与HashMap相比,LinkedHashMap维护的是一个具有双重链表的HashMap,LinkedHashMap输出时其键值对是有顺序的,而HashMap输出时其键值对是随机的。对于键值对比较复杂而又要求效率的情况下,最好使用LinkedHashMap。LinkedHashMap支持两种排序方式,一种是插入排序,最近插入的在链表尾部;一种是使用排序,最近使用的会移至链表尾部。
private static final float DEFAULT_LOAD_FACTOR=.75F;
private final Map<String, AddressCacheEntry> map;
map=new LinkedHashMap<String, AddressCacheEntry>(0, DEFAULT_LOAD_FACTOR, true); //true表示使用排序,false表示插入排序
synchronized(map){
map.put(hostname, new AddressCacheEntry(addresses, expiryNanos));
}
注意,LinkedHashMap并非线程安全的,如果确需实现线程安全,那么可以使用如下方法:
Map<String, String>map=Colleations.synchronizedMap(new LinkedHashMap<String, String>)
3)WeakHashMap
WeakHashMap和普通HashMap的不同之处在于其包含的键值对的Key为弱引用,当Key不再被其他对象引用时,相应的键值对可能会被垃圾回收器随时释放。
WeakHashMap对象的创建方法:private final WeakHashMap<String, Package>mPackages=new WeakHashMap<String, Package>();
WeakHashMap具有的方法和HashMap类似,两者都继承于AbstractMap的。
(3)list类
List类存储对象可以重复。Android目前支持的List类主要有ArrayList、LinkedList等。
1)ArrayList
ArrayList是Android中最常用的集合类,本质上是可变数据的实现,允许包括null在内的所有元素。由于插入数据会涉及数组元素的移动,因此所有ArrayList进行数据索引较快,而插入数据较慢。
ArrayList<THread>list =new ArrayList<Thread>();
for(Node p=tail; p!=null; p=p.prev){
Thead t=p.thread;
if(t!= null)
list.add(t)
}
ArrayList也是非线程安全的,和ArrayList类似的一个工具是Vector,所不同的是Vector是线程安全的,其性能比ArrayList稍差。Stack继承自Vector,是一个后进先出的栈。
2)LinkedList
LinkedList利用双向链表实现存储,其插入数据较快,索引数据较慢。
(4)流类
Android对流的支持和Java类似,主要有BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter、ByteArrayInputStream、ByteArrayOutputStream、FileInputStream、FileOutputStream、StringReader、StringWriter等。
1)缓冲流
考虑到硬盘存取的速度远低于内存中数据存取速度,为了减小对硬盘的存取,在内存中建立了缓存区,这样可以提高存取的效率。这就是BufferedInputStream、BufferedOutputStream优于InputStream、OutputStream之处。
BufferedInputStream和BufferedOutputStream并非改变InputStream和OutputStream等行为,具体的读写仍有InputStream和OutputStream负责。
下面是BufferedOutputStream的一个示例:
try{
BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream("\sdcard\a.text"));
for(int i=0; i<200;i++)
out.write(i);
out.flush(); //写入目标对象
}
2)字节流
在创建实例时ByteArrayInputStream和ByteArrayOutputStream会构建一个byte类型的数组作为缓冲区,以适合传输多个变量的场景。下面是一个示例:
CopyOnWriteArrayList q=populatedArray(SIZE);
ByteArrayOutputStream bout=new ByteArrayOutputStream(10000);
ObjectOutputStream out=new ObjectOutputStream(new BufferedOutputStream(bout));
out.writeObject(q);
out.close();
ByteArrayInputStream bin=new ByteArrayInputStream(bout.toByteArray());
ObjectArrayInputStream in=new ObjectInputStream(new BufferedInputStream(bin));
CopyOnWriteArrayList r=(CopyOnWriteArrayList)in.readObject();
3)文件流
FileInputStream和FileOutputStream专门用于文件读取,以字节为单位进行流处理,与编码无关,所以不会存在乱码问题。FileReader和FileWriter则以字符为单位进行流处理。下面是一个文件复制的实现:
File inFile =new File("source.jpg");
File outFile=new File("dest.jpg");
FileInputStream inStream=new FileInputStream(inFile);
FileOutputStream outStream=new FileOutputStream(outFile);
byte[] inOutb=new byte[inStream.available()];
inStream.read(inOutb); //读入流,保存在byte数组
outStream.write(inOutb); //写入流,保存在文件dest.jpg中
inStream.close();
outStream.close();
byte数组的最大存储容量为64MB,当一个文件超过64MB时,需要分多个流进行读写。另外,当在两个不同的线程间进行文件读写时,同步会变得比较困难,这时可以考虑运用管道,即PipedInputStream、PipedOutputStream等。
当程序对读写的速率要求较高时,FileChannel会是比FileInputStream/FileOutputStream更好的选择,FileChannel是线程安全的。
当需求对内容进行分析时,就需要用到FileReader和FileWriter,他们经常和BufferedReader、BufferedWrite组合使用。FileReader和BufferedReader组合使用的示例如下:
BufferedReader input = new BufferedReader(new FileReader(layoutFile));
String line;
while((line=input.readLine())!=null){
}
FileWriter和BufferedWriter组合使用的示例如下:
BufferedWriter out=null;
try{
out=new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
int pin=(int)Math.floor(Math,random()*10000);
mDockPin=String.format("%04d",pin);
out.write(mDockPin);
return true;
}
4)字符串流
StringReader和StringWriter是专门针对字符串设计得。StringReader多用于XML的解析的场景。StringReader的用法如下:
private static final String XMLFILE=”<feed xmlns='http://www.w3.org/2005/Atom‘><name><id>bob</id></name>"
+"<entry1><id year=\"2009\">james</id></entry1><entry2 year=\"2000\"><id>jim</id></entry2>"
+"<name><id>tom</id></name><name><id>brett</id></name></feed>";
SAXParserFactory spfactory=SAXPareserFactory.newInstance();
SAXPareser saxPareser=spfactory.newSAXPareser();
XMLReader xmlReader=saxPareser.getXMLReader();
xmlReader.setContentHandle(root.getContentHander());
InputSource source=new InputSource(new StringReader(XMLFILE));
xmlReader.parse(source);
StringWriter的用法如下:
Therowable thrown=r.getThrown();
StringWriter sw=new StringWriter();
PrintWriter pw=new PrinrWriter(sw);
sw.write(r.getMessage());
sw.write("\n");
throw.printStackTrace(pw);
pw.flush();
return sw.toString();
5)正则表达式
在Android中使用正则表达式非常简单,相关的类主要有Pattern、Matcher等。对读者来说,最重要的是了解正则表达式的语法规则。下面是几个常用场景的实现
String str=“ceponline@yahoo.com.cn";
Pattern pattern=Pattern.compile("[\\w\\.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+",Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(str);
替换字符的方式实现:
Matcher m=Pattern.compile("[\t\n],").matcher(string);
string = m.replaceAll(".").raplace('\n', ' ').replace('\n',' ');
查找字符串的实现:
private static final Pattern sRepeatedRangePattern=Pattern.compile("Resource entry (.+) already has bag item (.+)\\.");
matcher=sRepeatedRangePattern.matcher(message);
if(matcher.find()){
String property=matcher,group(2);
return findRange(file, line,property, null);
}
(6)I/O管理
除了流类型的I/O管理和文件管理外,Android还提供了MemoryFile、FileChannel进行I/O管理。MemoryFile是对Ashmem驱动的封装器,而Ashmem本质上是基于共享内存的。在dev目录下对应的设备为\dev\ashmem。相比于传统的内存分配机制,如malloc,Ashmem的好处是提供了辅助内核内存回收算法的pin\unpin机制,使通过MemoryFile进行I/O操作可以在很大程度上提高效率。
FileChannel是MMAP的一个上层封装。利用FilehChannel可以更高效地读写文件,而且FileChannel是线程安全的。
1)MemoryFile
创建一个MemoryFile对象即可在共享内存上分配一块区域。通过MemoryFile对象读写数据的方式有流、字节两种。
MemoryFile在断言、SQLite等多个领域的读写得到了应用。
2)FileChannel
开发者可以从FileInputStream、FileOutputStream、RandomAccessFile等的对象中获得FileChannel对象,FileChannel是线程安全的。从FileInputStream中得到FileChannel的方法如下:
FileChannel fc=new FileInputStream(new File("temp.tmp")).getChannel();
IntBuffer ib=fc.map(FileChannel.MapMode.READ_ONLY,0,fc.size()).asIntBuffer();
while(ib.hasRemaining())
ib.get();
fc.close();
从FileOutputStream、RandomAccessFile等对象获得FileChannel对象的方法和FileInputStream类似。另外在FileChannel中读写数据都是基于缓冲进行的,读取数据的方法如下:
FileChannel fc=new FileInputStream("demo.tex").getChannel();
ByteBuffer buff=ByteBuffer.allocate(BSIZE);
buff.clear();
fc.read(buff);
buff.flip();
在FileChannel中写入数据的方法如下:
FileOutputStream fileOutputStream=new FileOutputStream(tmpFile);
FileChannel fileChannel=fileOutputStream.getChannel();
ByteBuffer byteBuffer=ByteBuffer.allocatedirect(BUFFER_LENGTH);
byteBuffer.clear(); //向byteBuffer中写入数据
fileChannel.write(byteBuffer);
fielChannel.Close();
fileOutputStream.close();
FileChannel还支持在两个通道间传输,方法如下:
FileChannel in=new FileInputStream("source.txt").getChannel();
FileChannel out=new FileOutputStream("dest.txt").getChannel();
in.transferTo(0, in.size(), out);
需要说明的是,FileChannel.MapMode支持3中类型的映射模式,即READ_ONLY、PRIVATE和READ_WRITE。
(7)JSON实现
在网络编程中,数据的传递非常重要,JSON(JavaScript Object Notation)和XML各有优点,XML扩展性好,JSON语法简单。JSON支持两种数据结构,一种是数组,即值得有序列表;另一种是对象,即键值对的集合。
JSON中有一些特殊字符需要注意,如单引号、正斜杠、反斜杠、换行符等。
Android对JSON的支持比较全面,以前Android通过org.json包提供了对JSON的支持,但功能不够强大,使用起来十分不便。目前,Android引入了JsonWrite、JsonReader两个类,极大地简化了操作。
1)写入数据
JsonWrite本质上是一种输出流,在写入对象数据时,必须以beginObject方法开始,以endObject方法结束;在写入数组数据时,必须以beginArray方法开始,以endArray方法结束。
JsonWriter不支持线程安全。
2)读取数据
JsonReader本质上是一种输入流,在读取对象数据时,必须以beginObject方法开始,以endObject方法结束;读取数组数据时,必须以beginArray方法开始,以endArray方法结束。