shell 是一种脚本语言。
脚本:本质是一个文件,文件里面存放的是 特定格式的指令,系统可以使用脚本解析器 翻译或解析 指令 并执行(它不需要编译)
shell 既是应用程序 又是一种脚本语言(应用程序 解析 脚本语言)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
#!/bin/bash

# 第一行主要用于指定解释器(包括:bin/sh、bin/bash、usr/bin/sh、usr/bin/bash)

# 我是单行注释

# 执行脚本的方式有三种:sh test.sh、. test.sh、source test.sh
# 决定路径执行:./test.sh,这种执行方式需要可执行权限,给脚本添加可执行权限:chmod 777 test.sh
# ./test.sh 方式执行需要指定解释器

# 三种变量:局部变量、环境变量、shell变量

# 变量:
name="hello" # 等号两边不能有空格
echo ${name} # 输出普通变量

function test() {
local name="test hello" # 局部变量用local关键字修饰,local必须在函数中使用
echo ${name}
}
name="only_read"
readonly name # readonly修饰符,修饰只读变量
echo $name # 或者 ${name}
name1="name1"
unset name1 # 删除变量,不能删除只读变量,删除后的变量无法被访问

# 单引号和双引号字符串的区别
name2='name2' # 单引号字符串,字符串内不能转义,不能包含变量
name3="name3\"${name2}" # 双引号字符串,字符串内可以有转义,也可以包含变量
echo ${name3}

# 字符串拼接
str1="1""2""3"'4''5' # 字符串字面量拼接
str2='6'
str3=${str1}${str2} # 字符串变量拼接
echo $str3
str4=`date`'==='$str3 # 命令拼接
echo $str4

