分类归档 终端开发

通过admin

Java多线程notify&notifyall的区别

当一个线程进入wait之后,就必须等其他线程notify/notifyall,使用notifyall,可以唤醒

所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。注意,任何时候只有一个线程可以获得锁,也就是说只有一个线程可以运行synchronized 中的代码,notifyall只是让处于wait的线程重新拥有锁的争夺权,但是只会有一个获得锁并执行。

 

那么notify和notifyall在效果上又什么实质区别呢?

主要的效果区别是notify用得不好容易导致死锁,例如下面提到的例子。

 

public synchronized void put(Object o) {

 

while (buf.size()==MAX_SIZE) {

 

wait(); // called if the buffer is full (try/catch removed for brevity)

 

}

 

buf.add(o);

 

notify(); // called in case there are any getters or putters waiting

 

}

 

 

 

public synchronized Object get() {

 

// Y: this is where C2 tries to acquire the lock (i.e. at the beginning of the method)

 

while (buf.size()==0) {

 

wait(); // called if the buffer is empty (try/catch removed for brevity)

 

// X: this is where C1 tries to re-acquire the lock (see below)

 

}

 

Object o = buf.remove(0);

 

notify(); // called if there are any getters or putters waiting

 

return o;

 

}

 

所以除非你非常确定notify没有问题,大部分情况还是是用notifyall。
更多详细的介绍可以参看:

http://stackoverflow.com/questions/37026/java-notify-vs-notifyall-all-over-again

 

 

通过admin

linux下Google的Protobuf安装及使用笔记

protobuf

项目主页:http://code.google.com/p/protobuf/

下载:http://code.google.com/p/protobuf/downloads/list protobuf-2.4.1.tar.gz

解压后进入protobuf-2.4.1目录进行安装:

1、./configure(注:默认可能会安装在/usr/local目录下,可以加–prefix=/usr来指定安装到/usr/lib下,可以免去路径的设置,路径设置见Linux命令pkg-config

./configure –prefix=/usr/local/protobuf

2、make

3、make check

4、make install(需要超级用户root权限)

二、使用

1、写proto文件,定义消息具体格式。如:helloworld.proto

package lm;

message helloworld

{

required int32 id = 1;//ID

required string str = 2;//str

optional int32 opt = 3;//optional field

}

2、使用protoc来编译生成对应语言的文件

–cpp_out:表示输出c++语言使用格式,–java_out,–python_out等,其他第三方的插件:Third-Party Add-ons for Protocol Buffers

此时会生成helloworld.pb.h及helloworld.pb.cc两个文件

3、Write A message

View Code

4、Read A message

View Code

5、编译及运行

g++ -g -o Writer helloworld.pb.cc writermessage.cpp `pkg-config –cflags –libs protobuf`

g++ -g -o Reader helloworld.pb.cc Readermessage.cpp `pkg-config –cflags –libs protobuf`

通过admin

详解Google-ProtoBuf中结构化数据的编码

原文:http://www.wuzesheng.com/?p=1258

本文的主要内容是google protobuf中序列化数据时用到的编码规则,但是,介绍具体的编码规则之前,我觉得有必要先简单介绍一下google protobuf。因此,本文首先会介绍一些google protobuf相关的内容,让读者朋友对google protobuf有一个初步的印象,然后,再开始进入正题—-深入浅出地介绍google protobuf中用到的编码规则。下面言归正传,开始今天的话题。

1. Google-ProtoBuf是什么

ProtoBuf,全称是Protocol Buffers, 它是谷歌内部用的一种高效的、可扩展的对结构化数据进行编码的格式规范。谷歌自己内部很多程序之间的通信协议都用了ProtoBuf。

ProtoBuf可以支持多种编程语言,目前已经C++, Java和Python,本文中所前的内容用到例子的话,会以C++为例。

2.如何得到Google-ProtoBuf

ProtoBuf在Google Code上的主页是:http://code.google.com/p/protobuf/, 感兴趣的朋友可以在这里下载ProtoBuf的源码,也可以在这里阅读ProtoBuf的详细的文档。

3. 深入浅出Google-ProtoBuf中的编码规则

(1)序列化和反序列化:

在开始本部分的内容之前,首先有必要介绍两个基本概念,一个是序列化,一个是反序列化。这两个概念的定义在网上搜一下都很多的,但大多都讲得比较晦涩,不太好理解,在这里我会用比较通俗的文字来解释,尽可能让读都朋友们一读就明白是怎么回事:

序列化:是指将结构化的数据按一定的编码规范转成指定格式的过程

反序列化:是指将转成指定格式的数据解析成原始的结构化数据的过程

举个例子,Person是一个表示人的对象类型,person是一个Person类型的对象,将person存到一个对应的XML文档中的过程就是一种序列化,而解析XML生成对应Person类型对象person的过程,就是一个反序列化的过程。在这里结构化数据指的就是Person类型的数据,一定的编码规范指的就是XML文档的规范。XML是一种简单的序列化方式,用XML序列化的好处是,XML的通用性比较好,另外,XML是一种文本格式,对人阅读比较友好,但是XML方式比较占空间,效率也不是很高。通常,比较高效的序列化都是采用二进制方式的,将要序列化的结构化数据,按一定的编码规范,转成为一串二进制的字节流存储下来,需要用的时候再从这串二进制的字节流中反序列化出对应的结构化的数据。

通过上面的介绍,我们给protobuf下一个比较正式的定义了:Google ProtoBuf是Google制定的一种用来序列化结构化数据的程序库。

(2)ProtoBuf中的编码:

1) ProtoBuf编码基础——Varints, varints是一种将一个整数序列化为一个或者多个Bytes的方法,越小的整数,使用的Bytes越少。

