c#基础系列:序列化效率比拼——谁是最后的赢家Newtonsoft.Json
前言:作为开发人员,对象的序列化恐怕难以避免。楼主也是很早以前就接触过序列化,可是理解都不太深刻,对于用哪种方式去做序列化更是随波逐流——项目中原来用的什么方式照着用就好了。可是这么多年自己对于这东西还是挺模糊的,今天正好有时间,就将原来用过的几种方式总结了下,也算是做一个记录,顺便做了下性能测试。楼主算了下,从使用序列化到现在,用到的无非下面几种方式:(1)JavaScriptSerializer方式;(2)DataContract方式;(3)Newtonsoft.Json.
1、准备工作:要对这三种方式分别作测试,必须要将相应的内库引用进来。
(1)JavaScriptSerializer这个类是.Net内置的,属于System.Web.Script.Serialization这个命名空间下面。需要引用System.Web.Extensions这个dll。
(2)DataContract方式也是.net内置的,主要使用的DataContractJsonSerializer这个类,属于System.Runtime.Serialization.Json这个命名空间。需要引用System.Runtime.Serialization这个dll。
(3)Newtonsoft.Json是第三方的dll,但是Visual Studio 对它做了很好的支持。使用方式有两种:一种是去网上下载最新的dll,然后添加引用即可;第二种是直接使用NuGet安装这个包。方式如下:
按照步骤安装即可。
2、类库准备完毕,还需要提供几个通用的方法。自己分别封装了JavaScriptSerializer和DataContract方式两个方法,代码如下:
#region DataContract序列化
public static class DataContractExtensions
{
/// <summary>
/// 将对象转化为Json字符串
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="instanse">对象本身</param>
/// <returns>JSON字符串</returns>
public static string ToJsonString<T>(this T instanse)
{
try
{
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
js.WriteObject(ms, instanse);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(ms);
return sr.ReadToEnd();
}
}
catch
{
return String.Empty;
}
} /// <summary>
/// 将字符串转化为JSON对象,如果转换失败,返回default(T)
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="s">字符串</param>
/// <returns>转换值</returns>
public static T ToJsonObject<T>(this string s)
{
try
{
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
StreamWriter sw = new StreamWriter(ms);
sw.Write(s);
sw.Flush();
ms.Seek(0, SeekOrigin.Begin);
return (T)js.ReadObject(ms);
}
}
catch
{
return default(T);
}
}
}
#endregion #region JavaScriptSerializer方式序列化
public static class JavascriptExtentions
{
public static string ToScriptJsonString<T>(this T instanse)
{
try
{
JavaScriptSerializer js = new JavaScriptSerializer();
return js.Serialize(instanse);
}
catch
{
return String.Empty;
}
} public static T ToScriptJsonObject<T>(this string s)
{
try
{
JavaScriptSerializer js = new JavaScriptSerializer();
return js.Deserialize<T>(s);
}
catch
{
return default(T);
}
}
}
#endregion
至于Newtonsoft.Json,自己有对应的方法,自己也封装了几个方法:
public class Newtonsoft_Common
{
#region 序列化
// 将对象(包含集合对象)序列化为Json
public static string SerializeObjToJson(object obj)
{
string strRes = string.Empty;
try
{
strRes = JsonConvert.SerializeObject(obj);
}
catch
{ } return strRes;
} //将xml转换为json
public static string SerializeXmlToJson(System.Xml.XmlNode node)
{
string strRes = string.Empty;
try
{
strRes = JsonConvert.SerializeXmlNode(node);
}
catch
{ } return strRes;
} //支持Linq格式的xml转换
public static string SerializeXmlToJson(System.Xml.Linq.XNode node)
{
string strRes = string.Empty;
try
{
strRes = JsonConvert.SerializeXNode(node);
}
catch
{ } return strRes;
}
#endregion #region 反序列化
//将json反序列化为实体对象(包含DataTable和List<>集合对象)
public static T DeserializeJsonToObj<T>(string strJson)
{
T oRes = default(T);
try
{
oRes = JsonConvert.DeserializeObject<T>(strJson);
}
catch
{ } return oRes;
} //将Json数组转换为实体集合
public static List<T> JsonLstToObjs<T>(List<string> lstJson)
{
List<T> lstRes = new List<T>();
try
{
foreach (var strObj in lstJson)
{
//将json反序列化为对象
var oRes = JsonConvert.DeserializeObject<T>(strObj);
lstRes.Add(oRes);
}
}
catch
{ } return lstRes;
}
#endregion
}
还有就是提供测试数据的两个方法:
public static List<Person> GetPersons()
{
var lstRes = new List<Person>();
for (var i = 0; i < 50000; i++)
{
var oPerson = new Person(); oPerson.Name = "李雷" + i;
oPerson.Age = 20;
oPerson.IsChild = i % 5 == 0 ? true : false;
oPerson.Test1 = "aaaaaa";
oPerson.Test2 = i.ToString() ;
oPerson.Test3 = i.ToString();
oPerson.Test4 = i.ToString();
oPerson.Test5 = i.ToString();
oPerson.Test6 = i.ToString();
oPerson.Test7 = i.ToString();
oPerson.Test8 = i.ToString();
oPerson.Test9 = i.ToString();
oPerson.Test10 = i.ToString();
lstRes.Add(oPerson);
} return lstRes;
} public static DataTable GetDataTable()
{
var dt = new DataTable("dt");
dt.Columns.Add("Age", Type.GetType("System.Int32"));
dt.Columns.Add("Name", Type.GetType("System.String"));
dt.Columns.Add("Sex", Type.GetType("System.String"));
dt.Columns.Add("IsChild", Type.GetType("System.Boolean"));
for (var i = 0; i < 1000; i++)
{
DataRow dr = dt.NewRow();
dr["Age"] = i + 1;
dr["Name"] = "Name" + i;
dr["Sex"] = i % 2 == 0 ? "男" : "女";
dr["IsChild"] = i % 5 > 0 ? true : false;
dt.Rows.Add(dr);
} return dt;
}
public static List<Person> GetPersons()
{
var lstRes = new List<Person>();
for (var i = 0; i < 50000; i++)
{
var oPerson = new Person(); oPerson.Name = "李雷" + i;
oPerson.Age = 20;
oPerson.IsChild = i % 5 == 0 ? true : false;
oPerson.Test1 = "aaaaaa";
oPerson.Test2 = i.ToString() ;
oPerson.Test3 = i.ToString();
oPerson.Test4 = i.ToString();
oPerson.Test5 = i.ToString();
oPerson.Test6 = i.ToString();
oPerson.Test7 = i.ToString();
oPerson.Test8 = i.ToString();
oPerson.Test9 = i.ToString();
oPerson.Test10 = i.ToString();
lstRes.Add(oPerson);
} return lstRes;
} public static DataTable GetDataTable()
{
var dt = new DataTable("dt");
dt.Columns.Add("Age", Type.GetType("System.Int32"));
dt.Columns.Add("Name", Type.GetType("System.String"));
dt.Columns.Add("Sex", Type.GetType("System.String"));
dt.Columns.Add("IsChild", Type.GetType("System.Boolean"));
for (var i = 0; i < 1000; i++)
{
DataRow dr = dt.NewRow();
dr["Age"] = i + 1;
dr["Name"] = "Name" + i;
dr["Sex"] = i % 2 == 0 ? "男" : "女";
dr["IsChild"] = i % 5 > 0 ? true : false;
dt.Rows.Add(dr);
} return dt;
}
3、测试开始之前,先介绍下,本篇测试分别通过强类型对象和若类型的DataTable分别去做序列化和反序列化的测试。测试代码:
static void Main(string[] args)
{
#region 强类型对象
var lstRes = GetPersons();
#region JavaScriptSerializer序列化方式
var lstScriptSerializeObj = new List<string>();
Stopwatch sp_script = new Stopwatch();
sp_script.Start();
foreach (var oPerson in lstRes)
{
lstScriptSerializeObj.Add(oPerson.ToScriptJsonString<Person>());
}
sp_script.Stop();
Console.WriteLine("JavaScriptSerializer序列化方式序列化" + lstScriptSerializeObj.Count + "个对象耗时:" + sp_script.ElapsedMilliseconds + "毫秒"); lstRes.Clear();
Stopwatch sp_script1 = new Stopwatch();
sp_script1.Start();
foreach (var oFrameSerializeObj in lstScriptSerializeObj)
{
lstRes.Add(oFrameSerializeObj.ToScriptJsonObject<Person>());
}
sp_script1.Stop();
Console.WriteLine("JavaScriptSerializer序列化方式反序列化" + lstScriptSerializeObj.Count + "个对象耗时:" + sp_script1.ElapsedMilliseconds + "毫秒");
#endregion #region DataContract序列化方式
var lstFrameSerializeObj = new List<string>();
Stopwatch sp = new Stopwatch();
sp.Start();
foreach (var oPerson in lstRes)
{
lstFrameSerializeObj.Add(oPerson.ToJsonString<Person>());
}
sp.Stop();
Console.WriteLine("DataContract序列化方式序列化" + lstFrameSerializeObj.Count + "个对象耗时:" + sp.ElapsedMilliseconds + "毫秒"); lstRes.Clear();
Stopwatch sp1 = new Stopwatch();
sp1.Start();
foreach (var oFrameSerializeObj in lstFrameSerializeObj)
{
lstRes.Add(oFrameSerializeObj.ToJsonObject<Person>());
}
sp1.Stop();
Console.WriteLine("DataContract序列化方式反序列化" + lstFrameSerializeObj.Count + "个对象耗时:" + sp1.ElapsedMilliseconds + "毫秒");
#endregion #region Newtonsoft
var lstNewtonsoftSerialize = new List<string>();
Stopwatch sp2 = new Stopwatch();
sp2.Start();
foreach (var oPerson in lstRes)
{
lstNewtonsoftSerialize.Add(JsonConvert.SerializeObject(oPerson));
}
sp2.Stop();
Console.WriteLine("Newtonsoft.Json方式序列化" + lstNewtonsoftSerialize.Count + "个对象耗时:" + sp2.ElapsedMilliseconds + "毫秒"); lstRes.Clear();
Stopwatch sp3 = new Stopwatch();
sp3.Start();
foreach (var oNewtonsoft in lstNewtonsoftSerialize)
{
lstRes.Add(JsonConvert.DeserializeObject<Person>(oNewtonsoft));
}
sp3.Stop();
Console.WriteLine("Newtonsoft.Json方式反序列化" + lstNewtonsoftSerialize.Count + "个对象耗时:" + sp3.ElapsedMilliseconds + "毫秒");
#endregion
#endregion #region 弱类型DataTable
/*var dt = GetDataTable();
#region JavaScriptSerializer序列化方式
var lstScriptSerializeObj = new List<string>();
Stopwatch sp_script = new Stopwatch();
sp_script.Start();
var strRes = dt.ToScriptJsonString<DataTable>();
sp_script.Stop();
Console.WriteLine("JavaScriptSerializer序列化方式序列化" + lstScriptSerializeObj.Count + "个对象耗时:" + sp_script.ElapsedMilliseconds + "毫秒"); dt.Clear();
Stopwatch sp_script1 = new Stopwatch();
sp_script1.Start();
dt = strRes.ToScriptJsonObject<DataTable>();
sp_script1.Stop();
Console.WriteLine("JavaScriptSerializer序列化方式反序列化" + lstScriptSerializeObj.Count + "个对象耗时:" + sp_script1.ElapsedMilliseconds + "毫秒");
#endregion #region DataContract序列化方式
var lstFrameSerializeObj = new List<string>();
Stopwatch sp = new Stopwatch();
sp.Start();
strRes = dt.ToJsonString<DataTable>();
sp.Stop();
Console.WriteLine("DataContract序列化方式序列化" + lstFrameSerializeObj.Count + "个对象耗时:" + sp.ElapsedMilliseconds + "毫秒"); dt.Clear();
Stopwatch sp1 = new Stopwatch();
sp1.Start();
dt = strRes.ToJsonObject<DataTable>();
sp1.Stop();
Console.WriteLine("DataContract序列化方式反序列化" + lstFrameSerializeObj.Count + "个对象耗时:" + sp1.ElapsedMilliseconds + "毫秒");
#endregion #region Newtonsoft
var lstNewtonsoftSerialize = new List<string>();
Stopwatch sp2 = new Stopwatch();
sp2.Start();
strRes = JsonConvert.SerializeObject(dt);
sp2.Stop();
Console.WriteLine("Newtonsoft.Json方式序列化" + lstNewtonsoftSerialize.Count + "个对象耗时:" + sp2.ElapsedMilliseconds + "毫秒"); dt.Clear();
Stopwatch sp3 = new Stopwatch();
sp3.Start();
dt = JsonConvert.DeserializeObject<DataTable>(strRes);
sp3.Stop();
Console.WriteLine("Newtonsoft.Json方式反序列化" + lstNewtonsoftSerialize.Count + "个对象耗时:" + sp3.ElapsedMilliseconds + "毫秒");
#endregion*/
#endregion Console.ReadLine();
}
static void Main(string[] args)
{
#region 强类型对象
var lstRes = GetPersons();
#region JavaScriptSerializer序列化方式
var lstScriptSerializeObj = new List<string>();
Stopwatch sp_script = new Stopwatch();
sp_script.Start();
foreach (var oPerson in lstRes)
{
lstScriptSerializeObj.Add(oPerson.ToScriptJsonString<Person>());
}
sp_script.Stop();
Console.WriteLine("JavaScriptSerializer序列化方式序列化" + lstScriptSerializeObj.Count + "个对象耗时:" + sp_script.ElapsedMilliseconds + "毫秒"); lstRes.Clear();
Stopwatch sp_script1 = new Stopwatch();
sp_script1.Start();
foreach (var oFrameSerializeObj in lstScriptSerializeObj)
{
lstRes.Add(oFrameSerializeObj.ToScriptJsonObject<Person>());
}
sp_script1.Stop();
Console.WriteLine("JavaScriptSerializer序列化方式反序列化" + lstScriptSerializeObj.Count + "个对象耗时:" + sp_script1.ElapsedMilliseconds + "毫秒");
#endregion #region DataContract序列化方式
var lstFrameSerializeObj = new List<string>();
Stopwatch sp = new Stopwatch();
sp.Start();
foreach (var oPerson in lstRes)
{
lstFrameSerializeObj.Add(oPerson.ToJsonString<Person>());
}
sp.Stop();
Console.WriteLine("DataContract序列化方式序列化" + lstFrameSerializeObj.Count + "个对象耗时:" + sp.ElapsedMilliseconds + "毫秒"); lstRes.Clear();
Stopwatch sp1 = new Stopwatch();
sp1.Start();
foreach (var oFrameSerializeObj in lstFrameSerializeObj)
{
lstRes.Add(oFrameSerializeObj.ToJsonObject<Person>());
}
sp1.Stop();
Console.WriteLine("DataContract序列化方式反序列化" + lstFrameSerializeObj.Count + "个对象耗时:" + sp1.ElapsedMilliseconds + "毫秒");
#endregion #region Newtonsoft
var lstNewtonsoftSerialize = new List<string>();
Stopwatch sp2 = new Stopwatch();
sp2.Start();
foreach (var oPerson in lstRes)
{
lstNewtonsoftSerialize.Add(JsonConvert.SerializeObject(oPerson));
}
sp2.Stop();
Console.WriteLine("Newtonsoft.Json方式序列化" + lstNewtonsoftSerialize.Count + "个对象耗时:" + sp2.ElapsedMilliseconds + "毫秒"); lstRes.Clear();
Stopwatch sp3 = new Stopwatch();
sp3.Start();
foreach (var oNewtonsoft in lstNewtonsoftSerialize)
{
lstRes.Add(JsonConvert.DeserializeObject<Person>(oNewtonsoft));
}
sp3.Stop();
Console.WriteLine("Newtonsoft.Json方式反序列化" + lstNewtonsoftSerialize.Count + "个对象耗时:" + sp3.ElapsedMilliseconds + "毫秒");
#endregion
#endregion #region 弱类型DataTable
/*var dt = GetDataTable();
#region JavaScriptSerializer序列化方式
var lstScriptSerializeObj = new List<string>();
Stopwatch sp_script = new Stopwatch();
sp_script.Start();
var strRes = dt.ToScriptJsonString<DataTable>();
sp_script.Stop();
Console.WriteLine("JavaScriptSerializer序列化方式序列化" + lstScriptSerializeObj.Count + "个对象耗时:" + sp_script.ElapsedMilliseconds + "毫秒"); dt.Clear();
Stopwatch sp_script1 = new Stopwatch();
sp_script1.Start();
dt = strRes.ToScriptJsonObject<DataTable>();
sp_script1.Stop();
Console.WriteLine("JavaScriptSerializer序列化方式反序列化" + lstScriptSerializeObj.Count + "个对象耗时:" + sp_script1.ElapsedMilliseconds + "毫秒");
#endregion #region DataContract序列化方式
var lstFrameSerializeObj = new List<string>();
Stopwatch sp = new Stopwatch();
sp.Start();
strRes = dt.ToJsonString<DataTable>();
sp.Stop();
Console.WriteLine("DataContract序列化方式序列化" + lstFrameSerializeObj.Count + "个对象耗时:" + sp.ElapsedMilliseconds + "毫秒"); dt.Clear();
Stopwatch sp1 = new Stopwatch();
sp1.Start();
dt = strRes.ToJsonObject<DataTable>();
sp1.Stop();
Console.WriteLine("DataContract序列化方式反序列化" + lstFrameSerializeObj.Count + "个对象耗时:" + sp1.ElapsedMilliseconds + "毫秒");
#endregion #region Newtonsoft
var lstNewtonsoftSerialize = new List<string>();
Stopwatch sp2 = new Stopwatch();
sp2.Start();
strRes = JsonConvert.SerializeObject(dt);
sp2.Stop();
Console.WriteLine("Newtonsoft.Json方式序列化" + lstNewtonsoftSerialize.Count + "个对象耗时:" + sp2.ElapsedMilliseconds + "毫秒"); dt.Clear();
Stopwatch sp3 = new Stopwatch();
sp3.Start();
dt = JsonConvert.DeserializeObject<DataTable>(strRes);
sp3.Stop();
Console.WriteLine("Newtonsoft.Json方式反序列化" + lstNewtonsoftSerialize.Count + "个对象耗时:" + sp3.ElapsedMilliseconds + "毫秒");
#endregion*/
#endregion Console.ReadLine();
}
4、测试结果:
先说强类型对象的结果:
(1)集合数量100和1000时,序列化和反序列化三种方式差别不大:
(2)当超过10000时,
(3)继续加大数据量
弱类型DataTable的测试结果:
JavaScriptSerializer方式直接报错:
DataContract方式需要提供DataTable的表名,序列化得到是DataTable的Xml
Newtonsoft.Json方式可以实现和Json数据的序列化和反序列化。
5、测试总结:
(1)总的来说,DataContract和Newtonsoft.Json这两种方式效率差别不大,随着数量的增加JavaScriptSerializer的效率相对来说会低些。
(2)对于DataTable的序列化,如果要使用json数据通信,使用Newtonsoft.Json更合适,如果是用xml做持久化,使用DataContract合适。
(3)随着数量的增加JavaScriptSerializer序列化效率越来越低,反序列化和其他两种相差不大。
(4)后来发现当对象的DataTime类型属性不赋值时,DataContract和JavaScriptSerializer这两种方式序列化都会报错,而用Newtonsoft.Json方式可以正常序列化。所以看来在容错方便,还是Newtonsoft.Json比较强。
以上只是楼主自己做的简单测试,可能存在不够严谨的地方,望各位大虾拍砖指正~~
c#基础系列:序列化效率比拼——谁是最后的赢家Newtonsoft.Json的更多相关文章
- 序列化效率比拼——谁是最后的赢家Newtonsoft.Json
前言:作为开发人员,对象的序列化恐怕难以避免.楼主也是很早以前就接触过序列化,可是理解都不太深刻,对于用哪种方式去做序列化更是随波逐流——项目中原来用的什么方式照着用就好了.可是这么多年自己对于这东西 ...
- 序列化效率比拼——谁是最后的赢家avaScriptSerializer方式、DataContract方式、Newtonsoft.Json
前言:作为开发人员,对象的序列化恐怕难以避免.楼主也是很早以前就接触过序列化,可是理解都不太深刻,对于用哪种方式去做序列化更是随波逐流——项目中原来用的什么方式照着用就好了.可是这么多年自己对于这东西 ...
- c# 序列化效率比拼
前言:作为开发人员,对象的序列化经常用到,特别是在现在前后端分离 采用json 交互 ,就将原来用过的几种方式总结了下,也算是做一个记录,顺便做了下性能测试. 1:内置 JavaScriptSeria ...
- Java基础系列——序列化(一)
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6797659.html 工作中发现,自己对Java的了解还很片面,没有深入的研究,有很多的J ...
- C#基础系列——委托和设计模式(二)
前言:前篇 C#基础系列——委托实现简单设计模式 简单介绍了下委托的定义及简单用法.这篇打算从设计模式的角度去解析下委托的使用.我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性 ...
- C#基础系列——再也不用担心面试官问我“事件”了
前言:作为.Net攻城狮,你面试过程中是否遇到过这样的问题呢:什么是事件?事件和委托的区别?既然事件作为一种特殊的委托,那么它的优势如何体现?诸如此类...你是否也曾经被问到过?你又是否都答出来了呢? ...
- C#基础系列——异步编程初探:async和await
前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...
- C#基础系列——一场风花雪月的邂逅:接口和抽象类
前言:最近一个认识的朋友准备转行做编程,看他自己边看视频边学习,挺有干劲的.那天他问我接口和抽象类这两个东西,他说,既然它们如此相像, 我用抽象类就能解决的问题,又整个接口出来干嘛,这不是误导初学者吗 ...
- c#基础系列(转)
转:http://www.cnblogs.com/landeanfen/p/4953025.html C#基础系列——一场风花雪月的邂逅:接口和抽象类 前言:最近一个认识的朋友准备转行做编程,看他自己 ...
随机推荐
- .Net中的不可变集合(Immutable Collection)简介
今天发现MS在Nuget上发布了一个Immutable Collection的程序集,提供了对不可变对象的集合的支持. 简单的看了一下,貌似支持的还比较全: ImmutableArray<T&g ...
- sqlserver 巧用REVERSE和SUBSTRING实现lastindexof
原文:sqlserver 巧用REVERSE和SUBSTRING实现lastindexof select REVERSE(SUBSTRING(REVERSE(testFixtureNumber),0, ...
- delphi 设置开机自动启动函数
有些程序要设置为开机启动,所以自己写了个函数方便以后使用,供大家参考 procedure TMainForm.SetAutoRun(ok: boolean); var Reg:TRegistry; ...
- delphi怎样编译LINUX程序
delphi编译LINUX程序 DELPHI XE 10.2(TOKYO)开始可以开发LINUX控制台程序. 1)上传PASERVER到LINUX,并且运行PASERVER. 2)开始编译,PROFI ...
- Get Started with Subversion using SvnX
A very important part of a development environment is source code control. Subversion is the server- ...
- SQL SERVER 2012 尝试读取或写入受保护的内存。这通常指示其他内存已损坏。 (System.Data)
标题: 连接到服务器------------------------------ 无法连接到 192.168.1.253. ------------------------------其他信息: 尝试 ...
- IT开发者对Mac钟爱
由于Mac的操作系统OSX相比Windows win7/8/10来说,比較适合开发者使用.个人的体会例如以下: 首先.OSX的多窗体多应用程序切换功能非常强大,对开发者来说非常实用.开发者一般都须要开 ...
- Console-算法:fun1(do while)
ylbtech-Arithmetic:Console-算法[do while]-XX 1.A,Demo(案例) 1.B,Solution(解决方案) using System; namespace ...
- zabbix通过snmp监控windows主机
1.开启Windows的snmp功能 2.配置snmp服务 设置snmp服务社区名称及允许的主机,设置完成后重启snmp服务 3.在zabbix server上测试 测试需要使用命令snmpwalk, ...
- 《深入理解Android 卷III》第二章 深入理解Java Binder和MessageQueue
<深入理解Android 卷III>即将公布.作者是张大伟.此书填补了深入理解Android Framework卷中的一个主要空白,即Android Framework中和UI相关的部分. ...