Java中使用ByteBuffer处理tcp粘包

 分类:Android, Java, Java语言 阅读 (16)  Java中使用ByteBuffer处理tcp粘包已关闭评论
8月 222019
 
一、通讯数据格式

通讯协议的约定格式如下:

每一个数据包的长度不固定,但是前四个字节代表的意义是固定的

数据的长度取决于第3个字段的值

名称 长度 其他说明  
协议头 2字节 固定的两个字节,用以给协议分段  
数据类型 1字节 数据内容的类型,用以区别多种内容  
数据长度 1字节 后面数据内容的长度,1字节可表示最大长度是255  
数据 n字节 n取决于上一个字节中的数值  
     
     
     
二、处理的难点
  1. 长度不固定,解析每一组数据时需要先想办法获取数据的长度
  2. 如果发送速度很快,收到一包可能有多组数据,需要一个while循环处理收到的一包的数据(这也就是粘包处理的初衷)
  3. 收到一包数据(本例中使用1024长度的缓冲区接收)可能将某个数据包截成两截,当前缓冲区未解析完的要保留下来,否则会丢数据
三、处理流程图
四、代码

在while(true)里面是读取tcp数据和进行粘包处理的代码

五、其他说明

  1. 串口通讯的粘包处理也可以使用此方法

java中的左移<<操作符

 分类:Java, Java语言 阅读 (12)  java中的左移<<操作符已关闭评论
8月 162019
 

对于正数来说,左移1位相当于这个数值翻倍,或者对于类型为int的负数来说,左移一位也是翻倍

但是对于类型小于int的类型,比如short和byte,本来以为左移并不会带符号,但经过测试不是这样的,看代码:

左移后为int型数值,本来以左移8位后,前16位会用0填充,但是没有,左移是带符号移位的,看运行结果如下:

好,我们再看看其他情况的。

1、byte为正数的情况,moveb = 2的运行结果

 

 

 Posted by on 2019-08-16

java中的右移>>和无符号右移>>>

 分类:Java, Java语言 阅读 (31)  java中的右移>>和无符号右移>>>已关闭评论
8月 062019
 

右移>>是指带符号右移,如果最高位的符号位为1,则右移时左侧补上的空位用1填充,否则用0填充

而无符号右移>>>,不管左侧最高位是1还是0,左侧补上的空位统统用0填充,如下面的例子:

看输出结果:

我们从第13位开始左移,即第二字节的101那里,左移后的结果如下,右侧应该有一些0用省略号代替了

101 11110100 01010100…..

我们再右移29位,即只剩下101,那么如果用>>前面将被补上很多的1,如果使用>>>,则会补很多0

 Posted by on 2019-08-06

gradle中修改生成的apk的文件

 分类:Android, Java 阅读 (14)  gradle中修改生成的apk的文件已关闭评论
8月 062019
 

在app/build.gradle中的android节点中增加如下代码:

生成的apk的文件名为myapp_debug.apk

 Posted by on 2019-08-06

Java数据类型转换的一些问题总结

 分类:Java, Java语言, Uncategorized 阅读 (33)  Java数据类型转换的一些问题总结已关闭评论
8月 062019
 
一、强制类型转换导致数据的变化

举例:short类型的值范围为-32768 ~ 32767,看下面这些对short值的操作

最后的输出结果为:

二、关于一些运算后生成的默认值

对于长度小于或者等于int的数据类型,如short, byte在进行如下运算后,其他运算结果为int型

<<,>>,>>>,&,|,^,+,-,*,/

如下代码,就会提示你将运算强制转换成short类型

三、有符号数转无符号数

可以通过&运算将有符号号转为无符号数,

对于byte类型& 0xFF,

对于short类型& 0xFFFF,

对于int类型& 0xFFFFFFFF

注意&运算完的结果是一个int型的值

下面看一个示例代码:

运行结果:

强制类型转换并没有改变符号。

四、负数的左移位操作

负数的左移位操作会使数据保持为负数

http://bcoder.com/java/right-shift-operator-in-java

 Posted by on 2019-08-06

ByteBuffer详解

 分类:Android, Java, Java语言 阅读 (107)  ByteBuffer详解已关闭评论
7月 022019
 
一、ByteBuffer的父类

  ByteBuffer的父类是Buffer类,意思为缓冲区类,ByteBuffer为字节缓冲区,当然他也可以处理int, long, char等基本数据类型。

  相比于Buffer类的其他继承类CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer,ByteBuffer类应用更广泛。

 