Varints的基本规则是:

(a)每个Byte的最高位(msb)是标志位,如果该位为1,表示该Byte后面还有其它Byte,如果该位为0,表示该Byte是最后一个Byte。

(b)每个Byte的低7位是用来存数值的位

(c)Varints方法用Litte-Endian(小端)字节序

举个例子:300用Varints序列化的结果是1010 1100 0000 0010,运算过程如下所示:

1010 1100 0000 0010->010 1100 000 0010(去标志位)->

000 0010 010 1100(调整字节序)-> 1 0010 1100 ->256+32+8+4=300(计算值)

2)ProtoBuf中消息的编码规则:

(a)每条消息(message)都是有一系列的key-value对组成的, key和value分别采用不同的编码方式。

(b)对某一条件消息(message)进行编码的时候,是把该消息中所有的key-value对序列化成二进制字节流;而解码的时候,解码程序读入二进制的字节流,解析出每一个key-value对,如果解码过程中遇到识别不出来的类型,直接跳过。这样的机制,保证了即使该消息添加了新的字段,也不会影响旧的编/解码程序正常工作。

(c)key由两部分组成,一部分是在定义消息时对字段的编号(field_num),另一部分是字段类型(wire_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

(d)key的编码方式:field_num << 3 | wire_type

(e)varint类型(wire_type=0)的编码,与第(1)部分中介绍的方法基本一致,但是int32, int64和sint32,sint64有些特别之处:int32和int64就是简单的按varints方法来编码,所以像-1、-2这样负数也会占比较多的Bytes。于是sint32和sint64采用了一种改进的方法:先采用Zigzag方法将所有的整数(正数、0和负数)一一映射到所有的无符号数上,然后再采用varints编码方法进行编码。Zigzag映射函数为:

Zigzag(n) = (n << 1) ^ (n >> 31),  n为sint32时

Zigzag(n) = (n << 1) ^ (n >> 63),  n为sint64时

下表是一个比较直观的映射表,这样映射后再进行编码的好处就是绝对值比较小的负数序列化后的结果占的Bytes数也会比较少。

Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2 4
-3 5
2147483647 4294967294
-2147483648 4294967295

(f)64-bit(wire_type=1)和32-bit(wire_type=5)的编码方式就比较简单了,直接在key后面跟上64bits或32bits,采用Little-Endian(小端)字节序。

(g)length-delimited(wire_type=2)的编码方式:key+length+content, key的编码方式是统一的,length采用varints编码方式,content就是由length指定的长度的Bytes。

(h)wire_type=3和4的现在已经不推荐使用了,因此这里也不再做介绍。

3)ProtoBuf编解码中字段顺序(Field order)的问题:

(a) 编码/解码与字段顺序无关,这一点由key-value机制就能保证

(b)对于未知的字段,编码的时候会把它写在序列化完的已知字段后面。

 

源文档 <http://www.wuzesheng.com/?p=1258>

通过admin

Java protobuf框架使用向导

 

ProtoBuf,全称是Protocol Buffers, 它是谷歌内部用的一种高效的、可扩展的对结构化数据进行编码的格式规范。谷歌自己内部很多程序之间的通信协议都用了ProtoBuf。

下面介绍的是使用Java ProtoBuf的基本步骤:

1.http://code.google.com/p/protobuf/downloads/list ,选择其中的win版本下载

2.下载一个protobuf-java-2.4.1.jar文件(注意,要与你刚才下的proto.exe版本相同,否则可能出现编译通不过现象)

http://grepcode.com/snapshot/repo1.maven.org/maven2/com.google.protobuf/protobuf-java/2.4.1

3.在proto.exe同级目录,编写一个msg.proto文件:

 

package tutorial;

 

option java_package = “com.protobuftest.protobuf”;

option java_outer_classname = “PersonProbuf”;

 

message Person {

required string name = 1;

required int32 id = 2;

optional string email = 3;

 

enum PhoneType {

MOBILE = 0;

HOME = 1;

WORK = 2;

}

 

message PhoneNumber {

required string number = 1;

optional PhoneType type = 2 [default = HOME];

}

 

repeated PhoneNumber phone = 4;

 

message CountryInfo {

required string name = 1;

required string code = 2;

optional int32 number = 3;

}

}

 

message AddressBook {

repeated Person person = 1;

}

 

4.使用如下命令编译这个文件:

5.将生成的ProtoBufferPractice.java文件引入eclipse

6.把下载的protobuf-java-2.4.1.jar也引入工程

7.使用方法:

package com.protobuftest;

 

import java.util.List;

 

import com.google.protobuf.InvalidProtocolBufferException;

import com.protobuftest.protobuf.PersonProbuf;

import com.protobuftest.protobuf.PersonProbuf.Person;

import com.protobuftest.protobuf.PersonProbuf.Person.PhoneNumber;

import com.protobuftest.protobuf.PersonProbuf.Person.PhoneNumberOrBuilder;

import com.protobuftest.protobuf.PersonProbuf.Person.PhoneType;

 

public class ProtoBufTest {

 

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

PersonProbuf.Person.Builder builder = PersonProbuf.Person.newBuilder();

builder.setEmail(“kkk@email.com”);

builder.setId(1);

builder.setName(“TestName”);

builder.addPhone(PersonProbuf.Person.PhoneNumber.newBuilder().setNumber(“131111111”).setType(PersonProbuf.Person.PhoneType.MOBILE));

builder.addPhone(PersonProbuf.Person.PhoneNumber.newBuilder().setNumber(“011111”).setType(PersonProbuf.Person.PhoneType.HOME));

 

Person person = builder.build();

byte[] buf = person.toByteArray();

 

try {

Person person2 = PersonProbuf.Person.parseFrom(buf);

System.out.println(person2.getName() + “, ” + person2.getEmail());

List<PhoneNumber> lstPhones = person2.getPhoneList();

for (PhoneNumber phoneNumber : lstPhones) {

System.out.println(phoneNumber.getNumber());

}

} catch (InvalidProtocolBufferException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

 

System.out.println(buf);

 

}

 

}

 

源文档 <http://blog.csdn.net/csharp25/article/details/6632127>

通过admin

Android日期时间格式国际化

在做多语言版本的时候,日期时间的格式话是一个很头疼的事情,幸好Android提供了DateFormate,可以根据指定的语言区域的默认格式来格式化。直接贴代码:

