pojo对象就用来数据存储。实现Looper.prepare()这个办法。proto文件定义了商讨数被之实业结构(message

产品还是服务由数存储和数据计算组成。pojo对象就是用来数据存储。一旦确定后,整个应用或产品的数目来自就确定。比如一个页面或者功能要动用什么数据就是可以长足找到相应之目标要经对象的关系找出来。

1. 安创造Looper?

Looper的构造方法为private,所以未可知一直下该构造方法创建。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

假若想当眼前线程创建Looper,需以Looper的prepare方法,Looper.prepare()。

假使现在如果我们来贯彻Looper.prepare()这个艺术,我们欠怎么开?我们掌握,Android中一个线程最多只能有一个Looper,若在曾起Looper的线程中调用Looper.prepare()会抛出RuntimeException(“Only
one Looper may be created per
thread”)。面对如此的需要,我们或许会见考虑以一个HashMap,其中Key为线程ID,Value为与线程关联的Looper,再长部分同步机制,实现Looper.prepare()这个艺术,代码如下:

public class Looper {

    static final HashMap<Long, Looper> looperRegistry = new HashMap<Long, Looper>();

    private static void prepare() {
        synchronized(Looper.class) {
            long currentThreadId = Thread.currentThread().getId();
            Looper l = looperRegistry.get(currentThreadId);
            if (l != null)
                throw new RuntimeException("Only one Looper may be created per thread");
            looperRegistry.put(currentThreadId, new Looper(true));
        }
    }
    ...
}

 

 

pojo对象属于对系的静态描述。它应该是名词,不应当是动词或者其它。动词、类型或者状态相当当是算法类型的靶子,权限应该是AOP考虑的,在后的漫谈里还会见详细提到。

2. ThreadLocal

ThreadLocal位于java.lang包中,以下是JDK文档中对该类的叙说

Implements a thread-local storage, that is, a variable for which each
thread has its own value. All threads share the same ThreadLocal object,
but each sees a different value when accessing it, and changes made by
one thread do not affect the other threads. The implementation supports
null values.

约莫意思是,ThreadLocal实现了线程本地存储。所有线程共享同一个ThreadLocal对象,但不同线程仅会顾与该线程相关联的价值,一个线程修改ThreadLocal对象对另线程没有影响。

ThreadLocal为编写多线程并发程序提供了一个初的思路。如下图所示,我们可以拿ThreadLocal理解吧平块存储区,将马上无异于可怜块存储区分割为多片小之存储区,每一个线程拥有一致片属于自己的存储区,那么对协调之存储区操作就不见面影响其它线程。对于ThreadLocal<Looper>,则每一样聊片存储区中尽管保存了与一定线程关联的Looper。
图片 1

Protobuf的大概介绍、使用与剖析

 

目的

对世界的合理描述反应。比如说:教育领域,农业领域,电商领域,零食领域等。这些使领域背景没有变化,就会见是合情稳定之。当然不同的产品的商业模式对同一个领域的解吧会不同,这些是碰头经常变化之,但是一般也特是体现在流程、类型、算法、功能等地方,这些并无影响pojo对象。

  • 富有人于维系的下理解一致
  • 每个对象职责单一、明确、不可替代

3. ThreadLocal之中贯彻原理

一、protobuf是什么?

        protobuf(Google Protocol
Buffers)是Google提供一个负有便捷的合计数据交换格式工具库(类似Json),但对比于Json,Protobuf有重新胜似的转账效率,时间效率与空中效率还是JSON的3-5倍增。后面将会晤生简短的demo对于当下有限种格式的多少转发效率的自查自纠。但此库房时使还不是无与伦比盛,据说谷歌内部多产品还有应用。

 

属性分类

为了快速区分属性,并且很快找到真正的pojo对象和性质。这些性可以于成品里的新增、详情、列表等效果里取得反映。

3.1 Thread、ThreadLocal和Values的关系

Thread的成员变量localValues代表了线程特定变量,类型也ThreadLocal.Values。由于线程特定变量可能会见时有发生多单,并且类型不确定,所以ThreadLocal.Values有一个table成员变量,类型为Object数组。这个localValues可以知晓吧二维存储区中同特定线程相关的一列。
ThreadLocal类则相当给一个代理,真正操作线程特定存储区table的是那里面类Values。
图片 2
图片 3

二、protobuf有什么?

        Protobuf
提供了C++、java、python语言的支撑,提供了windows(proto.exe)和linux平台动态编译生成proto文件对应之源文件。proto文件定义了商事数中之实业结构(message
,field)

关键字message: 代表了实体结构,由多只信息字段(field)组成。

消息字段(field): 包括数据类型、字段名、字段规则、字段唯一标识、默认值

数据类型:常见的原子类型且支持(在FieldDescriptor::kTypeToName中生定义)

字段规则:(在FieldDescriptor::kLabelToName中定义)

        required:必须初始化字段,如果没有赋值,在多少序列化时会丢来好

        optional:可选字段,可以不用初始化。

        repeated:数据可更(相当于java 中的Array或List)

        字段唯一标识:序列化和反序列化将会采用到。

默认值:在概念消息配段经常得给出默认值。

 

自描述

一般体现出的即是手动输入。比如:名称,标题等。

3.2 set方法

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

Values values(Thread current) {
    return current.localValues;
}

既是跟特定线程相关,所以先抱当前线程,然后取当前线程特定存储,即Thread中的localValues,若localValues为空,则创造一个,最后以value存入values中。

void put(ThreadLocal<?> key, Object value) {
    cleanUp();

    // Keep track of first tombstone. That's where we want to go back
    // and add an entry if necessary.
    int firstTombstone = -1;

    for (int index = key.hash & mask;; index = next(index)) {
        Object k = table[index];

        if (k == key.reference) {
            // Replace existing entry.
            table[index + 1] = value;
            return;
        }

        if (k == null) {
            if (firstTombstone == -1) {
                // Fill in null slot.
                table[index] = key.reference;
                table[index + 1] = value;
                size++;
                return;
            }

            // Go back and replace first tombstone.
            table[firstTombstone] = key.reference;
            table[firstTombstone + 1] = value;
            tombstones--;
            size++;
            return;
        }

        // Remember first tombstone.
        if (firstTombstone == -1 && k == TOMBSTONE) {
            firstTombstone = index;
        }
    }
}

自put方法中,ThreadLocal的reference和价值都见面满怀进table,索引分别吗index和index+1。
于Looper这个例子,
table[index] = sThreadLocal.reference;(指向自己的一个闭眼引用)
table[index + 1] = 同眼前线程关联的Looper

老三、protobuf有啊用?

       
Xml、Json是现阶段常用的数据交换格式,它们一直动用字段名称保护序列化后类实例中字段与数码里的映射关系,一般用字符串的形式保留于序列化后的字节流中。消息和消息之概念相对独立,可读性较好。但序列化后的多寡字节很非常,序列化和倒序列化的时间比丰富,数据传效率不赛。

       
Protobuf和Xml、Json序列化的法门不同,采用了第二进制字节的序列化方式,用配段索引与字段类型通过算法计算得到字段之前的涉嫌映射,从而达到更胜似的年月效率和空间效率,特别吻合对数据大小及传输速率比较敏感的场子下。

关联

出因来源,即于别的地方是手动输入,但是目前力量是挑选。比如:选择地段,选择项目。

3.3 get方法

public T get() {
    // Optimized for the fast path.
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
        values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

率先取出与线程相关的Values,然后于table中检索ThreadLocal的reference对象在table中的位置,然后返回下一个职所蕴藏的目标,即ThreadLocal的值,在Looper这个事例中虽是暨时线程关联的Looper对象。

打set和get方法好观看,其所操作的且是当下线程的localValues中之table数组,所以不同线程调用同一个ThreadLocal对象的set和get方法互不影响,这虽是ThreadLocal为缓解多线程程序的出现问题提供了同种新的思绪。

季、Protobuf在Android上的采用

1、创建proto文件,定义消息的实体结构

2、编译proto文件生成对应的java文件

3、添加protobuf-java-2.5.0.jar到android工程

4、在android中贯彻对信息结构的序列化/反序列化  

 

冗余

便民查询,减少复杂度。一般生以下情形:

  • 设若好成不见面扭转的,可以设想冗余,因为这么好减小复杂度。
  • 偏统计类。比如:视频里冗余评论数购买数。
  • 为了减少不同类型表的依。

4. ThreadLocal背后之统筹思想Thread-Specific Storage模式

Thread-Specific
Storage让大多个线程能够使同样之”逻辑全局“访问点来抱线程本地的对象,避免了历次看对象的锁定开销。

五、Protobuf与json的对比

功能

个性化业务,纯粹是以做功能

独留下于描述,这个很不便。需要充分层次了解世界。通过世界让设计。这样可经面向对象,通过大少的关注点,对整体系产生个静态的认。而且还可以判定出产品变更的时节对周体系的组织(即数据存储)有啊震慑。特别是出新新名词的当儿。

消根据产品之实际上情形来判断这些性怎么统筹。如果是想要高效、简单,但是4种植类型且放pojo上,开发是极抢之,但是同时必将吗是扩展性最差之。也需要根据产品的真需求来判断怎么处理后3栽档次的性。

4.1 Thread-Specific Storage模式的源

errno机制被周边用于一些操作系统平台。errno
是记录系统的终极一不好错误代码。对于单线程程序,在大局意图域内实现errno的功效是,但每当多线程操作系统被,多线程并发可能引致一个线程设置的errno值被另外线程错误解读。当时众多遗留库和应用程序都是基于单线程编写,为了在无修改既出接口及留代码的场面下,解决多线程访问errno的题材,Thread-Specific
Storage模式诞生。

1、创建product.proto文件

        定义了三只Message(ProductInfo、PhoneInfo、Watch)消息结构

图片 4

抽取步骤

森童鞋打在面向对象的牌子干着面向过程的从业。在抽取名词的下还要还要考虑算法、流程、权限等。这样一来关注点几哪里倍数增长,本来当用于考虑pojo对象是否站得住的时日再次未曾办法尽管得到应用。

众童鞋想成一糟糕就是管对象抽取出来。实现达标抽取比印象中还要复杂。所以建议的凡劈步骤,按部就班的错过抽取才是无与伦比抢的计。

4.2 Thread-Specific Storage模式的完好布局

图片 5

线程特定目标,相当于Looper。
线程特定对象集寓一组以及特定线程相关联的线程特定对象。每个线程都发友好之线程特定对象集。相当给ThreadLocal.Values。线程特定目标集可以储存于线程内部还是外部。Win32、Pthread和Java都针对线程特定数据产生支撑,这种场面下线程特定对象集可以储存在线程内部。
线程特定目标代理,受客户端能够如看常规对象同看线程特定目标。如果没代理,客户端必须一直访问线程特定目标集并出示地使用键。相当给ThreadLocal<Looper>。

自打概念上称,可将Thread-Specific
Storage的布局即一个二维矩阵,每个键对应一行,每个线程对应一列。第k执、第t排的矩阵元素呢对相应线程特定对象的指针。线程特定目标代理及线程特定目标集协作,向应用程序线程提供平等栽访问第k推行、第t列对象的安体制。注意,这个模型只是类比。实际上Thread-Specific
Storage模式的落实并无是运用二维矩阵,因为键不肯定是邻近整数。

图片 6

 

  

 

2、消息结构对应的java类(ProductInfo、PhoneInfo、Watch)

图片 7

 

枚举

只是把产品里关系到的持有名词枚举出来。
下面是枚举时的圈套:

  • 莫使失去通过友好的掌握去修改名词叫法
  • 切莫设错过忽略自己当不重大的名词
  • 毫无考虑表怎么存储
  • 决不考虑非名词

这些骗局很易吃后期返工。

3、消息结构及java对象赋值

PhoneName:” idol3”

Price:2000

Top:1

 

WatchName:” tcl watch”

Price:1000

Top:1

 

删除

去和产品(领域)无关的名词。比如:文案可能出现了故宫抑或平台名等和以领域无关之名词。

4、JSON字符串

 

{“phone”:{“phoneName”:”idol3″,”price”:2000,”top”:1},”watch”:{“watchName”:”tcl
wtch”,”top”:1,”price”:1000}}

 

去重

必要确保每个名词都是任务单一,不可取代的。
诚如去重的性状如下:不同之名词体现出的特性,功能及生命周期是同的,只是叙不同。
以:
不同角色的总人口以对同一个名词描述不同,他们在增产的早晚属性相似度杀大,流程也专程像。

诚如的反问自己或者产品:

  • 它的不同点在哪?
  • 比方转一个地方,另一个地方会无会见待而修改?
  • 若把她做成一样会时有发生啊问题为?

5、Protobuf转化后底二进制文件

图片 8

 

空间效率

Json:107个字节

Protobuf:32个字节

 

时光效率

Json序列化: 1ms ,  反序列化:0ms

Protobuf 序列化: 0ms 反序列化:0ms

 

拿public List<Phone> list和repeated PhoneInfo phoneInfoList
=3;都赋值为1000个PhoneInfo

 

空间效率

Json:4206个字节

Protobuf:1332个字节

 

时间效率

Json序列化: 4ms ,  反序列化:1ms

Protobuf 序列化: 1ms 反序列化:0ms

添加

  • 在叙一个概念的时节,必须透过杀多外对象,而且经常提。
  • 则产品无领过,但是当履之时光发有为数不少靶来一致的特征。常见情形:
    • 一个列表涉及到特别多的名词,但是列表本身产品并从未反映概念。
    • 不等之名词,他们的性质很一致,而且生命周期几乎是同等的,有种植几久平行线的觉得。比如说:同样要新增、发布、审核等

六、protobuf的略解析

聚合

将性能名词聚合到对象名词里。这里要确认单放由描述属性。其他的性能暂时无考虑,因为好好有益于之经关系来叙述,而且以此呢时不时会面变卦。

1、优缺点

瑜:通过上述之时效率及空中效率,可以看看protobuf的长空效率是JSON的2-5倍,时间效率要高,对于数据大小敏感,传输效率高之模块可利用protobuf库

 

症结:消息结构可读性不赛,序列化后底字节序列为二进制序列不可知简单的剖析中;目前应用未常见,只支持java,C++和Python;

 

陷阱

倘产生以下的图景证明对象分析的不够合理,后面很轻返工,请务必重视。

2、数据序列化/反序列化

另一方面描述

发平等在出一直以说,但是其他一样着从来不提。说明这里少重要名词。

a、规则:

protobuf把信息结果message也是通过
key-value对来表示。只是内的key是动一定的算法计算出来的就是透过每个message中每个字段(field
index)和字段的数据类型进行演算得来的key = (index<<3)|type;

type类型的相应关系如下:

 

Type

Meaning

Used For

0

Varint

int32, int64, uint32, uint64, sint32, sint64, bool, enum

1

64-bit

fixed64, sfixed64, double

2

Length-delimited

string, bytes, embedded messages, packed repeated fields

3

Start group

groups (deprecated)

4

End group

groups (deprecated)

5

32-bit

fixed32, sfixed32, float

 

Value会根据数据类型的不比会时有发生点儿种植表现形式:

对于各种int,bool,enum类型,value就是Varint

于string,bytes,message等等类型,value就是length+原始内容编码

 

Varints是同一栽艰苦凑表示数字的章程。它因此一个还是基本上只字节表示一个数字,值更小的数字字节数更是少。相对于传统的所以4字节代表int32列数字,Varints对于小于128底数值都可据此一个字节表示,大于128的数值会就此重新多之字节来代表,对于生可怜的数额虽然需因此5只字节来代表。

 

Varints算法描述:
每一个字节的高位都是出特异含义的,如果是1,则意味继续之字节也是欠数字的一致局部;如果是0,则结束

叙不同等

每当叙同一称词之时光,往往要求越来越翻译。
然也许会见现出的题材是:

  • 沟通和保障本大增
  • 雅可能短重要消息或者说提到理解的畸形等。

b、demo生成的底二进制文件反序列化。

第1个字节 (0A)

配段索引(index):         0A = 0001010  0A>>3 = 001 = 1

数据类型(type):           0A = 0001010&111  = 2 (String);

 

第2个字节 (0C)

字符串长度(length):      0E = 12;

字符串:                         0A 05 69 64 6F 6C 33 10 01 18 BD 0F

 

第3个字节 (0A)

坐字符串是根源phoneInfo属于嵌套类型

许段索引(index):         0A = 0001010  0A>>3 = 001 = 1

数据类型(type):           0A = 0001010&111  = 2 (String);

第4-9个字节(69 64 6F 6C 33)

字符串长度(length):    05 = 5

字符串:                       69 64 6F 6C 33 = idol3

 

第10个字节 (10)

许段索引(index):         10 = 00010000    10A>>3 = 0010 = 2

数据类型(type):           10 = 00010000&111  = 0 (Varints);

 

第11个字节  (01)

Varints:                          01 = 00001字节的万丈位呢0 整数结束

Value:                            1;

 

第12个字节(18)

许段索引(index):           18 = 00011000    18>> 00011 = 3

数据类型(type):           18 = 00011000&111  = 0 (Varints);

 

第13个字节(D0)

参天位吗1,整数计算到下一个字节

 

第14个字节(0F)

高高的位为0,整数计算截止

Value:为11111010000 =2000

 

成描述

  • 用多单词来讲述一个概念。需要一个新词。
  • 一个概念没有切实可行于描述,而是关系下的,但是同时是关联描述时常常出现。

C、反序列化结果

phoneinfo为

phoneName = “idol3”

top = 1

price = 2000;

 

同的不二法门watchInfo为:

watchName = “tcl name”

top = 1

price=2000 

推介书单

  • 《UML基础,应用与案例》
  • 《领域让设计》

3、时间效率

由此protobuf序列化/反序列化的长河得得出:protobuf是通过算法生成二上制流,序列化与反序列化不需要分析相应的节点性与剩下的叙说信息,所以序列化和反序列化时间效率比高。

4、空间效率

xml、json是故字段名称来规定类实例中字段之间的独立性,所以序列化后底数量多了多叙述信息,增加了序列化后底字节序列的容量。

 

Protobuf的序列化/反序列化过程可以汲取:

protobuf是由于字段索引(fieldIndex)与数据类型(type)计算(fieldIndex<<3|type)得出的key维护字段之间的投射且只占一个字节,所以相比json与xml文件,protobuf的序列化字节没有了多之key与叙述吻合信息,所以占用空间要稍群。

七、Protobuf的源码分析

1、protobuf在java使用的序列化流程

 

java程序调用parserFrom(byte[]
data)开始字节序列的反序列,Java程序通过调用编译生类GenerateMessage中的wirteTo()方法开始以序列化后底字节写副输出流中

 图片 9

图片 10

GenerateMessage
继承AbstractMessage类,序列化最终以AbstractMesssage中成功,序列化的实现过程:

a、遍历对象被Message结构()

调用AbstractMessage类中的writeTo()方法

 图片 11

b、 序列化Message中列一个字段

调用CodeOutputStream类中的writeMessageSetExtension()方法

图片 12

 

c、 对于Varints  Tag 的序列化流程:

调用CodeOutputStream类中的writeUInt32()方法

图片 13

调用CodeOutputStream类中的WriteRawVarint32()方法

图片 14

 

d、 对于非Varints Tag的序列化

调用CodeOutputStream类中的WriteTag()方法

图片 15 

图片 16

 

切切实实的序列化实现还当CodedOutputStream中完成

 

2、java以protobuf 的反序列化流程分析

java程序通过调用parserFrom(byte[] data)开始反序列化

 图片 17

图片 18

切切实实于com.google.protobuf. AbstractParser类中落实

 

图片 19

 

图片 20

图片 21

 

图片 22

 

 

最后在com.google.protobuf.CodedInputStream类中得反序列化

3、动态编译

为windows下用protoc.exe工具实现proto文件编译为例,protoc.exe是为此C++实现。在控制台执行命令:

图片 23

编译的流程:

自我批评proto的语法规则

将proto的公文中之message结构转换为GenerateMessage类的子类,并贯彻Builder接口。

编译流程

Main.cc中的main()方法

图片 24

 

Command_line_interface.cc中的Run()方法

图片 25

 

Import类中Import()

图片 26

 

于Descriptor中形成message消息的采与转账。

相关文章