# 获取字符串长度的5种方式
echo "hello" | wc -L # 1、wc -L 获取当前行的长度
expr length ${str3} # 2、使用expr length可以获取string的长度
echo "abc" |awk -F "" '{print NF}' # 3、awk获取域的个数
echo "Alex" |awk '{print length($0)}' # 4、通过awk+length的方式获取字符串长度
str5=Alex
echo ${#str5} # 通5、过 ${#str5} 的方式获取字符串长度

# 提取字符串
str6=123456789.jpg
echo ${str6##*56} # 从左向右截取最后一个string后的字符串 ,输出:789.jpg
echo ${str6#*56} # 左向右截取第一个string后的字符串,输出:789.jpg
echo ${str6%%56*} # 从右向左截取最后一个string后的字符串,输出:1234
echo ${str6%56*} # 从右向左截取第一个string后的字符串,输出:1234
echo ${str6:1:3} # 截取字符串,输出:234

# 数组
echo "====数组==== 数组越界不会报错"
array=(1 2 3 4 5 6)
echo ${array} # 输出数组,默认输出角标为0的数据
echo ${array[5]} # 输出数组角标为5的数据
array[0]=7 # 数组赋值
echo ${array} # 输出数组
array=([0]=0 [1]=1 [2]=2 [3]=3 [4]=4 [5]=5 [6]=6) # 数组初始化或赋值
echo ${array[6]} # 输出数组角标为6的数据
for((i=0;i<7;i++)) # for循环遍历数组
do
echo "array[$i]=${array[$i]}"
done
echo ${array[*]} # 输出整个数组
echo "a len: ${#array[*]}" # 输出数组的长度
a=(1 2 3 4 5 6)
b=("hello" "zhaixue.cc")
c=(${a[*]} ${b[*]}) # 数组拼接
echo ${c[*]} # 输出整个数组
echo "c length: ${#c[*]}" # 输出:c length: 8
unset c[6] # 删除数组中的元素
echo "c length: ${#c[*]}" # 输出:c length: 7

# 参数传递
# $0 代表执行的文件名
# $1 代表传入的第1个参数
# $n 代表传入的第n个参数
# $# 参数个数
# $* 以一个单字符串显示所有向脚本传递的参数。
# $@ 与$*相同,但是使用时加引号,并在引号中返回每个参数
# $$ 脚本运行的当前进程号
# $! 后台运行的最后一个进程的ID
# $? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

# 运算符 需注意:条件表法式需要放在方括号之间,并且要有空格。使用expr进行计算时需要使用反引号。
# 加法 expr $a + $b
# 减法 expr $a - $b
# 乘法 expr $a \* $b
# 除法 expr $b / $a
# 取余 expr $b % $a
# 赋值 a=$b
# 相等 [ $a == $b ]
# 不相等 [ $a != $b ]
a=10
b=20
c=`expr $a + $b` # 加法运算
echo "a + b : $c"

# 关系运算符
# 关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
# 关系用 [] 来表示,
echo "====关系运算符===="
a=1
b=2
# 检测两个数是否相等 [ $a -eq $b ] -eq
if [ $a == $b ]
then
echo "a和b相等"
else
echo "a和b不相等"
fi

# 或者

if [ $a -eq $b ]
then
echo "a和b相等"
else
echo "a和b不相等"
fi

# 检测两个数是否不相等 [ $a -ne $b ] -ne
if [ $a != $b ]
then
echo "a和b不相等"
else
echo "a和b相等"
fi

# 或者

if [ $a -ne $b ]
then
echo "a和b不相等"
else
echo "a和b相等"
fi

# 检测左边的数是否大于右边的 [ $a -gt $b ] -gt
if [ $a -gt $b ]
then
echo "a大于b"
else
echo "a等于b或a小于b"
fi

# 检测左边的数是否小于右边的 [ $a -lt $b ] -lt
if [ $a -lt $b ]
then
echo "a小于b"
else
echo "a等于b或a大于b"
fi

# 检测左边的数是否大于等于右边的 [ $a -ge $b ] -ge
if [ $a -ge $b ]
then
echo "a大于等于b"
else
echo "a小于b"
fi

# 检测左边的数是否小于等于右边的 [ $a -le $b ] -le
if [ $a -le $b ]
then
echo "a小于等于b"
else
echo "a大于b"
fi


# 布尔运算符
# 非运算 [ ! false ] !
# 或运算 [ $a -lt 20 -o $b -gt 100 ] -o
# 与运算 [ $a -lt 20 -a $b -gt 100 ] -a

# 逻辑运算符
# 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] &&
# 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] ||

# 布尔运算符和逻辑运算符的区别:
# 语法上,逻辑运算需要双括弧,布尔运算只需要单大括弧功能上,逻辑运算具有特殊的短路功能,
# 即是在AND运算中第一个表达式为false时则不执行第二个表达式,
# 在OR运算中第一个表达式为true时不执行第二个表达式。

# 字符串运算符
# 检测两个字符串是否相等 [ $a = $b ] =
# 检测两个字符串是否不相等 [ $a != $b ] !=
# 检测字符串长度是否为0 [ -z $a ] -z
# 检测字符串长度是否不为 0 [ -n “$a” ] -n
# 检测字符串是否为空 [ $a ] $

# 文件测试运算符
# 检测文件是否是块设备文件 [ -b $file ] -b file
# 检测文件是否是字符设备文件 [ -c $file ] -c file
# 检测文件是否是目录 [ -d $file ] -d file
# 检测文件是否是普通文件(既不是目录,也不是设备文件) [ -f $file ] 返回 true -f file
# 检测文件是否设置了 SGID 位 [ -g $file ] -g file
# 检测文件是否设置了粘着位(Sticky Bit) [ -k $file ] -k file
# 检测文件是否是有名管道 [ -p $file ] -p file
# 检测文件是否设置了 SUID 位 [ -u $file ] -u file
# 检测文件是否可读 [ -r $file ] -r file
# 检测文件是否可写 [ -w $file ] -w file
# 检测文件是否可执行 [ -x $file ] -x file
# 检测文件是否为空(文件大小是否大于0) [ -s $file ] -s file
# 检测文件(包括目录)是否存在 [ -e $file ] -e file
file="/home/westos/Desktop/textcpp/test.sh"
if [ -e $file ]
then
echo "文件存在"
else
echo "文件不存在"
fi

# 运算指令
# (( )) 可以直接使用双圆括弧计算其中的内容,如((var=a+b))
# let 在计算表达式的时候我们可以直接使用let,如let var=a+b。
# expr
var=`expr a+b`
echo $var
# bc计算器 :bc计算器支持shell中的小数进行运算,并且可以交互式或者非交互式的使用
var=$(echo "(1.1+2.1)"|bc)
echo $var
# $[]: 计算中括弧中的内容,如echo $[1+2]


# 控制语句
a=1
b=1
# if-fi
if [ $a -eq $b ]
then
echo "a等于b"
fi

# if-else-fi
if [ $a -eq $b ]
then
echo "a等于b"
else
echo "a不等于b"
fi

# if else-if else
if [ $a -gt $b ]
then
echo "a大于b"
elif [ $a -lt $b ]
then
echo "a小于b"
else
echo "a等于b"
fi


# for循环
for a in 1 5 3 4
do
echo ${a} # 一次输出 1 5 3 4
done

array=(1 2 3 4 5 6 7 8)
for((i=0;i<8;i++)) # for循环遍历数组
do
echo "array[$i]=${array[$i]}"
done

for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done

# while循环

a=1
b=4
while [ $a -lt $b ]
do
echo "${a}"
let a++
done

# 无限循环
# for (( ; ; ))
# do
# echo 111
# done

# while true
# do
# echo 222
# done

# until循环
# until 循环执行一系列命令直至条件为 true 时停止
a=1
until [ $a -gt 3 ]
do
echo "until循环:$a"
let a++
done

# 跳出循环

var=1
while(( $var < 5 ))
do
if(( $var>3 ))
then
echo "break跳出循环"
break
fi
echo "$var"
var=`expr $var + 1`
done


var=1
while(( $var < 5 ))
do
echo "$var"
var=`expr $var + 1`
if [ $var -gt 2 ] && [ $var -lt 4 ]
then
echo "continue跳出循环:$var"
continue
elif [ $var -eq 4 ]
then
echo "break跳出循环"
break
fi
done


# case-esac多选择语句
echo "====case-esac多选择语句===="
var=100
case $var in # 分支语句开始
10)
echo "输出10"
;; # 相当于break
100)
echo "输出100"
;; # 相当于break
esac # 分支语句结束


# select-in语句
# select in是shell中独有的一种循环,非常适合终端的交互场景,它可以显示出带编号的菜单,
# 用户输入不同编号就可以选择不同的菜单,并执行不同的功能
echo "What is your favourite OS?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other";
do
echo "You have selected $var"
break;
done


# 函数

function funName() { # function可以省略
echo "执行了funName函数"
}
funName # 执行函数

funWithParam(){
echo "第一个参数为 $1 !"
echo "第十个参数为 ${10} !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73 # 执行函数并传递参数


# 重定向
#一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:
# 1、标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
# 2、标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
# 3、标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

# > 标准输出覆盖重定向:将命令的输出重定向输出到其他文件中
# >> 标准输出追加重定向:将命令的输出重定向输出到其他文件中
# >& 标识输出重定向:将一个标识的输出重定向到另一个标识的输入
# < 标准输入重定向:命令将从指定文件中读取输入而不是从键盘输入
# | 管道符,从一个命令中读取输出并作为另一个命令的输入


# 输入重定向
# 可以让命令从文件中获取,这样本来的命令需要从标准输入stdin中获取,
# 转换为从我们的指定文件中获取。这样本来需要从键盘输入的命令就会转移到文件读取内容
# command1 < file
# 0<1.txt cat # 重定向输入流

# 输出重定向
# 输出重定向也是将本来需要输出标准输出文件stdout中转化为我们的指定文件中
# cat 1.txt > 2.txt # 将1.txt的内容覆盖到2.txt
# cat 1.txt 1> 2.txt # 将1.txt的内容覆盖到2.txt
# cat 1.txt >> 2.txt # 将1.txt的内容追加到2.txt
# cat 1.txt 1>> 2.txt # 将1.txt的内容追加到2.txt

# 标准错误文件重定向
# 借助标准错误文件的文件描述符来重定向stderr
# command 2>file
# 将stdout标准输出文件和stderr标准错误文件合并重定向到一个指定文件中
# command > file 2>&1

# /dev/null 文件
# 如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null中,
# /dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;
# 如果尝试从该文件读取内容,那么什么也读不到。
# 但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
# command > /dev/null

原文链接:https://www.jianshu.com/p/4599ef826895

一、Linux环境

1.NDK下载链接:https://github.com/android/ndk/wiki/Unsupported-Downloads(尽量选择R17版本)

2.解压压缩包:unzip xxx.zip

3.配置环境变量:sudo gedit /etc/profile (在最底部配置环境)

1
2
NDKRO0T=/root/ndk/android-ndk-r17c //(设置一个ndk变量,解压的目录)
export PATH=SNDKROOT:SPATH //(配置变量,保存后退出)

4.立即生效:source /etc/profile

5.验证是否成功:ndk-build

1
2
3
4
//出现以下内容说明配置成功
Android NDK: Could not find application project directory !
Android NDK: Please define the NDK PROJECT PATH variable to point to it
/root/ndk/android-ndk-r17c/build/core/build-local,mk:151: *** Android NDK: Aborting
一、动态库和静态库

【1】生成动态库

动态库在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库。Linux中后缀名为“.so”。
动态库节省空间:如果一个动态库被两个程序调用,那么这个动态库只需要在内存中。

1
2
3
4
5
6
7
8
9
10
【1】分步骤生成动态库

g++ -fPIC -c Hello.cpp -o Hello.o
g++ -shared -o libHello.so Hello.o

【2】一步生成动态库

g++ -fPIC -shared -o libHello.so Hello.o

g++ -fPIC -shared -o libHello.so Hello.cpp

【2】生成静态库

静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。Linux中后缀名为”.a”。
静态库节省时间:不需要再进行动态链接,需要调用的代码直接就在代码内部。

1
2
3
ar -crv libHello.a Hello.o

ar -crv libHello.a Hello.cpp

【3】链接静态库(生成可执行文件)

1
2
3
g++ Hello.cpp -o Hello -static -L. -l Hello
-L指定库目录,.表示当前目录。
-l Hello 指定libHello.a静态库。

【4】-I 添加搜索头文件的路径(大写i)

系统默认头文件搜索路径大致为

1
2
3
4
当前目录(如果是双引号包含的话) -->  /usr/include   --> /usr/local/include

// 添加/home/include 到搜索路径
g++ test.cpp -I/home/include

【5】-L 指定要链接的库所在目录,-l 指定路径寻找库文件

1
2
3
4
5
6
链接时库搜索路径  /lib  -->  /usr/lib   -->/usr/local/lib
运行时库搜索路径 /lib --> /usr/lib

// -L 将/lib 添加到库搜索目录,
// -l 将 libmt.so 库文件链接到项目
g++ test.cpp -L/usr/local/lib -lfmt

【6】查看可执行文件符号

1
nm main

【7】默认优先使用动态库

1
gcc main.c -L. -lTest -o main

【8】强制使用静态库

1
2
3
#-Wl 表示传递给 ld 链接器的参数
#最后的 -Bdynamic 表示 默认仍然使用动态库
gcc main.c -L. -Wl,-Bstatic -lTest -Wl,-Bdynamic -o main

【9】打包 .a 到so

1
2
3
4
#打包 .a 到so
#--whole-archive: 将未使用的静态库符号(函数实现)也链接进动态库
#--no-whole-archive : 默认,未使用不链接进入动态库
gcc -shared -o libMain.so -Wl,--whole-archive libMain.a -Wl,--no-whole-archive

【10】其它命令

–sysroot=XX
使用XX作为这一次编译的头文件与库文件的查找目录,查找下面的 usr/include usr/lib 目录。

1
g++ --sysroot=C:\Users\94317\AppData\Local\Android\ndk\android-ndk-r20b\sysroot Hello.cpp

-isysroot XX
头文件查找目录,覆盖–sysroot ,查找 XX/usr/include

-isystem XX
指定头文件查找路径(直接查找根目录)

-IXX
头文件查找目录

优先级:
-I -> -isystem -> sysroot

二、gcc和g++的使用

GCC编译过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1)预处理,C 编译器对各种预处理命令进行处理,包括头文件包含、宏定义的扩 展、条件编译的选择等,后缀是 .i文件。
test.c是c代码,预处理参数:-E ;另起名称:-o
gcc -E test.c –o test.i #得到 .i文件

2)编译,将预处理得到的源代码文件,进行“翻译转换”,产生出机器语言的目标 程序,得到机器语言的汇编文件,后缀是 .s文件。
编译参数:-S
gcc -S test.i #得到 .s文件