public static CharSequence formatTimeInListForOverSeaUser(

final Context context, final long time, final boolean simple,

Locale locale) {

final GregorianCalendar now = new GregorianCalendar();

 

// special time

if (time < MILLSECONDS_OF_HOUR) {

return “”;

}

 

// today

final GregorianCalendar today = new GregorianCalendar(

now.get(GregorianCalendar.YEAR),

now.get(GregorianCalendar.MONTH),

now.get(GregorianCalendar.DAY_OF_MONTH));

final long in24h = time – today.getTimeInMillis();

if (in24h > 0 && in24h <= MILLSECONDS_OF_DAY) {

java.text.DateFormat df = java.text.DateFormat.getTimeInstance(

java.text.DateFormat.SHORT, locale);

return “” + df.format(time);

}

 

// yesterday

final long in48h = time – today.getTimeInMillis() + MILLSECONDS_OF_DAY;

if (in48h > 0 && in48h <= MILLSECONDS_OF_DAY) {

return simple ? context.getString(R.string.fmt_pre_yesterday)

: context.getString(R.string.fmt_pre_yesterday)

+ ” ”

+ java.text.DateFormat.getTimeInstance(

java.text.DateFormat.SHORT, locale).format(

time);

}

 

final GregorianCalendar target = new GregorianCalendar();

target.setTimeInMillis(time);

 

// same week

if (now.get(GregorianCalendar.YEAR) == target

.get(GregorianCalendar.YEAR)

&& now.get(GregorianCalendar.WEEK_OF_YEAR) == target

.get(GregorianCalendar.WEEK_OF_YEAR)) {

java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(“E”, locale);

final String dow = “” + sdf.format(time);

return simple ? dow : dow

+ java.text.DateFormat.getTimeInstance(

java.text.DateFormat.SHORT, locale).format(time);

}

 

// same year

if (now.get(GregorianCalendar.YEAR) == target

.get(GregorianCalendar.YEAR)) {

return simple ? java.text.DateFormat.getDateInstance(

java.text.DateFormat.SHORT, locale).format(time)

: java.text.DateFormat.getDateTimeInstance(

java.text.DateFormat.SHORT,

java.text.DateFormat.SHORT, locale).format(time);

}

 

return simple ? java.text.DateFormat.getDateInstance(

java.text.DateFormat.SHORT, locale).format(time)

: java.text.DateFormat.getDateTimeInstance(

java.text.DateFormat.SHORT, java.text.DateFormat.SHORT,

locale).format(time);

}

 

注意这里用的是java.text.DateFormat,还有另外一个java.text.format.DateFormat,后者不能指定locale。

详细介绍见:http://developer.android.com/reference/java/text/DateFormat.html

通过admin

Android中string.xml使用总结

<b></b>加粗字体

 

<i></i> 斜体字体

 

<u></u> 给字体加下划线

 

\n 换行

 

\u0020表示空格

 

\u2026表示省略号

 

使用&lt;b>和&lt;b>来打印出<b></b> 这样的文字;“&lt;”表示“<”的意思;

 

使用textView.setText(Html.fromHtml(“Hello <b>World</b>,<font size=\”3\” color=\”red\”>AnalysisXmlActivty!</font>”));设置类似于html那样的效果

 

如果你需要使用 String.format(String, Object…) 来格式化你的字符串,你可以把格式化参数放在你的字符串中,参见下面的例子:

 

<string name=”welcome_messages”>Hello, %1$s! You have %2$d new messages.</string>

 

在这个例子中,这个格式化的字符串有2个参数, %1$s是个字符串 %2$d 是个浮点数,你可以在你的程序中按照下面的方法来根据参数来格式化字符串:

 

Resources res = getResources();

 

String text = String.format(res.getString(R.string.welcome_messages), username, mailCount);

 

那么根据例子上说的我需要把%s换成%1$s才行了,修改后编译通过,程序成功启动。

 

问题补充:如何在<string></string>中使用%号

 

有两个办法可供选择

1.用%%来表示1个%,和转意符号 \ 的用法相同

 

2.如果你的字符串不需要格式化,可以在你的<string 标签上增加一个属性:formatted=”false”例如 <string name=”test” formatted=”false”>% test %</string> 即可

通过admin

Update plug-in ant from 1.7 to 1.8

