bcoder

关于Gson使用的一些深入研究

 分类:Android, Java 阅读 (1,364)  关于Gson使用的一些深入研究已关闭评论
7月 042019
 
一、Gson的复用

一直有一个担心,对于不同的数据类是否可以用同一个gson实例,今天研究了一下,不仅是可以,而且最好是这样使用。看代码Gson.java中的getAdapter方法,一开始就是在typeTokenCache这个缓存中找type相关的TypeAdapter。

因此复用一个gson的实例会省去匹配TypeAdapter(当然还有new Gson())的时间。

如下代码实例:

在我机器上测试不复用的话用时220-260毫秒之间(非常不稳定),复用120毫秒。

二、TypeToken是个什么东东?

TypeToken其实是为了给标识一个泛型类中的T,对于普通的类我们通过getClass().getName就可以得到类的标识,但是对于泛型类,只能得到泛型外层的类名,并不能得到泛型外层类+泛型类这样的全名。

如下代码:

输出结果是:java.util.ArrayList,而不是java.util.ArrayList<java.lang.Integer>

使用TypeToken来获取就是另外一种情况了,看下面的例子:

输出结果:

三、TypeAdapter的使用

 

四、一些类的作用

1. ConstructorConstructor – 返回一个函数可以给要操作的类新建一个实例对象。

2. InstanceCreator – 用于给没有无参数构造函数的类,创建一个实例的一个接口。解释:gson在反序列时需要创建类的一个实例,如果该类没有无参数构造函数(如:三方库中的类),则会出现异常,所以开发者需要新建一个类实现这个接口,并用该类可用的构造函数新建一个实例。并使用下面方式Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdInstanceCreator()).create();注册到gson中。

3. FutureTypeAdapter – 用于缓存TypeAdapter。为什么不用TypeAdapter的实例缓存?因为TypeAdapter是个虚类 :-D 

 

树莓派无键盘情况下配置无线网络

 分类:Others, Others 阅读 (2,815)  树莓派无键盘情况下配置无线网络已关闭评论
7月 032019
 

转载自:http://shumeipai.nxez.com/2017/09/13/raspberry-pi-network-configuration-before-boot.html

 

不算是什么新功能了,在树莓派3B发布后不久,树莓派官方 Raspbian 系统久加入了允许在开机前对 WiFi 网络进行配置的机制。

注意,这个方法仅适用于全新安装树莓派系统到 SD 卡之后没有做过任何 Wi-Fi 配置的情况下有效。如果你之前配置过 Wi-Fi,再用本方法系统会默认使用已有的配置而忽略这里的配置。因此建议使用前重新安装系统。

一、WiFi 网络配置

用户可以在未启动树莓派的状态下单独修改 /boot/wpa_supplicant.conf 文件配置 WiFi 的 SSID 和密码,这样树莓派启动后会自行读取 wpa_supplicant.conf 配置文件连接 WiFi 设备。

操作方法简单:将刷好 Raspbian 系统的 SD 卡用电脑读取。在 boot 分区,也就是树莓派的 /boot 目录下新建 wpa_supplicant.conf 文件,按照下面的参考格式填入内容并保存 wpa_supplicant.conf 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
country=CN
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
 
network={
ssid="WiFi-A"
psk="12345678"
key_mgmt=WPA-PSK
priority=1
}
 
network={
ssid="WiFi-B"
psk="12345678"
key_mgmt=WPA-PSK
priority=2
scan_ssid=1
}

说明以及不同安全性的 WiFi 配置示例:
#ssid:网络的ssid
#psk:密码
#priority:连接优先级,数字越大优先级越高(不可以是负数)
#scan_ssid:连接隐藏WiFi时需要指定该值为1

如果你的 WiFi 没有密码

1
2
3
4
network={
ssid="你的无线网络名称(ssid)"
key_mgmt=NONE
}

如果你的 WiFi 使用WEP加密