3)汇编,将汇编代码翻译成了机器码,但是还不可以运行,后缀是 .o文件。
编译参数:-c
gcc -c test.s #得到 .o文件

4)链接,处理可重定位文件,把各种符号引用和符号定义转换成为可执行文件中 的合适信息,通常是虚拟地址,后缀是 .out 文件或者可执行文件
gcc test.c #在gcc命令紧跟test源文件名,会有一个 .out 文件
gcc test.c -o test01.out #通过 -o 选项来自定义文件名test01.out文件
gcc test.c -o #/home/user/local/test.out # 在路径/home/user/local/下生成.out文件
./test.out #运行

img

【1】预处理

1
2
3
4
5
6
7
8
9
不输出文件:
gcc -E Hello.cpp(同样适用于c文件)

g++ -E Hello.cpp(同样适用于c文件)

输出文件:(将预处理信息输出到Hello.i)
gcc -E Hello.cpp -o Hello.i(同样适用于c文件)

g++ -E Hello.cpp -o Hello.i(同样适用于c文件)

【2】转换为汇编文件

1
2
3
4
5
6
7
8
9
10
11
12
13
不指定预处理文件:
gcc -S Hello.cpp // 生成Hello.s文件(同样适用于c文件)
gcc -S Hello.cpp -o Hello.s // 生成Hello.s文件(同样适用于c文件)

g++ -S Hello.cpp // 生成Hello.s文件(同样适用于c文件)
g++ -S Hello.cpp -o Hello.s // 生成Hello.s文件(同样适用于c文件)

指定预处理文件:
gcc -S Hello.i // 生成Hello.s文件(同样适用于c文件)
gcc -S Hello.i -o Hello.s // 生成Hello.s文件(在windows上测试失败,并没有生成Hello.s,c文件也同样如此)

g++ -S Hello.i // 生成Hello.s文件(同样适用于c文件)
g++ -S Hello.i -o Hello.s // 生成Hello.s文件(同样适用于c文件)

