更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680
本篇文章将从以下几个内容来阐述数据传输与序列化:
所以涉及到数据传输或者存储的类,严格意义上来说都要加上序列化ID,这也是一种良好的编程习惯
。/**
* Interface for classes whose instances can be written to
* and restored from a {@link Parcel}. Classes implementing the Parcelable
* interface must also have a non-null static field called CREATOR
* of a type that implements the {@link Parcelable.Creator} interface.
*
* A typical implementation of Parcelable is:
*
*
* public class MyParcelable implements Parcelable {
* private int mData;
*
* public int describeContents() {
* return 0;
* }
*
* public void writeToParcel(Parcel out, int flags) {
* out.writeInt(mData);
* }
*
* public static final Parcelable.Creator CREATOR
* = new Parcelable.Creator() {
* public MyParcelable createFromParcel(Parcel in) {
* return new MyParcelable(in);
* }
*
* public MyParcelable[] newArray(int size) {
* return new MyParcelable[size];
* }
* };
*
* private MyParcelable(Parcel in) {
* mData = in.readInt();
* }
* }
*/
通过源码中的介绍 可以知道,Parcelable接口的实现类是可以通过Parcel写入和恢复数据的,并且必须要有一个非空的静态变量 CREATOR,而且还给了一个例子,这样我们写起来就比较简单了,但是简单的使用并不是我们的最终目的,通过查看Android源码中Parcelable可以看出,Parcelable实现过程主要分为序列化,反序列化,描述三个过程,下面分别介绍下这三个过程。
在介绍之前我们需要先了解Parcel是什么?Parcel翻译过来是打包的意思,其实就是包装了我们需要传输的数据,然后在Binder中传输,也就是用于跨进程传输数据
简单来说,Parcel提供了一套机制,可以将序列化之后的数据写入到一个共享内存中,其他进程通过Parcel可以从这块共享内存中读出字节流,并反序列化成对象,下图是这个过程的模型。
Parcel可以包含原始数据类型(用各种对应的方法写入,比如writeInt(),writeFloat()等),可以包含Parcelable对象,它还包含了一个活动的IBinder对象的引用,这个引用导致另一端接收到一个指向这个IBinder的代理IBinder。
Parcelable通过Parcel实现了read和write的方法,从而实现序列化和反序列化
序列化,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。
到这里,基本上关系都理清了,也明白简单的介绍和原理了,接下来在实现Parcelable之前,介绍下实现Parcelable的三大流程 .
[图片上传中...(image.png-73288d-1522235519935-0)]
我先把代码贴出来
public class Album implements Parcelable {
/**
* 负责反序列化
*/
private static final Creator CREATOR = new Creator() {
/**
* 从序列化对象中,获取原始的对象
* @param source
* @return
*/
@Override
public Album createFromParcel(Parcel source) {
return new Album(source);
}
/**
* 创建指定长度的原始对象数组
* @param size
* @return
*/
@Override
public Album[] newArray(int size) {
return new Album[0];
}
};
private final String mId;
private final String mCoverPath;
private final String mDisplayName;
private final long mCount;
Album(String id, String coverPath, String displayName, long count) {
mId = id;
mCoverPath = coverPath;
mDisplayName = displayName;
mCount = count;
}
Album(Parcel source) {
mId = source.readString();
mCoverPath = source.readString();
mDisplayName = source.readString();
mCount = source.readLong();
}
/**
* 描述
* 返回的是内容的描述信息
* 只针对一些特殊的需要描述信息的对象,需要返回1,其他情况返回0就可以
*
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 序列化
*
* @param dest
* @param flags
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mId);
dest.writeString(mCoverPath);
dest.writeString(mDisplayName);
dest.writeLong(mCount);
}
1)永久性保存对象,保存对象的字节序列到本地文件中;
2)通过序列化对象在网络中传递对象;
3)通过序列化在进程间传递对象。
首先写一个类实现Parcelable接口,会让我们实现两个方法:
describeContents 描述
其中describeContents就是负责文件描述.通过源码的描述可以看出,只针对一些特殊的需要描述信息的对象,需要返回1,其他情况返回0就可以
writeToParcel 序列化
我们通过writeToParcel方法实现序列化,writeToParcel返回了Parcel,所以我们可以直接调用Parcel中的write方法,基本的write方法都有,对象和集合比较特殊下面单独讲,基本的数据类型除了boolean其他都有,Boolean可以使用int或byte存储
我们将上面的Album对象实现序列化,Album对象包含四个字段。
反序列化需要定义一个CREATOR的变量,上面也说了具体的做法,这里可以直接复制Android给的例子中的,也可以自己定义一个(名字千万不能改),通过匿名内部类实现Parcelable中的Creator的接口
根据上面三个过程的介绍,Parcelable就写完了,就可以直接在Intent中传输了,可以自己写两个Activity传输一下数据试一下,其中一个putExtra另一个getParcelableExtra即可。
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
/**
* Created by fengxing on 2018/3/28.
*/
public class ParcelDemo implements Parcelable {
private int count;
private String name;
private ArrayList tags;
private Book book;
// ***** 注意: 这里如果是集合 ,一定要初始化 *****
private ArrayList books = new ArrayList<>();
/**
* 序列化
*
* @param in
*/
protected ParcelDemo(Parcel in) {
count = in.readInt();
name = in.readString();
tags = in.createStringArrayList();
// 读取对象需要提供一个类加载器去读取,因为写入的时候写入了类的相关信息
book = in.readParcelable(Book.class.getClassLoader());
//读取集合也分为两类,对应写入的两类
//这一类需要用相应的类加载器去获取
in.readList(books, Book.class.getClassLoader());// 对应writeList
//这一类需要使用类的CREATOR去获取
in.readTypedList(books, Book.CREATOR); //对应writeTypeList
//books = in.createTypedArrayList(Book.CREATOR); //对应writeTypeList
//这里获取类加载器主要有几种方式
getClass().getClassLoader();
Thread.currentThread().getContextClassLoader();
Book.class.getClassLoader();
}
public static final Creator CREATOR = new Creator() {
@Override
public ParcelDemo createFromParcel(Parcel in) {
return new ParcelDemo(in);
}
@Override
public ParcelDemo[] newArray(int size) {
return new ParcelDemo[size];
}
};
/**
* 描述
*
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 反序列化
*
* @param dest
* @param flags
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(count);
dest.writeString(name);
//序列化一个String的集合
dest.writeStringList(tags);
// 序列化对象的时候传入要序列化的对象和一个flag,
// 这里的flag几乎都是0,除非标识当前对象需要作为返回值返回,不能立即释放资源
dest.writeParcelable(book, 0);
// 序列化一个对象的集合有两种方式,以下两种方式都可以
//这些方法们把类的信息和数据都写入Parcel,以使将来能使用合适的类装载器重新构造类的实例.所以效率不高
dest.writeList(books);
//这些方法不会写入类的信息,取而代之的是:读取时必须能知道数据属于哪个类并传入正确的Parcelable.Creator来创建对象
// 而不是直接构造新对象。(更加高效的读写单个Parcelable对象的方法是:
// 直接调用Parcelable.writeToParcel()和Parcelable.Creator.createFromParcel())
dest.writeTypedList(books);
}
}
Book类,需要先实现Parcelable,实现步骤就不贴出来了,和普通的对象一样,实现三个过程
。
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by fengxing on 2018/3/28.
*/
public class Book implements Parcelable {
protected Book(Parcel in) {
}
public static final Creator CREATOR = new Creator() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
}
}
写入和读取集合有两种方式,
一种是写入类的相关信息,然后通过类加载器去读取, –> writeList | readList
二是不用类相关信息,创建时传入相关类的CREATOR来创建 –> writeTypeList | readTypeList | createTypedArrayList
第二种效率高一些
一定要注意如果有集合定义的时候一定要初始化 like this –>public ArrayList
Parcelable和Serializable都是实现序列化并且都可以用于Intent间传递数据,Serializable是Java的实现方式,可能会频繁的IO操作,所以消耗比较大,但是实现方式简单 Parcelable是Android提供的方式,效率比较高,但是实现起来复杂一些 , 二者的选取规则是:内存序列化上选择Parcelable, 存储到设备或者网络传输上选择Serializable(当然Parcelable也可以但是稍显复杂)
1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。
一种数据传输格式,与开发语言无关,轻量级。
一开始是javaScript的,但是后面比较流传,几乎所有语言都有相应的使用API。
Object---对象
使用花括号{}包含的键值对结构
Key必须是String类型,value为任何基本类型或者数据结构(对象,或者数组)
Array---数组
使用中括号[]来起始,并用逗号,来分隔元素
基本类型:
string number true false null
例如:
{
"name" :"王小二",
"age": 22,
"birthday" : "1990-12-1", 注意:json里没有Date这种时间的类型,所以用string;需要了解一下时间戳
"school" : "蓝翔",
"major": ["理发","挖掘机"],
"has_girlfriend":false,
"car": null,
"house": null,
"commont": "这是一个注释"
}
注意:
json中没有注释的写法,但是我们可以曲线救国
{
"id":"1",
"author":{
"id":"1506200043",
"name":"kylin",
"gender":1
},
"major":["理发","挖掘机"]
}
java对json有支持,在使用的时候需要引入以下包:
commons-beanutils.xxx.jar
commons-collections.xxx.jar
commons-lang.xxx.jar
commons-logging.jar
json-lib.xxx.jar
ezmorph.xxx.jar
JSON字符串的常用生成方式:----------从后端发送给前端的数据
1.使用map来放key-value数据,然后转换成json对象--再转换成json字符串---再发送给前端
//map(javaObject)--->jsonObject---->jsonStr ----前端
import net.sf.json.JSONObject;
public void jsonStrByMap(){
//构建map对象,并且把需要发送给前端的东西添加进入
Map params=new HashMap<>();
params.put("status","200");
params.put("username","kylin");
params.put("password","123456");
params.put("sexy","male");
params.put("userID","15062");
//java对象变成json对象
JSONObject jsonObject=JSONObject.fromObject(params);
//json对象转换成json字符串
String jsonStr=jsonObject.toString();
System.out.println(jsonStr);
}
2.使用javabean来构建json
User.java
public class User {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
//javabean---json对象---json字符串---->前端
public void jsonStrByJavaBean(){
//创建一个User对象
User user=new User();
user.setUsername("kylin");
user.setPassword("123456");
//java对象转换成json对象
JSONObject jsonObject=JSONObject.fromObject(user);
//json对象转换成json字符串
String jsonStr=jsonObject.toString();
System.out.println(jsonStr);
}
从前端获取的数据------>转换成后端可以用的数据
json字符串转换成java对象
//前端传递过来的是json字符串,那么我们对这个字符串需要进行获取并且处理
//json字符串-----json对象------java对象
JSONObject jsonobject = JSONObject.fromObject(jsonStr);
User user=(User)JSONObject.toBean(jsonobject,User.class);
//然后就通过user对象来操作了
String username=user.getUsername();
String password=user.getPassword();
json字符串转换成map对象
JSONObject jsonobject = JSONObject.fromObject(jsonStr);
Map params=(Map) jsonObject;
//然后就可以通过map来操作了
//判断是否有username字段和username字段
if (params.containsKey("username")){
System.out.println(params.get("username"));
}
if (params.containsKey("password")){
System.out.println(params.get("password"));
}
更多Android架构进阶视频学习请点击:https://space.bilibili.com/474380680
参考:https://www.cnblogs.com/shan-kylin/p/9218680.html
https://www.cnblogs.com/chenmingjun/p/9746310.html
https://www.jianshu.com/p/df35baa91541