二、ByteBuffer的几个变量
position 表示当前bytebuffer的数据指针的位置。因为ByteBuffer还可以做为一个数据解析器,比如getShort()可以读出来一个short类型的数据,因此position也是很重要的。
limit 读取时可用作数据索引的上限(此时相当于数据的length – 1),但是写入时又不能有这个limit,否则会造成数据无法写入(写入时可设置limit = capacity)
mark 用于标记读取数据的起始位置,比如我们已经处理完成10个字节的数据,以后不用再管这10个字节,那么我们可以用mark()标记一下,然后配套的时候reset使position回到这个位置(比如后边的处理遇到了异常,或者遇到了不完整的数据)。
capacity 容量,很简单啦,就是最大允许的字节数量。
 
三、几个要点

  1、limit的设置,因为put和get系列函数只会影响position,不会影响limit,limit是数据的结尾位置,是相当的重要。

 
四、ByteBuffer类的方法
put(byte b) 向ByteBuffer中写入一个字节,并且如果成功position+1
put(byte[] src) 将参数中的byte数据写入bytebuffer中,并且如果成功position+src的长度
put(byte[] src, int offset, int length) 将src数组中,在offset处开始,长度为length的数据写入到ByteBuffer中,并且如果成功,position+length
compact() 其实就是删除已读过的数据,将position到limit之间的数据移动到0和limit-position处,并将mark重置为-1,position放到数据结尾,总结一下,就是可以继续写数据了,但是不能读数据。
limit() 获取当前的数据尾的位置(当然,前提是你设置过limit或者调用过flip)
limit(int newLimit) 设置数据的结尾位置。
rewind() 执行后position = 0, mark = -1,数据内容不变。应用场景:新put数据后,使用rewind将指针位置恢复到起始位置。
reset() 将position恢复到mark处的位置。
clear() 重置ByteBuffer的 position = 0; limit = capacity; mark = -1,实际数据内容无变化,其实这个函数会比较少用,不如rewind的实用性强,因为rewind不会改变limit。
remaining() 还未读取的数据limit – position,前提是你正确设置了limit的位置(所有数据的结尾位置)或者调用过flip()函数。
hasRemaining() 是否还有未读取的数据,前提是你正确设置了limit的位置(所有数据的结尾位置)或者调用过flip()函数。
mark() 将当前的position设备为mark(),mark要和reset配合使用。
discardMark() 将mark的位置重置为-1,貌似很少会单独使用这个函数,而是用rewind或者clear来重置mark值
flip() 其实这个函数的意思是,数据准备好了,可以读了,估计是reset、rewind、clear这些名字用的差不多了,起了一个这个难理解的名字,翻转,听着就让人晕。这个函数相当于下面两个函数的组合buf.limit(buf.position());
buf.rewind();。
get系列函数  
 Posted by on 2019-07-02

Android Studio中引入编译好的so文件

 分类:Android, Java 阅读 (146)  Android Studio中引入编译好的so文件已关闭评论
6月 252019
 

so文件单纯的放在源码libs目录下是不能编译到apk中的,需要下面的步骤才可以使用so文件。

一、首先将so文件放到app/src/main/libs目录下,so文件在libs目录下或者libs子目录下均可

二、修改build.gradle(app)文件,在defaultConfig节中加入下面代码

三、然后调用so文件就可以了

 Posted by on 2019-06-25

Android编译系统(mk)路径变量整理

 分类:Android, Java 阅读 (194)  Android编译系统(mk)路径变量整理已关闭评论
6月 062019
 

本文基于Android8.1系统源码,常量的定义在文件./build/make/core/envsetup.mk中

TOPDIR – 源码根目录

OUT_DIR – out目录,默认情况下是源码目录下的out目录,如果指定OUT_DIR_COMMON_BASE这个环境变量,则为这个变量指定的目录

TARGET_BUILD_TYPE – 是release还是debug的编译,在环境变量TARGET_BUILD_TYPE中指定

SOONG_OUT_DIR – soong目录,默认情况下是out/soong

DEBUG_OUT_DIR – TARGET_BUILD_TYPE为debug时会用到此目录,默认是out/debug

TARGET_OUT_ROOT – target目录,默认是out/target,debug时为out/debug/target

TARGET_PRODUCT_OUT_ROOT – 默认是out/target/product目录

TARGET_COMMON_OUT_ROOT – 默认是out/target/common目录,用于生成编译时的中间文件

PRODUCT_OUT – 默认是out/target/product/{设备型号},真正的编译生成文件的目录

TARGET_OUT – $(PRODUCT_OUT)/system目录

TARGET_OUT_GEN – $(PRODUCT_OUT)/gen

TARGET_OUT_EXECUTABLES – $(TARGET_OUT)/bin

TARGET_OUT_OPTIONAL_EXECUTABLES – $(TARGET_OUT)/xbin

TARGET_OUT_RENDERSCRIPT_BITCODE := $(TARGET_OUT_SHARED_LIBRARIES)