如果不更新1.8,会报以下的错误:

The Android Ant-based build system requires Ant 1.8.0 or later. Current version is 1.7.1…

1)到网站(http://ant.apache.org/)下载Ant的目前最新的版本1.8

2)下载解压到本地的一个目录, 如 /opt/apache-ant-1.8.2

3)打开eclipse->Preferences->Ant->Runtime

删除 Ant Home Entries下关于1.7的所有选项,

然后 点击 Ant Home ,点位到已经解压出来的ant( /opt/apache-ant-1.8.2)

一次点击确定/OK 即可。

 

转自:http://www.cnblogs.com/mengshu-lbq/archive/2011/03/24/1993764.html

通过admin

在Android中支持表情

最近项目需要支持表情,表情的添加和解析实现基本上是参照Android自身的SmileyParser,具体就不多讲了,直接贴上代码:
public class SmileyParser {
private static SmileyParser sInstance = null;

private Context mContext = null;
private Pattern mPattern = null;
private HashMap<String, Integer> mSmileyTextToId = null;
private final String[] mSmileyArrays =
{“/西瓜”,”89″,”/便便”,”59″,”/太陽”,”74″,”/偷笑”,”20″,”/傲慢”,”23″,”/再見”,”39″,”/凋謝”,”64″,”/發呆”,”3″,”/發怒”,”11″,”/閃電”,”54″,”/可愛”,”21″,”/豬頭”,”46″,”/咖啡”,”60″,”/哈欠”,”104″,”/鄙視”,”105″,”/委屈”,”106″,”/快哭了”,”107″,”/陰險”,”108″,”/親親”,”109″,”/嚇”,”110″,”/可憐”,”111″,”/菜刀”,”112″,”/啤酒”,”113″,”/籃球”,”114″,”/乒乓”,”115″,”/示愛”,”116″,”/瓢蟲”,”117″,”/抱拳”,”118″,”/勾引”,”119″,”/拳頭”,”120″,”/差勁”,”121″,”/愛你”,”122″,”/NO”,”123″,”/OK”,”124″,”/轉圈”,”125″,”/磕頭”,”126″,”/回頭”,”127″,”/跳繩”,”128″,”/揮手”,”129″,”/激動”,”130″,”/街舞”,”131″,”/獻吻”,”132″,”/左太極”,”133″,”/右太極”,”134″,”/吐”,”19″,”/蛋糕”,”53″,”/呲牙”,”13″,”/咒罵”,”31″,”/足球”,”57″,”/嘘”,”33″,”/困”,”25″,”/大兵”,”29″,”/大哭”,”9″,”/强”,”76″,”/奮鬥”,”30″,”/擁抱”,”49″,”/害羞”,”6″,”/尷尬”,”10″,”/右哼哼”,”103″,”/慪火”,”86″,”/勝利”,”79″,”/得意”,”4″,”/驚訝”,”14″,”/心碎”,”67″,”/驚恐”,”26″,”/微笑”,”0″,”/憨笑”,”28″,”/抓狂”,”18″,”/折磨”,”35″,”/發抖”,”41″,”/握手”,”78″,”/飛吻”,”85″,”/鼓掌”,”99″,”/撇嘴”,”1″,”/敲打”,”38″,”/暈”,”34″,”/月亮”,”75″,”/流汗”,”27″,”/流淚”,”5″,”/糗大了”,”100″,”/愛心”,”66″,”/左哼哼”,”102″,”/玫瑰”,”63″,”/疑問”,”32″,”/白眼”,”22″,”/睡”,”8″,”/冷汗”,”96″,”/示愛”,”65″,”/弱”,”77″,”/跳跳”,”43″,”/色”,”2″,”/炸彈”,”55″,”/壞笑”,”101″,”/衰”,”36″,”/刀”,”56″,”/調皮”,”12″,”/摳鼻”,”98″,”/酷”,”16″,”/禮物”,”69″,”/閉嘴”,”7″,”/難過”,”15″,”/饑餓”,”24″,”/飯”,”61″,”/骷髏”,”37″,”/愛情”,”42″};
private int[] mSmileyIds = null;
private String[] mSmileyTexts = null;
public static SmileyParser getInstance() {
if (sInstance == null) {
sInstance = new SmileyParser(GameDataMgr.getInstance().getActivity());

}

return sInstance;
}
private SmileyParser(Context context) {
// TODO Auto-generated constructor stub
mContext = context;
initSmileyIds();
mPattern = buildPattern();
mSmileyTextToId = buildSmileyRes();
}

private void initSmileyIds(){
mSmileyIds = new int[mSmileyArrays.length / 2];
mSmileyTexts = new String[mSmileyArrays.length /2];
for (int i = 0; i < mSmileyArrays.length / 2; i++) {
mSmileyTexts[i] = mSmileyArrays[i*2];
mSmileyIds[i] = Integer.parseInt(mSmileyArrays[i*2 + 1]);
}
}

public int[] getSmileyIDs(){
return mSmileyIds;
}

public int getSmileyResourceId(int smileyId){
String idString = “face_” + Integer.toString(smileyId);

int id = getResId(idString, mContext, R.drawable.class);

return id;
}

public static int getResId(String variableName, Context context, Class<?> c) {

try {
Field idField = c.getDeclaredField(variableName);
return idField.getInt(idField);
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}

public String[] getSmileyTexts(){
return mSmileyTexts;
}

Drawable getSmileyDrawable(int id){
Drawable drawable = null;
drawable = mContext.getResources().getDrawable(getSmileyResourceId(id));

return drawable;

}

/**
* 建立String – Id的对应关系
*/
private HashMap<String, Integer> buildSmileyRes(){

HashMap<String, Integer> smileyTextToId = new HashMap<String, Integer>(mSmileyIds.length);
for(int i = 0;i < mSmileyIds.length;++i){
smileyTextToId.put(mSmileyTexts[i], mSmileyIds[i]);
}

return smileyTextToId;
}

/**
* 建立匹配用的正则表达式
* @return
*/
private Pattern buildPattern(){
StringBuilder builder = new StringBuilder(mSmileyTexts.length * 3);
builder.append(‘(‘);
for (String  s:  mSmileyTexts) {
builder.append(Pattern.quote(s));
builder.append(‘|’);
}

builder.replace(builder.length() – 1, builder.length(), “)”);

return Pattern.compile(builder.toString());
}

/**
* 把文字转换为图片
* @param text
* @return
*/
public Spannable addSmileySpans(CharSequence text){
SpannableStringBuilder spBuilder = new SpannableStringBuilder(text);

Matcher matcher = mPattern.matcher(text);

while (matcher.find()) {
int id = mSmileyTextToId.get(matcher.group());
matcher.start(),matcher.end(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spBuilder.setSpan(new ImageSpan(mContext,getSmileyResourceId(id),ImageSpan.ALIGN_BASELINE),  matcher.start(),matcher.end(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

}

return spBuilder;
}
}

 

实现过程中遇到个小问题:往TextView中添加表情时,当文本既有表情也有文字时,显示是正常的,但是当文本中只有表情时,发现表情显示会偏上,而且上面有一部分被截断。TextView布局如下:

<TextView

android:id=”@+id/comment_item_content”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:layout_marginTop=”10dp”

android:layout_marginBottom=”10dp”

android:textSize=”16sp”

android:textColor=”#333333″

/>

 

 

解决方法:这里的问题应该是TextView在判断行距的时候是根据字体来判断的,但是当文本是表情的时候这个判断有些问题,导致行距过小,所以显示表情的时候就截断了,解决方法是设置一下TextView的最小高度,同时要指定文本向下对齐。另外在创建ImagePan的时候如果指定ImageSpan.ALIGN_BOTTOM对齐方式一般是不会出现这个问题的,但是这种方式下表情显示会偏下。修改后TextView布局如下:

<TextView

android:id=”@+id/comment_item_content”

android:layout_width=”fill_parent”

android:layout_height=”wrap_content”

android:layout_marginTop=”10dp”

android:layout_marginBottom=”10dp”

android:textSize=”16sp”

android:textColor=”#333333″

android:minHeight=”25dp”

android:gravity=”bottom”

/>