【3】编译(生成目标文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
不指定汇编文件:
gcc -c Hello.cpp // 生成目标文件Hello.o(同样适用于c文件)
gcc -c Hello.cpp -o Hello.o // 生成目标文件Hello.o(同样适用于c文件)

g++ -c Hello.cpp // 生成目标文件Hello.o(同样适用于c文件)
g++ -c Hello.cpp -o Hello.o // 生成目标文件Hello.o(同样适用于c文件)

指定汇编文件:
gcc -c Hello.s // 生成目标文件Hello.o(同样适用于c文件)
gcc -c Hello.s -o Hello.o // 生成目标文件Hello.o(同样适用于c文件)

g++ -c Hello.s // 生成目标文件Hello.o(同样适用于c文件)
g++ -c Hello.s -o Hello.o // 生成目标文件Hello.o(同样适用于c文件)

【4】链接(生成可执行文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
不指定目标文件:
gcc Hello.cpp // 链接失败,不能生成可执行文件(适用于c文件,生成a.exe)
gcc Hello.cpp -o Hello // 链接失败,不能生成可执行文件(适用于c文件,生成Hello.exe)
gcc Hello.cpp -o Hello.exe // 链接失败,不能生成可执行文件(适用于c文件,生成Hello.exe)
gcc -o Hello Hello.cpp // 链接失败,不能生成可执行文件(适用于c文件,生成Hello.exe)

g++ Hello.cpp // 生成a.exe文件(适用于c文件,生成a.exe)
g++ Hello.cpp -o Hello // 生成Hello.exe文件(适用于c文件,生成Hello.exe)
g++ Hello.cpp -o Hello.exe // 生成Hello.exe文件(适用于c文件,生成Hello.exe)
g++ -o Hello Hello.cpp // 生成Hello.exe文件(适用于c文件,生成Hello.exe)
g++ -o Hello.exe Hello.cpp // 生成Hello.exe文件(适用于c文件,生成Hello.exe)

指定目标文件:
gcc Hello.o // 链接失败,不能生成可执行文件(适用于c文件,生成a.exe)
gcc Hello.o -o Hello // 链接失败,不能生成可执行文件(适用于c文件,生成Hello.exe)
gcc Hello.o -o Hello.exe // 链接失败,不能生成可执行文件(适用于c文件,生成Hello.exe)

g++ Hello.o // 生成a.exe文件(适用于c文件,生成a.exe)
g++ Hello.o -o Hello // 生成Hello.exe文件(适用于c文件,生成Hello.exe)
g++ Hello.o -o Hello.exe // 生成Hello.exe文件(适用于c文件,生成Hello.exe)

【5】执行可执行文件

1
2
3
.\Hello

.\Hello.exe
3、gcc/g++多文件多目录

同目录多文件的情况下:

1
2
3
4
5
6
gcc Hello.c A.c B.c -o Hello
或使用通配符:
gcc *.c -o Hello

如果将头文件放入单独的文件夹中(header文件夹):
gcc -I header *.c -o Hello
4、gcc/g++常用编译选项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
【1】-o FILE: 指定输出文件名,在编译为目标代码时,这一选项不是必须的。
如果FILE没有指定,缺省文件名是a.out;
【2】-c: 只编译生成目标文件,不链接;
【3】-Wall: 允许发出gcc能提供的所有有用的警告,也可以用-W(warning)来标记指定的警告;
g++ -W Hello.o -o Hello
g++ -Wall Hello.o -o Hello
【4】-Werror: 把所有警告转换为错误,以在警告发生时中止编译过程;
g++ -Werror Hello.o -o Hello
【5】-v: 显示在编译过程的每一步中用到的命令;
g++ -v Hello.o -o Hello
【6】-static: 链接静态库,即执行静态链接,g++默认是链接动态库,如果需要链接静态库需要使用本选项进行指定;
【7】-g: 在可执行程序中包含标准调试信息, 使用该选项生成的可执行文件可以用gdb工具进行调试;
【8】-w: 关闭所有警告,建议不要使用该选项
【9】-shared: 生成共享目标文件。通常用在建立共享库时;
【10】-On: 这是一个优化选项,如果在编译时指定该选项,则编译器会根据n的值(n取0到3之间)对代码进行不同程度的优化,其中-O0 表示不优化,n的值越大,优化程度越高;
【11】-L: 库文件依赖选项,该选项用于指定编译的源程序依赖的库文件路径,库文件可以是静态链接库,也可以是动态链接库,linux系统默认的库路径是/usr/lib,如果需要的库文件不在这个路径下就要用-L指定
g++ foo.cpp -L/home/lib -lfoo -o foo
【12】-I: 该选项用于指定编译程序时依赖的头文件路径,linux平台默认头文件路径在/usr/include下,如果不在该目录下,则编译时需要使用该选项指定头文件所在路径
gcc foo.cpp -I/home/include -o foo

【1】ndkBuild配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
android {
compileSdk 32

defaultConfig {
// ...
externalNativeBuild {
ndkBuild {
abiFilters 'armeabi-v7a'
}
}
ndk { // "armeabi-v7a", "arm64-v8a"
abiFilters 'armeabi-v7a'
}
}

// ...

externalNativeBuild {
ndkBuild{
path file('src/main/cpp/Android.mk')
}
}

在模块下的 build.gradle 中配置 ndkBuild,确认CPU架构以及Android.mk路径。

【2】将源码编译成动态库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 每个Android.mk文件必须以定义LOCAL_PATH为开始
# 宏my-dir则由Build System提供,返回包含Android.mk的目录路径
LOCAL_PATH := $(call my-dir)

# 清除所有LOCAL_开头的变量,除了LOCAL_PATH
include $(CLEAR_VARS)

# 模块名称,需要保证模块名称的唯一性
LOCAL_MODULE := native-lib

# 指定将要打包的源码,多个文件用空格隔开,如果需要换行,在换行处添加“\”
LOCAL_SRC_FILES := native-lib.cpp

# 负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并决定编译成什么
# BUILD_STATIC_LIBRARY:编译为静态库
# BUILD_SHARED_LIBRARY:编译为动态库
# BUILD_EXECUTABLE:编译为可执行程序
include $(BUILD_SHARED_LIBRARY)

编译之后,动态库libnative-lib.so会在模块下的 build/intermediates/ndkBuild/ 目录下生成。

img

【3】Android.mk 编译时日志

1
2
3
4
#输出字符串的方式有如下三种,程度越来越高,有点像Android里面的Log等级
$(info string) #一般打印信息,仅仅是显示调试信息
$(warning string) #警告级别打印信息,不会停止脚本运行,一般用于严重警告
$(error string) #错误级别,会停止编译直接退出来

【4】c/c++代码日志在AS控制台上现实

1
2
3
4
5
6
7
# 加载NDK的日志库
LOCAL_LDLIBS := -llog
# 负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并决定编译成什么
# BUILD_STATIC_LIBRARY:编译为静态库
# BUILD_SHARED_LIBRARY:编译为动态库
# BUILD_EXECUTABLE:编译为可执行程序
include $(BUILD_SHARED_LIBRARY)

需要在 include $(BUILD_SHARED_LIBRARY) 前面添加 LOCAL_LDLIBS := -llog,加载NDK中自带的日志库。

在源代码中定义宏:

1
2
3
4
5
6
#define LOG_TAG "native-lib"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

在源代码中,打印日志到AS控制台:

1
LOGI("start thread task");

【5】Android.mk 生成动态库

1
2
3
4
5
6
7
8
9
10
11
12
LOCAL_PATH := $(call my-dir)
# 清除所有LOCAL_开头的变量,除了LOCAL_PATH
include $(CLEAR_VARS)
# 模块名称,需要保证模块名称的唯一性
LOCAL_MODULE := Test
# 指定将要打包的源码,多个文件用空格隔开,如果需要换行,在换行处添加“\”
LOCAL_SRC_FILES := Test.cpp
# 负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并决定编译成什么
# BUILD_STATIC_LIBRARY:编译为静态库
# BUILD_SHARED_LIBRARY:编译为动态库
# BUILD_EXECUTABLE:编译为可执行程序
include $(BUILD_SHARED_LIBRARY)

编译之后, 自动生成动态库,可以在build的以下目录中找到。

img

【6】使用预先编译好的动态库

编写mk:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 每个Android.mk文件必须以定义LOCAL_PATH为开始
# 宏my-dir则由Build System提供,返回包含Android.mk的目录路径
LOCAL_PATH := $(call my-dir)

# 清除所有LOCAL_开头的变量,除了LOCAL_PATH
include $(CLEAR_VARS)
# 模块名称,需要保证模块名称的唯一性
LOCAL_MODULE := Test
# 指定将要打包的动态库
LOCAL_SRC_FILES := libTest.so
# PREBUILT_SHARED_LIBRARY:动态库
# PREBUILT_STATIC_LIBRARY:静态库
include $(PREBUILT_SHARED_LIBRARY)

# 清除所有LOCAL_开头的变量,除了LOCAL_PATH
include $(CLEAR_VARS)
# 模块名称,需要保证模块名称的唯一性
LOCAL_MODULE := native-lib
# 指定将要打包的源码,多个文件用空格隔开,如果需要换行,在换行处添加“\”
LOCAL_SRC_FILES := native-lib.cpp
# 加载Test库
LOCAL_SHARED_LIBRARIES := Test
# 加载NDK的日志库
LOCAL_LDLIBS := -llog
# 负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息,并决定编译成什么
# BUILD_STATIC_LIBRARY:编译为静态库
# BUILD_SHARED_LIBRARY:编译为动态库
# BUILD_EXECUTABLE:编译为可执行程序
include $(BUILD_SHARED_LIBRARY)

include $(PREBUILT_SHARED_LIBRARY) 预编译动态库,LOCAL_SHARED_LIBRARIES := Test 加载动态库到apk中:

img

在c++源码中声明libTest.so中的函数

1
2
// 如果是C,需要添加 extern "C"{}
extern int test();

如果调用的是C代码,需要更改为:

1
2
3
extern "C"{
extern int test();
}

【7】变量和宏

定义自己的任意变量。在定义变量时请注意,NDK 构建系统会预留以下变量名称:

  • LOCAL_ 开头的名称,例如 LOCAL_MODULE
  • PRIVATE_NDK_APP 开头的名称。构建系统在内部使用这些变量。
  • 小写名称,例如 my-dir。构建系统也是在内部使用这些变量。

如果为了方便而需要在 Android.mk 文件中定义自己的变量,建议在名称前附加 MY_

【8】其它语法详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
1、`LOCAL_MODULE` 可以设置模块名称:

LOCAL_MODULE := Test

2、使用 `LOCAL_MODULE_FILENAME` 可以覆盖 `LOCAL_MODULE` 设置的模块名称。

LOCAL_MODULE_FILENAME := libTest

3、LOCAL_ARM_MODE

缺省模式下,ARM目标代码被编译为thumb模式。每个指令16位。如果指定此变量为'arm',则指令为32位。

LOCAL_ARM_MODE := arm

4、TARGET_ARCH

目标CPU架构名,如果为“arm” 则声称ARM兼容的指令,与CPU架构版本无关。

ifeq ($(TARGET_ARCH),arm)
...
endif

5、TARGET_PLATFORM

目标平台的名字,对应android版本号,取值包括:android-8、android-9...android-21。

ifeq ($(TARGET_PLATFORM),android-8)
...
endif

6、TARGET_ARCH_ABI:cpu架构

取值包括:

32位:armeabi、armeabi-v7a、x86、mips;
64位:arm64-v8a、x86_64、mips64;

ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
$(warning armeabi-v7a)
endif
ifeq ($(TARGET_ARCH_ABI),armeabi)
$(warning armeabi)
endif
ifeq ($(TARGET_ARCH_ABI),mips)
$(warning mips)
endif
ifeq ($(TARGET_ARCH_ABI),x86)
$(warning x86)
endif

7、NDK_MODULE_PATH

NDK_MODULE_PATH 是一个环境变量,不是Android.mk中设置的变量,
可以在 Android.mk中设置NDK_MODULE_PATH:

$(call import-add-path,$(LOCAL_PATH))
$(call import-add-path,$(LOCAL_PATH)/../external)

环境变量可以设置多个,以上配置只设置了两个。

$(call import-module,android/cpufeatures)
$(call import-module,freetype2/prebuilt/android)
$(call import-module,platform/android)
$(call import-module,png/prebuilt/android)

以上配置的意思是:在环境变量的目录下:
寻找android目录,在android目录下寻找cpufeatures目录;
寻找freetype2目录,在freetype2目录下寻找prebuilt目录,在prebuilt目录下寻找android目录;
寻找platform目录,在platform目录下寻找android目录;
寻找png目录,在png目录下寻找prebuilt目录,在prebuilt目录下寻找android目录。

8、LOCAL_CPP_EXTENSION
(可选)指出C++扩展名。

LOCAL_CPP_EXTENSION := .cxx

从NDK R7后,可以写多个:

LOCAL_CPP_EXTENSION := .cxx .cpp .cc

9、LOCAL_CPP_FEATURES
(可选)用来指定C++ features。

LOCAL_CPP_FEATURES := rtti
LOCAL_CPP_FEATURES := exceptions

10、LOCAL_C_INCLUDES

一个可选的path列表,相对于NDK ROOT目录,编译时将会把这些目录附上,主要为了头文件的引用。

LOCAL_C_INCLUDES := sources/foo
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo

11、LOCAL_CFLAGS

(可选)在编译C/C++ source 时添加如Flags,用来附加编译选项。
注意:不要尝试在此处修改编译的优化选项和Debug等级,它会通过您Application.mk中的信息自动指定。

-Wall:是打开警告开关。

-O:代表默认优化,可选:-O0不优化,-O1低级优化,-O2中级优化,-O3高级优化,-Os代码空间优化。

-g:是生成调试信息,生成的可执行文件具有和源代码关联的可调试的信息。

-fopenmp:OpenMp是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,
用于共享内存并行系统的多处理器程序设计的一套指导性的编译处理方案(Compiler Directive)。
OpenMP支持的编程语言包括C语言、C++和Fortran;而支持OpenMp的编译器包括Sun Compiler,GNU Compiler和Intel Compiler等。
OpenMp提供了对并行算法的高层的抽象描述,程序员通过在源代码中加入专用的pragma来指明自己的意图,
由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。
当选择忽略这些pragma,或者编译器不支持OpenMp时,程序又可退化为通常的程序(一般为串行),
代码仍然可以正常运作,只是不能利用多线程来加速程序执行。

-D:增加全局宏定义

-ffast-math:浮点优化选项,极大地提高浮点运算速度。

-mfloat-abi=softfp 浮点运算

12、LOCAL_CPPFLAGS

C++ Source 编译时添加的C Flags,这些Flags将出现在LOCAL_CFLAGS flags 的后面。

13、LOCAL_WHOLE_STATIC_LIBRARIES

静态库全链接,不同于LOCAL_STATIC_LIBRARIES,类似于使用--whole-archive,
LOCAL_WHOLE_STATIC_LIBRARIES在连接静态连接库的时候不会移除"daed code",
何谓dead code呢,就是调用者模块永远都不会用到的代码段和变量。

14、LOCAL_LDLIBS

链接flags,链接的库不产生依赖关系,一般用于不需要重新编译的库,可以用它来添加系统库。

LOCAL_LDLIBS += -lm –lz –lc -lcutils –lutils –llog …

15、LOCAL_ALLOW_UNDEFINED_SYMBOLS

默认情况下,在试图编译一个共享库时,任何未定义的引用将导致一个“未定义的符号”错误。
然而,如果你因为某些原因,需要不启动这项检查,把这个变量设为'true'。
注意相应的共享库可能在运行时加载失败。(这个一般尽量不要去设为true)

16、LOCAL_ARM_NEON

设置为true时,会将浮点编译成neon指令。
这会极大地加快浮点运算(前提是硬件支持),只有targeting为'armeabi-v7a'时才可以。

17、LOCAL_DISABLE_NO_EXECUTE

Android NDK r4版本开始支持这种"NX bit"的安全功能。
默认是启用的,你也可以设置该变量的值为true来禁用它。
但不推荐这么做。该功能不会修改ABI,只在ARMv6+CPU的设备内核上启用。

18、LOCAL_EXPORT_CFLAGS

定义这个变量用来记录C/C++编译器标志集合,
并且会被添加到其他任何以LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES的模块的LOCAL_CFLAGS定义中。

注意:此处NDK版本为NDK R7C。(不同NDK版本,ndk-build所产生的Makefile并不完全相同)

19、LOCAL_LDFLAGS

# 指定动态库的路径以及ffmpeg库
LOCAL_LDFLAGS := -L$(LOCAL_PATH)/src/main/jniLibs/$(TARGET_ARCH_ABI) -lffmpeg

原文链接:https://www.jianshu.com/p/4dbb47a9e044

一、Eclipse安装

1.官网地址:https://www.eclipse.org/

2.点击【Download】 ,进入eclispe下载页面,点击【Download x86_64】这个按钮直接下载eclipse软件;

3.下载后安装时选择:Eclipse IDE for Java Developers

4.选择安装路径及jre版本号;

5.Eclipse运行后会弹出一个对话框,提示选择工作空间(Workspace);

二、使用Eclipse进行程序开发

1、创建Java项目,点击“File”→“New”→“Project…”。

768c08163e6d408487408dcc43219033.png

2、选择“Java Project”,点击【Next】下一步。

dbd702a76df743d581b2fbb5cfc70dc9.png

3、在Project name(项目名字)框里取个名字叫”Chapter01”(第一章的意思),其余选项保持默认,然后点击【Finsh】完成。

0ce5f6679e034936a557042340fec8a3.png

4、在项目下创建包,找到刚刚创建的Chapter01项目展开找到src文件夹右键打开找到“New”→“Package”。

9f9c16e8f5844bd4969c4f73a71aa467.png

5、打开“Package”后,在“name”框里取名叫“haha”,“Source folder”意思是项目所在的目录,Chapter01/src就是指这个包在Chapter01项目下的src文件里,然后单击【Finish】完成创建。

322c32877ebe4643abf57a3262354e36.png

6、 在包下面创建类,找到刚刚创建的“haha”包,然后右键打开“New”→“Class”。

c84b47a40b0646468dfde8b715e0db3e.png

7、打开Class后,在Name框里取名叫“HelloWorld”,然后点击【Finsh】。

eb3b70bc48fd42419b57219e39c87c72.png

8、在编写区编写如下代码:

1
2
3
public static void main (String[] args) {
System.out.println("这是我写的第一个java代码");
}
fefe11c603654df89b0d9fcb129c16da.png

9、运行程序,右键点击【Run As】→【Java Application】运行;

Windows和Ubuntu双系统安装

1、c盘压缩出300M的未分配空间,用于ubuntu系统的efi引导区

2、其它盘符压缩出足够的空间(500G/2T)未分配空间,用于ubuntu系统和存储区

3、下载rufus,下载ubuntu系统镜像iso,制作ubuntu系统u盘

4、插入U盘,bios设置U盘启动(还有个efi啥选项设置为enable还是ture,忘记了)

5、u盘启动,开始安装ubuntu系统

6、安装

1、语言,默认english

2、网络,选择暂时不链接

3、是否安装第三方软件,不安装,选择了安装速度会慢

4、installation type->选择something else(自定义安装,手动分区)

5、选中300M区域,primary/Beginning of this space/EFI System partition

6、再对(500G/2T)空间进行分区,

​ A:swap交换区,内存大小空间,Logical/Beginning of this space/swap area

​ B:根目录分区,剩下的全部空间 Logical/Beginning of this space/Ext4 journaling file system/加载点选择根目录(/)

​ C:最后选择挂载点(Device for boot loader installation),一定要选择上面分的那个300M的EFI分区

7、设置密码等操作即可安装完成

8、设置Windows和Ubuntu双系统切换,预防开机黑屏或花屏

在ubuntu系统,Ctrl + Alt + T打开终端;
在终端输入sudo gedit /etc/default/grub,回车输入用户密码(输入的密码是看不到)然后回车打开grub引导文件;
将grub文件中的GRUB_DEFAULT=0修改为GRUB_DEFAULT=saved;
在下面添加GRUB_SAVEDEFAULT=true,保存文件并退出一个具有注脚的文本5。
修改下图第10行中的参数为”quiet splash nomodeset”。(预防可能出现的花屏黑屏问题)
终端输入 sudo update-grub,回车更新grub文件。
终端输入sudo reboot,回车重启。在启动界面就可以切换想要打开的系统了。不操作默认打开上次打开的系统。

img

9、同步双系统的时间

双系统的时间不同步是由于,ubuntu会设置主机中的COMS时间为UTC时间,然后加上ip时区计算出本地时间,而windows则是使用CMOS中的时间为本地时间。

终端执行 timedatectl set-local-rtc 1

Logcat 查看日志可选参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
可选的参数主要有:
1、all:查看所有缓冲区日志
2、default:查看 main、system、crash 三个类型日志信息
3、main:查看 main 类型日志
4、radio:查看 radio 类型日志
5、events:查看 events 类型日志
6、system:查看 system 类型日志
7、crash:查看 crash 类型日志

查看系统运行日志:
adb shell
logcat //开始抓取或者-s
logcat -c//清空日志
logcat -s tag//开始抓取日志 并筛选指定的tag日志

使用示例:
logcat -b main #查看 main 类型日志
logcat -b main,system #用来查看 main 和 system 类型日志
logcat -b all #查看所有日志

aosp中查看c++版本

1
2
3
4
5
6
7
8
9
10
//文件路径:build/soong/cc/config/global.go
//文件路径:build/soong/cc/config/clang.go
CStdVersion = "gnu99"
//编译 C 语言代码的标准,c99:表示使用 C99 标准进行编译,gnu99:表示与 GNU 编译器兼容的 C99 标准(GNU C99)
CppStdVersion = "gnu++17"
//编译C++ 标准的版本设置为 GNU C++ 17,C++ 标准版本设置为 C++17,其中的 gnu 表示与 GNU 编译器兼容的意思
ExperimentalCStdVersion = "gnu11"
//表示启用了实验性的 C 语言标准版本,并将其设置为 GNU C11
ExperimentalCppStdVersion = "gnu++2a"
//启用了实验性的 C++ 标准版本,并将其设置为 GNU C++2a

java代码跳转natice查找方式

1
全局查找:_加java类名

搜索/查找文件

1
2
3
4
5
cd到framework/system或其他目录,以getevent文件为例
find -name getevent*
或者
ls -l | grep getevent
grep getevent ./ -rn

查看所有进程的pid

1
2
ps -A (查看指定进程的pid:ps -A | grep " packageName")
//生死指定进程:kill pid

adb执行截屏或其他手势

1
adb shell input keyevent 120 调用截屏命令

打印日志调试(这种方法只能查找java层,无法查找到native调用java层的方法,native调用java使用grep搜索)

1
2
3
4
5
//查找某个地方从底层调用了哪些,加入下面代码,这种方式不会引起程序崩溃,可以获取到堆栈的调用顺序;
// 低版本,具体多少没做验证,如果打印不出来,用下面的方式
Log.i("tyl","oncreate:",new Exception());
// 高版本,通过异常直接打印堆栈
new Exception().printStackTrace();

native端打印堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C++ 中使用android::CallStack将所在线程的调用堆栈打印出来
基本用法:
1. 进入对应的cpp文件中,放开 #define LOG_NDEBUG 0 注释,且变成#define LOG_NDEBUG 1
2. 声明头文件
#include <utils/CallStack.h>
#include <utils/Log.h>

3. 调用方法
android::CallStack stack;
stack.update();
stack.log(debug); // 输出到logcat
4. mk或者bp文件中需要链接以下so库
libutils
libcutils

查看当前view对应的具体的是谁

1
2
3
4
5
//加上 | grep ResumedActivity 是筛选出当前最top的Activity
adb shell dumpsys activity activities | grep ResumedActivity
//结果: 当前包名com.example.test/.MainActivity
mResumedActivity: ActivityRecord{6eb3073 u0 com.example.test/.MainActivity t5}
ResumedActivity: ActivityRecord{6eb3073 u0 com.example.test/.MainActivity t5}

查找系统应用(launcher2)在哪些地方编译的

1
2
3
4
5
6
//cd到~/android-8.1.0_r1/build目录,grep "Launcher2" ./ -rn查找在哪里有用到,注意大小写
tyl@ubuntu:~/android-8.1.0_r1/build$ grep "Launcher2" ./ -rn
./make/target/product/core.mk:45: Launcher2 \ 如果查出有多个文件,则都需要打开对应路径添加应用名
//vim打开显示
vi ./make/target/product/core.mk
//打开后如下图,说明PRODUCT_PACKAGES 在这个里面也需要添加内置应用的文件夹名

input dumpsys,打印触摸事件日志到本地文件

1
2
# 给触摸事件保存到一个txt中
adb shell dumpsys input > input.txt (input.txt本地文件名)

adb shell am stack list查看设备上当前运行的应用程序和任务的信息以及点击坐标,它们之间的关系和顺序等;

AOSP常用编译和查找命令

command describe
m 等同于 make ,进行一次完整编译。
mm 在当前目录下编译所有模块,但不包括依赖项。
mmm [目录] 编译指定目录下的所有模块,但不包括依赖项。只编译指定模块:mmm [dir/:target1,target2]
mma 在当前目录下编译所有模块,包括依赖项。
mmma [目录] 编译指定目录下的所有模块,包括依赖项。
make clean 删除整个 out/ 目录。
make clobber 用于清除编译缓存。
make snod 当重新编译部分模块完成后,快速生成img文件。
cgrep 对C/C++文件执行 grep (只搜寻C/C++文件类型,也包括.h类型)。
jgrep 只对Java文件执行 grep (只搜寻Java文件类型)。
resgrep 只匹配 /res 目录下的 xml 文件。
sepgrep 只查找sepolicy文件。

adb shell dumpsys命令

1
2
3
4
5
6
7
8
9
命令用于获取Android设备上正在运行的系统服务信息。通过该命令,可以获取到各种设备状态、应用程序信息以及系统服务信息。
以下是一些常见的用法:
adb shell dumpsys:列出所有可用的服务名称。
adb shell dumpsys <service>:打印指定服务的信息,例如adb shell dumpsys window打印窗口管理服务的信息。
adb shell dumpsys activity:打印活动管理器服务的信息,包括活动栈、任务栈、任务信息等。
adb shell dumpsys battery:打印电池信息。
adb shell dumpsys wifi:打印Wi-Fi服务的信息。
adb shell dumpsys bluetooth:打印蓝牙服务的信息。
这只是一小部分常见的用法,adb shell dumpsys命令提供了许多其他服务的信息;

vscode搜索跳转行号:搜索栏 :加行数;

vscode搜索查询方法:搜索栏 @ 加方法名

monkey简略:

1
2
monkey是android下自动化测试比较重要的的一个工具,该工具可以运行在host端或者设备(模拟器或真实设备)。
它会向系统发送随机事件流(即随机模拟用户各种操作:点击、滑动、AP切换等),对单个程序或者整个系统进行压力测试。

monkey脚本:

1
2
3
4
5
6
脚本优势:简单、快捷、不需要借助任何工具,可以做简单的性能测试
脚本缺点:只能简单实现坐标、按键等基本操作(根据像素点来进行操作)
#头文件、控制monkey发送消息的参数
type = raw events
count = 10
speed = 1.0
Api 说明
LaunchActivity(pkg_name,activity) 启动应用的Activity
Tap(x,y,tapDuration) 模拟单击事件.tapDuration为点击的持续时间,此参数可冒略
RotateScreen(rotationDegree,persist) 旋转屏幕,rotationDegree为旋转角度,1代表90度:persist表示旋转之后是否固定,0表示旋转后恢复,非0则表示固定不变
DispatchPress(keyName) 按键
DispatchFlip(true/false) 打开或者关闭软键盘
RunCmd(cmd) 在设备上运行 shell 命令
PressAndHold(x,y,pressDuration) 模拟长按事件
DispatchString(input) 输入字符串
DeviceWakeUp() 唤醒屏幕
UserWait(sleepTime) 让脚本中断一段时间

adb获取系统已安装的apk文件:

1
2
1.第三方app:adb shell /data/app/目录下apk pull出来
2.系统app:adb shell /system/app/目录下apk pull出来

monkey脚本编写步骤:

1
2
3
4
5
6
7
8
9
10
1:确定步骤,如下:
获取包名和activityname
通过SDK自带的aapt工具
一步一步的在手机上操作,并获取像素点
利用工具(SDK自带uiautomatorviewbat或者开发者选项中的坐标位置
要仔细的检查下,是否脚本上的每一步都跟你的手动操作都对应
检查脚本,跑一下脚本。
2:运行脚本
将文件放到手机中:adb push 本地monkey脚本文件 /data/local/tmp
运行文件:adb shell monkey -f 手机文件地址/文件名称 数量

脚本示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//myMonkey.txt文件内容:
#头文件、控制monkey发送消息的参数
type = raw events
count = 10
speed=1.0
#以下monkey命令
start data >>
#打开测试应用程序
LaunchActivity(com.android.browser,com,android.browser.BrowserActivity)
#让浏览器将页面加载出来,所以我们的脚本需要等待一段时间
UserWait(3000)
#点击浏览器网址
Tap(367,109)
#输入www.ningmengban.com
DispatchString(www.ningmengban.com)
#输入enter键
DispatchPress(KEYCODE ENTER)
#输入Home键返回到桌面
DispatchPress(KEYCODE HOME)

获取手机当前显示的界面的包名:adb shell dumpsys activity | find “mFocusedActivity”

命令帮助:adb shell monkey –help

monke常见参数:

-p:

1
2
3
4
5
用于约束限制,用此参数指定一个或多个包( Package,即App名字)。指定指定包之后Monkey将只允许系统启动指定的APP。如果不指定包,Monkey将允许系统启动设备中的所有APP
命令 :
adb shell monkey -p com.lemon.lemonban 1000
说明 :
com.emon.lemonban 为包名,1000是事件计数(即让Monkey程序模拟1000次随机用户事件)

-s:

1
2
3
4
5
6
用于指定伪随机数生反工seed值,如果seed相同,则两次Monkey测试所产生的事件序列也相同
命令 :
Monkey测试1 : adb shell monkey -p com.lemon.lemonban -s 101 100 提前指定
Monkey测试2 : adb shell monkey -p com.lemon.lemonban -s 101 100
说明 :两次测试的效果是相同的,因为模拟的用户操作序列( 每次操作按照一定的先后顺序所组成的一系列操作,即一个序列)是一样的。
操作序列虽然是随机生成的,但是只要我们指定了相同的Seed值就可以保证两次测试产生的随机操作序列是完全相同的,所以这个操作序列伪随机的,

-v

1
2
3
4
5
6
7
8
总共分3个级别日志级别(正常使用3个-v)
-v 用于指定反馈信息级别( 信息级别就是日志的详细程度) Level0
说明:缺省值,仅提供启动提示、测试完成和最终结果等少量信息
-v -v 日志级别 Level 1
说明 :提供较为详细的日志,包括每个发送到Activity的事件信息
-v -v -v日志级别 Level 2说明:最详细的日志,包括了测试中选中/未选中的Activity信息
命令 : adb shell monkey -p com.lemon.lemonban -v 100
说明: com.emon.lemonban 为包名,100是事件计数(即让Monkey程序模拟100次随机用户事件)

–throttle

1
2
用于指定用户操作( 即事件)间的时延,单位是毫秒
命令 : adb shell monkey -p com.lemon.lemonban --throttle 3000 100

–randomize-throttle

1
2
3
在事件之间插入随机延迟,随机延迟范围为0到throttle设置的时间,单位为毫秒
命令 : adb shell monkey -p com.lemon.lemonban --throttle 5000 --randomize-throttle
说明: 执行一百次monkey随机事件,每次事件的间隔在0到5000毫秒之间不固定

–pkg-whitelist-file

1
2
3
4
5
白名单,后面接txt文件(要执行测试的多个包名,多个包名换行保存即可)
1)第一步把你设置的白名单的包名写到一个txt文档里面,记得路径和文档名字都是英文!D:whitelist.txt
2)把这个文件拉到我们的安卓机中去,放到/data/local/tmp下面
adb push D:\whitelist.txt /data/local/tmp 注意这两个反斜杠
命令 : adb shell monkey --pkg-whitelist-file /data/local/tmp/whitelist.txt 100

–pkg-blacklist-file

1
2
黑名单,后面接txt文件
命令 : adb shell monkey --pkg-blacklist-file /data/local/tmp/blacklist.txt 100

日志输出到指定路径

1
2
> 后面加输出的路径即可;
命令:adb shell monkey -p com.lemon.lemonban 1000 > D:/monkey_log.txt

monkey其他常见参数

名称 说明
–pct-touch 调整点击事件的百分比,down-up,编号为0(100为百分之百)
-pct-motion 调整移动事件的百分比,直线滑动,编号为1
-pct-pinchzoom 调整两指缩放压缩手势的百分比,比如放大缩小手势操作,编号为2
-pct-trackball 调整轨迹球时间百分比,不常用,编号为3 现在手机几乎没有滚动球,但滚动球事件中包含曲线滑动事件,在被测程序需要曲线滑动时可以选用此参数
-pct-rotation 调整旋转事件的百分比,横屏竖屏,编号为4
-pct-nav 调整导航事件的百分比,编号为5,包括上下左右,如方向输入设备的输入 )老手机的上下左右键,智能机上没有
-pct-majornav 调整主要导航事件的百分比,编号为6,例如中间键、回退键、菜单键
-pct-syskeys 调整”系统”按键事件的百分比,编号为7,这些按钮一般专供系统使用,如Home, Back,Start Call, End Call,音量控制
-pct-appswitch 调整app切换事件的百分比,编号为8
-pct-flip 调整键盘唤出隐藏的百分比,编号为9
-pct-anyevent 跳转除上面事件外,其它事件的百分比,编号为10,如按键、其他在设备上不常用的按钮等

Linux中分析相关代码比较多的话就还是需要找一个比较好的工具进行,其实普遍比较主流的有2种:
1、sourceinsight –这个基本做linux kernel相关使用比较多,基本分析跳转和写代码都还是比较方便,缺点就是只要windows版本,还需要付费
2、VSCode这个是微软的轻量级IDE工具,这个开源,而且可以跨平台windows,mac,linux都可以,对c、c++等语言支持非常好,所以是个比较好的选择,缺点就是对java等支持相比没有as好,导致我们可能主要把它用于native代码阅读编写

因为我们是ubuntu开发,所以这里当然就选vscode进行native源码环境搭建,并且是Ubuntu上面:
步骤
1、下载vscode,这个其实官网有,但是因为实在是网络太卡,建议可以
https://code.visualstudio.com/下载

2、安装下载deb文件,命令如下:

1
sudo dpkg -i xxxx.deb

3、启动vscode,直接取Ubuntu应用列表中打开VSCode,刚开始进入建议先
安装vscode的插件,c/c++,c++ intelligent

1
2
3
4
5
6
7
8
9
10
Android System Tools
beko
Better C++ Syntax
C/C++
C/C++ Extension Pack
Chinese Languate
Cmake
Cmake Tools
Git History
RC Script Language
在这里插入图片描述

4、然后在file –>open folder打开我们的代码文件夹,我这里是仅仅打开的aosp/frameworks文件夹,没有打开整个aosp源码目录,因为整个aosp代码量太大,运行太卡,基本做完这几个就可以愉快的看到漂亮的native代码了

在这里插入图片描述

习惯了eclipse的快捷命令,肯定会不知道怎么操作VSCode类似的场景,这里总结一些快捷键:

1
2
3
4
5
6
7
快速文件导航:Ctrl + P 
文件和文件夹管理:Ctrl + Shift + E
切换侧边栏:Ctrl + B (Cmd + B on macOS)
集成终端:Ctrl + '
注释/取消注释代码:Ctrl + /
多光标魔法:Ctrl + Alt + Down
选择所有出现:Ctrl + Shift + L

1、查找当前工程中某个文件:ctl+p
2、查找当前cpp文件中某个方法展示出方法列表 :ctl+shift+o
3、返回上一个操作:在Linux中可以使用快捷键Ctrl+Alt + -

vscode搜索跳转行号:搜索栏 :加行数;

vscode搜索查询方法:搜索栏 @ 加方法名

ps:存在问题写代码时候一些对象等无法有提示,debug进程时候发现无法打断点;