1
2
3
4
5
network={
ssid="你的无线网络名称(ssid)"
key_mgmt=NONE
wep_key0="你的wifi密码"
}

如果你的 WiFi 使用WPA/WPA2加密

1
2
3
4
5
network={
ssid="你的无线网络名称(ssid)"
key_mgmt=WPA-PSK
psk="你的wifi密码"
}

如果你不清楚 WiFi 的加密模式,可以在安卓手机上用 root explorer 打开 /data/misc/wifi/wpa/wpa_supplicant.conf,查看 WiFi 的信息。

二、开启 SSH 服务

如果通过 ssh 连接树莓派出现 Access denied 这个提示则说明 ssh 服务没有开启。要手动开启的话,和 WiFi 配置相似,同样在 boot 分区新建一个文件,空白的即可,文件命名为 ssh。注意要小写且不要有任何扩展名。
树莓派在启动之后会在检测到这个文件之后自动启用 ssh 服务。随后即可通过登录路由器找到树莓派的 IP 地址,通过 ssh 连接到树莓派了。(有关开启 SSH 服务的详细方法

如果需要远程桌面方式操作树莓派,可以通过 ssh 安装 xrdp,再用 Windows 的远程桌面客户端连接到树莓派

这个小技巧对于没有有线网卡、没有标准 USB 接口来直连键鼠,但集成了 WiFi 的树莓派 Zero W 尤其实用。

 Posted by on 2019-07-03

ByteBuffer详解

 分类:Android, Java, Java语言 阅读 (5,930)  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 阅读 (2,265)  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 阅读 (4,984)  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 阅读 (1,118)  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

一个计算角度的类

 分类:Android, Java 阅读 (768)  一个计算角度的类已关闭评论
4月 062019
 

本代码是个半成品,只作为参考

使用时,将此类作为android组件放到布局中即可

计算红线与绿线之间的角度,效果图如下:

代码如下:

 

 Posted by on 2019-04-06
3月 262019
 

一、必要的系统环境

  硬盘200G,内存4G+,交换分区(swap)4G+(可装完系统后添加,见后面错误处理部分)

二、下载源码,国内可以在清华镜像站下载 

  地址:https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/

  网上有很多教程,这里就不再累述了

三、然后安装编译时需要的三方库

sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip

安装open-jdk-8

sudo add-apt-repository ppa:openjdk-r/ppa

sudo apt-get update

sudo apt-get install openjdk-8-jdk

四、开始编译

  然后进入到Android源码目录,假设源码在用户根目录的aosp目录中:

cd ~/aosp

  然后初始化编译环境,使用下面的命令:

source build/envsetup.sh

  然后选择要编译的目标版本,先输入lunch命令

lunch

  然后在显示的列表中选择你的目标版本,输入相应的数字,按回车即可

  然后使用make开始编译,可以使用-j参数增加编译的线程以提高速度,线程数量为cpu核心数的2倍比较合适,比如你的cpu是4核的使用

make -j8

  就可以了

五、遇到的错误

1、错误1:

03:00:11 ckati failed with: signal: killed
build/core/main.mk:21: recipe for target ‘run_soong_ui’ failed
make: *** [run_soong_ui] Error 1

解决方案1:
增加swap空间
增加办法:
cd /
增加4个G的交换空间
sudo dd if=/dev/zero of=/swp bs=10M count=400
sudo mkswap /swp
sudo swapon /swp

用free -m查看交换空间的情况
这个方法增加的是临时交换空间,如需一直保留,需要其他操作
参考链接:
https://blog.csdn.net/yellow_hill/article/details/38894317

解决方案2:
export LC_ALL=C

 

2、错误2:
JackServer提示out of memory

解决方案1:
增加虚拟机内存至最少4G

解决方案2:
修改jack server内存
步骤:
打开./prebuilts/sdk/tools/jack-admin
找到下面这一行
JACK_SERVER_VM_ARGUMENTS=”${JACK_SERVER_VM_ARGUMENTS:=-Dfile.encoding=UTF-8}”
修改为
JACK_SERVER_VM_ARGUMENTS=”${JACK_SERVER_VM_ARGUMENTS:=-Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4096M}”
保存jack-admin文件
在命令行执行
./prebuilts/sdk/tools/jack-admin kill-server
./prebuilts/sdk/tools/jack-admin start-server
重启jack server
然后重新编译

3、错误3:

Error while parsing ‘/media/liuderu/新加卷/android-8.0.0_r1/out/target/common/obj/APPS/QuickSearchBox_intermediates/with-local/classes.dex.flags’:1

原因:路径中包含中文字符

4、错误4

FAILED: out/target/common/obj/PACKAGING/checktestapi-current-timestamp

先执行一次make update-api然后再执行make

 Posted by on 2019-03-26

Android中aidl开发步骤

 分类:Android, Java 阅读 (1,449)  Android中aidl开发步骤已关闭评论
2月 242019
 
一、常见问题回顾

1、为什么要使用aidl?

答:aidl可以进行跨进程通讯。

2、aidl文件是怎么变成java代码的?

答:aidl是Android interface definition language的缩写,是一种中间语言,用于描述跨进程通信时的业务逻辑。aidl文件是通过android sdk中的名为aidl的程序生成java文件的,一般在{android-sdk}/build-tools/{android版本}/这个目录下。

3、aidl中都可以使用哪些数据类型?

答:可以使用基本的java数据类型(除了short以外),比如int, boolean, String,也可以使用List类型,如果要使用自己的类则该类需要实现Parcelable接口。

4、服务端中aidl接口的改变会不会导致未更新aidl的客户端崩溃或者出现数据混乱?

答:假如服务端中aidl更新了,客户端未更新,客户端不会报错。假如更新的接口是在接口的尾部添加的,不会出现数据混乱,如果接口在中间添加则会引发后面的函数返回结果混乱。即aidl的函数列表顺序不能乱。

二、aidl服务端开发步骤(使用Android Studio)

1、新建一个android工程

2、新建一个Service,本例为MyAidlService.java

3、新建一个aidl文件,比如IMyAidlInterface

4、为此aidl增加一个函数,比如如下,然后使用Android Studio重新编译一下项目:

5、在MyAidlService.java中,定义一个内部类,继承自IMyAidlInterface.Stub,比如AidlInterfaceStub

6、在MyAidlService.java中定义一个AidlInterfaceStub对象并初始化,如AidlInterfaceStub mAidlInterfaceStub = new AidlInterfaceStub();

7、在MyAidlService.java的onBind函数中返回上一步定义的对象,这样服务端的开发就完成了。

三、aidl客户端开发步骤

1、新建一个android工程

2、将服务端的aidl文件拷贝到客户端工程中,要保持aidl文件所在相对路径与服务端的路径结构一致,即让这个aidl的包名和服务端的包名一致

3、在客户端的Activity中定义一个IMyAidlInterface的对象,如:mIClientAidlInterface

4、在Activity的定义一个ServiceConnection对象,并用new进行实例化,这个ServiceConnection对象会生成以下两个回调函数的结构onServiceConnected和onServiceDisconnected

5、在上一个函数的onServiceConnected的回调中给mIClientAidlInterface赋值,如下

6、通过上一步得到的mIClientAidlInterface就可以调用远端的函数了

7、哦哦、忘了还没有绑定服务,为方便简单说明,我们直接在onCreate里绑定远端服务吧,如下:

8、客户端到此也完成了,看后面的全部代码试着写一下吧

四、代码

客户端:

服务端:

五、完整代码下载地址

https://download.csdn.net/download/wintergoes/11200474

六、其他问题

1、为什么byte[]数组不能作为参数传递?

传递byte数组时需要添加上in、out、inout修饰符,如下:

 

 Posted by on 2019-02-24