TARGET_OUT_JAVA_LIBRARIES :=$(PRODUCT_OUT)/system/framework

TARGET_OUT_APPS :=$(PRODUCT_OUT)/system/app

TARGET_OUT_APPS_PRIVILEGED :=$(PRODUCT_OUT)/system/priv-app

TARGET_OUT_KEYLAYOUT :=$(PRODUCT_OUT)/system/usr/keylayout

TARGET_OUT_KEYCHARS :=$(PRODUCT_OUT)/system/usr/keychars

TARGET_OUT_ETC :=$(PRODUCT_OUT)/system/etc

TARGET_OUT_FAKE := $(PRODUCT_OUT)/fake_packages

TARGET_OUT_TESTCASES := $(PRODUCT_OUT)/testcases

HOST_OUT_ROOT – host目录,默认是out/host

HOST_OUT

SOONG_HOST_OUT

HOST_CROSS_OUT

 Posted by on 2019-06-06

Android编译系统命令总结

 分类:Android, Java 阅读 (56)  Android编译系统命令总结已关闭评论
6月 062019
 

本文基于Android8.1系统源码

– lunch: lunch <product_name>-<build_variant>
– tapas: tapas [<App1> <App2> …] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user]
– croot: 回到源码根目录.
– cproject 回到当前项目的根目录
– m: 从根目录开始编译,相当于make整个系统.
– mm: 编译当前目录下的所有模块,但不包括他们的依赖.
– mmm: 编译指定目录下的所有模块,但不包括他们的依赖,.
To limit the modules being built use the syntax: mmm dir/:target1,target2.
– mma: 编译当前目录下的所有模块,包括他们的依赖.
– mmma: 编译指定目录下的所有模块和他们的依赖.
– provision: Flash device with all required partitions. Options will be passed on to fastboot.
– cgrep: 在所有的c或者c++文件中搜索某个关键字.
– ggrep: 在所有的gradle文件中搜索某个关键字.
– jgrep: 在所有的java文件中搜索某个关键字.
– resgrep: 在所有的资源文件中搜索某个关键字.
– mangrep: 在所有的AndroidManifest.xml文件中搜索某个关键字.
– mgrep: 在所有的Makefiles文件中搜索某个关键字.
– sepgrep: 在所有的sepolicy文件中搜索某个关键字.
– sgrep: 在所有的源代码文件中搜索某个关键字.
– rcgrep: 在所有的rc文件中搜索某个关键字
– godir: 跳转到包含某个文件的目录中某个关键字.

具体的函数实现可以查看build/envsetup.sh

 Posted by on 2019-06-06
4月 252019
 
一、启动init进程

init程序是Linux启动的和经一个程序

init程序的源文件在system/core/init/init.cpp里

二、解析init.rc

在init的main函数里,会先做一些系统初始化的工作,然后通过Parser对象来解析init.rc文件

parser对象的源文件在system/core/init/init_parser.cpp里面

在8.1系统源码里面,rc文件分别存在几个不同的目录里面

  • 主rc文件,init.rc在device/设备名/型号名/目录里,init.rc通过import的形式导入其他的rc文件
  • 还有一些其他的rc文件也在这个目录里面
  • init.zygote.rc文件在system/core/rootdir里面

但是在把系统镜像烧录到车机后,rc文件都在根目录/下

三、启动zygote

init进程解析完init.rc,将根据配置文件启动zygote

zygote是所有安卓服务和应用的父进程,承载着创建java虚拟机并且孵化安卓层的SystemServer和安卓应用的功能

zygote源码在frameworks/base/cmds/app_process/app_main.cpp中

四、启动SystemServer

在zygote中其实并没有fork出来一个SystemServer,而是先启动了java层的ZygoteInit类(通过runtime.start(“com.android.internal.os.ZygoteInit”, args, zygote))

当然fork是不能java层直接调用的,ZygoteInit.java中又通过Zygote类,Zygote类又通过jni调用了Linux层的函数fork出来了SystemServer

ZygoteInit.java的源码在:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

Zygote.java的源码在:frameworks/base/core/java/com/android/internal/os/Zygote.java

Zygote jni层的代码在:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

SystemServer启动后,会启动一大堆系统服务,安卓的基础运行环境就起来了,启动服务的几个函数为:

SystemServer的代码在:frameworks/base/services/java/com/android/SystemServer.java

五、Launcher的启动

在SystemServer的startOtherServices函数中调用了mActivityManagerService.systemReady,

在ActivityManagerService的systemReady函数中通过startHomeActivityLocked来启动Launcher

六、待续问题:

init.rc中的服务是怎么启动的

 Posted by on 2019-04-25