2017年11月

java对象序列化学习

由于开发中的需要,要将数据变成字节流之后分片传输,学习一下Java里面将对象序列化的方法。

1.简单序列化的实现

最简单的序列化实现的方法,就是自定义数据类的时候继承java.io.Serializable接口。
所有继承了这个接口的类都可以被序列化,用ObjectOutputStream和ObjectInputStream可以分别将对象输出和输入。

public class myObject implements java.io.Serializable{
    //这是一个空接口,所以不需要任何实现,只是告诉别人这个类可以序列化
    //所有基本类型默认实现了Serializable接口,所以都是可以序列化的
    int arg1;
    String msg;
    
    //可以序列化的类型的数组也可以序列化
    byte[] data;
    float[] d3;
    
    //如果下面的类实现了Serializable接口,则可以序列化
    MyAnotherClass obj;

    //对于不知道是否可以序列化的类要慎用,不然会出错
    UnknowClass uobj;//可能会在序列化时报错!!!
}

如果一个类声明实现了Serializable接口,那么它里面的所有属性都必须可以序列化,否则会在序列化的时候报错!

2.自己控制序列化的实现

很多时候,在对象序列化的时候,我们不希望某些敏感属性被序列化,或者某些属性不能被自动序列化,这时候就需要我们手动实现序列化了。
手动实现序列化需要自定义类实现java.io.Externalizable接口,然后重写相关的方法,来完成对象的序列化与反序列化。

int arg1;
long time;
String msg;
@Override
public void readExternal(ObjectInput in)
    throws IOException, ClassNotFoundException {
    //在这里对对象进行反序列化
    //注意调用readXXX()与writeXXX()的先后顺序!
    time = in.readLong();
    msg = (String) in.readObject();//String用Object来读
    arg1 = in.readInt();
}

@Override
public void writeExternal(ObjectOutput out) throws IOException {
    //在这里对对象进行序列化
    //注意调用readXXX()与writeXXX()的先后顺序!
    out.writeLong(time);
    out.writeObject(msg);//String用Object来写
    out.writeInt(arg1);
}

手动序列化的时候,按照一定的顺序调用writeXXX()和readXXX()的方法,将需要的属性序列化。
序列化与反序列化时,先序列化的就先反序列化,上面的例子中,序列化的顺序是long、String、int,所以反序列化的顺序就是long、String、int。
序列化生成的对象,并不会调用类的构造方法,所以要在这两个方法中对其他属性进行必要的初始化。

3.serialVersionUID的特殊用处

在自定义类的时候,只要更改了一点点的源文件,编译出来的类便与之前序列化出来的数据流不兼容了,无法完成反序列化。
serialVersionUID的作用就是标识当前类的版本号。
版本号相同的类可以序列化与反序列化,反之则不行。
所以如果有可能,自定义的所有需要序列化的类都应该定义一个serialVersionUID属性

private static final long serialVersionUID = -5314860580001L;

通过控制serialVersionUID来控制序列化的流与类的兼容性。
只要serialVersionUID相同,无论类里的方法如何变,对应的属性总能正确地完成序列化与反序列化。

4.可以序列化的属性

并不是所有的属性都可以用Serializable接口自动序列化,自动序列化的属性需要满足以下条件:

  • 该属性的类本身可以序列化
  • 该属性不是静态(static)属性
  • 该属性不是瞬时(transient)属性

低功耗蓝牙 BLE 通讯篇

继续研究安卓上面蓝牙BLE编程,一部安卓设备模拟服务端,一部安卓手机模拟客户端,看着官方文档摸索如何用BLE在两部安卓设备之间交换数据。

0.客户端与服务端连接

在客户端通过扫描得到了服务端的BluetoothDevice对象之后,在BluetoothGattCallback对象里面定义以下回调:

@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
    if (status == BluetoothGatt.GATT_SUCCESS
            && newState == BluetoothGatt.STATE_CONNECTED){
        //通知服务端准备服务
        gatt.discoverServices();
    }
}

即连接上之后立即扫描服务端服务,服务端准备就绪之后,会在客户端触发以下回调:

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    //这里可以通过gatt获取到BluetoothGattService对象
    //通过BluetoothGattService可以获取BluetoothGattCharacteristic对象
}

至此,客户端与服务端的通讯必要对象就准备完毕了。

1.客户端向服务端发送数据

  • writeCharacteristic向服务端发送数据

客户端:

//data为byte[]数组,大小不能超过512Byte
characteristic.setValue(data);
//characteristic为要更新的BluetoothGattCharacteristic对象
writeCharacteristic(BluetoothGattCharacteristic)

方法来发送一个更新Characteristic的请求。
服务端会触发

onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value){
    //characteristic为要更新的BluetoothGattCharacteristic,但是其中的value并不是要更新的value
    //value才是新的值,客户端发送的数值
    characteristic.setValue(value);//服务端和客户端同步
    //要告诉客户端数据已经更新了
    //server是BluetoothGattServer实例
    server.sendResponse(device,requestId, BluetoothGatt.GATT_SUCCESS,offset,value);
}

接着,会在客户端触发

onCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){
    //如果在writeCharacteristic方法之前调用了
    //BluetoothGatt对象的beginReliableWrite()方法
    //此时服务端不会触发上述方法,但是会返回收到的数据
    //可以用之前的value在这里和characteristic的value比较,相同则数据完整
    //调用executeReliableWrite()才会触发服务端的回调
    //do Something...
}

至此,客户端向服务端发送数据的流程结束。

2.服务端向客户端发送数据

  • notifyCharacteristicChanged向客户端发送数据

首先在客户端注册characteristic的变化监听

gatt.setCharacteristicNotification(characteristic,true);

之后实现回调:

public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
    Log.d("WDS","Notify");
    //characteristic已经是更改后的了,直接读取value即可
    Log.d("WDS",new String(characteristic.getValue()));
    //doSomething...
}

客户端即可收到服务端的特性改变通知。
服务端要通知的时候:

characteristic.setValue("2256".getBytes());
//server是BluetoothGattServer实例,device为客户端对象
server.notifyCharacteristicChanged(device,characteristic,true);

客户端获取到通知之后,会触发服务端的回调:

@Override
public void onNotificationSent(BluetoothDevice device, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS){
        //通知发送成功
    }
}

至此,服务端向客户端发送数据的流程结束。

由于某些原因,直接下载gradle文件很容易失败。我们可以手动下载工程对应版本的gradle文件
之后打开

C:\Users\用户名\.gradle\wrapper\dists\gradle-x.x-all\一串数字字母文件夹\ 

然后将下载的zip包放进去(不用改名)
重启Android Studio即可正常分析、编译工程了~