0%

1
2
3
4
5
6
./gradlew :MyNetworkSDK:assembleRelease   会把library下打包成aar

./gradlew assembleDebug 打 Debug 版本 APK/AAR

./gradlew assembleRelease 打 Release 版本 APK/AAR

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
| 十进制 (Dec) | 二进制 (Bin, 8位) | 十六进制 (Hex) |
| --------- | ------------- | ---------- |
| 0 | 00000000 | 0x00 |
| 1 | 00000001 | 0x01 |
| 2 | 00000010 | 0x02 |
| 3 | 00000011 | 0x03 |
| 4 | 00000100 | 0x04 |
| 5 | 00000101 | 0x05 |
| 6 | 00000110 | 0x06 |
| 7 | 00000111 | 0x07 |
| 8 | 00001000 | 0x08 |
| 9 | 00001001 | 0x09 |
| 10 | 00001010 | 0x0A |
| 11 | 00001011 | 0x0B |
| 12 | 00001100 | 0x0C |
| 13 | 00001101 | 0x0D |
| 14 | 00001110 | 0x0E |
| 15 | 00001111 | 0x0F |
| 16 | 00010000 | 0x10 |
| 17 | 00010001 | 0x11 |
| 18 | 00010010 | 0x12 |
| 19 | 00010011 | 0x13 |
| 20 | 00010100 | 0x14 |
| 32 | 00100000 | 0x20 |
| 64 | 01000000 | 0x40 |
| 128 | 10000000 | 0x80 |
| 255 | 11111111 | 0xFF |

👉 规律记忆:

1 个十六进制位 (0~F) = 4 个二进制位

2 个十六进制位 (00~FF) = 1 个字节 (8 bit)

常用的 0x0A=10, 0x10=16, 0x40=64, 0x80=128, 0xFF=255

十六转十:高位乘以16+低位


1️⃣ 有符号 Byte 的表示

总共有 8 位二进制:

b7 b6 b5 b4 b3 b2 b1 b0


b7 是 符号位(最高位)

0 → 正数

1 → 负数

b6~b0 是数值位(7 位)

范围:-128 ~ 127

最小值:1000 0000 → -128

最大值:0111 1111 → 127

2️⃣ 二进制例子
十进制 二进制(8位) 说明
0 0000 0000 中性值
1 0000 0001 正数
127 0111 1111 最大正数
-1 1111 1111 负数(补码表示)
-128 1000 0000 最小负数

“无符号”是二进制和计算机数据里一个非常重要的概念
1️⃣ 有符号数 vs 无符号数
✅ 有符号整数(Signed)

可以表示 正数、负数和零

例如 8 位(1 个字节):

范围:-128 ~ 127


第一位是 符号位:

0 = 正数

1 = 负数

✅ 无符号整数(Unsigned)

只能表示 非负数(0 或正数)

例如 8 位:

范围:0 ~ 255


所有 8 位都用来表示数值,没有符号位

因此最大值比有符号数大了一倍

2️⃣ 为什么要用无符号?

有些硬件或协议数据永远不会为负,比如:

电压值、温度传感器、PWM占空比

设备计数器、PID 输出值

使用无符号数可以 表示更大的范围,同样字节数存更多信息

3️⃣ 举例
类型 二进制 十进制
有符号 Int8 11111111 -1
无符号 UInt8 11111111 255

同样一个字节,解释方式不同,数值完全不同。

4️⃣ 在 Kotlin 里

Kotlin 没有原生 UInt16/UInt32(早期版本),所以我们用 Int 或 Long + 位运算 来表示无符号数:

val value = ((this[offset].toInt() and 0xFF) or ((this[offset+1].toInt() and 0xFF) shl 8))


and 0xFF 就是把 字节转成无符号

避免 Kotlin Byte 被当作 -128~127 的有符号数

5️⃣ 一句话总结

无符号整数 = 永远 ≥0,没有负数,最大值比同样字节数的有符号数大一倍。


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
在 Kotlin 里,object 定义的类就是 单例。

1️⃣ 基本概念
object MySingleton {
val name = "单例"
fun sayHello() = println("Hello from $name")
}


特点:

全局唯一:整个程序中只有一个实例。

懒加载:第一次访问时才初始化。

线程安全:初始化本身是线程安全的,不需要额外加锁。

调用:

fun main() {
MySingleton.sayHello()
println(MySingleton.name)
}


不能使用 MySingleton() 创建新实例

所有属性和方法都属于同一个实例

2️⃣ 使用场景

工具类(类似 Java 的静态方法集合)

全局管理类(配置、状态管理)

单例模式的对象(服务、控制器等)

3️⃣ 对比 Java 单例

Java 单例常见写法:

public class MySingleton {
private static final MySingleton instance = new MySingleton();
private MySingleton() {}
public static MySingleton getInstance() { return instance; }
}


Kotlin 直接写:

object MySingleton { ... }


✅ 语法简单,线程安全,省掉了手动写 static 和锁。

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
Kotlin 顶层函数(Top-level Function)总结
1️⃣ 定义

顶层函数就是直接写在 .kt 文件里的函数,不属于任何类或对象。

可以看作是 Java 工具类的静态方法,自动被编译成 JVM 静态方法。

// 文件名: Utils.kt
fun sayHello(name: String) {
println("Hello, $name")
}

fun add(a: Int, b: Int): Int = a + b


调用方式(Kotlin):sayHello("Alice")

调用方式(Java):UtilsKt.sayHello("Alice")

2️⃣ 顶层属性(静态字段)

顶层 val / var 也是静态的,JVM 编译后是静态字段。

val PI = 3.1415 // 常量
var counter = 0 // 可变静态变量


Kotlin 调用:println(PI) 或 counter += 1

Java 调用:UtilsKt.PI / UtilsKt.getCounter() / UtilsKt.setCounter()

3️⃣ JVM 编译规则
Kotlin 元素 JVM 编译结果
顶层函数 静态方法
顶层属性 静态字段 + getter/setter
文件名 JVM 类名 = 文件名 + Kt

例:Utils.kt → JVM 类 UtilsKt

4️⃣ 与 Java 工具类对比
Java Kotlin
static 方法 顶层函数
static 字段 顶层属性 (val/var)
工具类类名 Kotlin 文件名

Kotlin 不需要 class + static,更简洁。

5️⃣ 使用建议

顶层函数和顶层属性可以放在同一个文件里,集中管理相关工具方法。

按功能划分文件,不要把所有工具方法放在一个文件中(可维护性差)。

顶层函数也可以在 Java 中调用,非常方便。

6️⃣ 对比 companion object

如果必须在类内部写静态成员,用 companion object:

class MyClass {
companion object {
const val VERSION = "1.0"
fun greet() = println("Hello")
}
}


优点:类内部有组织

缺点:比顶层函数多了一层类包裹,调用稍麻烦

7️⃣ 总结一句话

Kotlin 顶层函数 = 静态工具方法 + 静态属性,可以直接写在文件里,不需要 class 或 static。

1
2
3
4
5
6
7
8
9
10
11
12
13
git reset --soft HEAD~   撤销当前本地提交的
git status 查看当前修改的状态

git config --global http.proxy http://127.0.0.1:7890 让git使用代理
git config --global https.proxy http://127.0.0.1:7890
git config --get https.proxy 测试是否走代理

git config --global --unset http.proxy 关掉代理
git config --global --unset https.proxy

git config --global --get http.proxy 确认,没有输出就说明清掉了
git config --global --get https.proxy

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下状态 -->
<item android:state_pressed="true"
android:drawable="@color/color_FF5800" />

<!-- 选中状态(可选) -->
<item android:state_selected="true"
android:drawable="@color/color_FF5800" />

<!-- 默认状态(抬起) -->
<item android:drawable="@color/gray_999999" />
</selector>

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
1. 添加依赖
在你的 App 模块的 build.gradle 文件中添加 AutoSize 依赖:

gradle
dependencies {
implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
}
如果无法从 Jcenter 获取(注意 Jcenter 已于 2022 年 2 月后停止维护),可以尝试使用 JitPack 仓库:

gradle
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
dependencies {
implementation 'com.github.JessYanCoding:AndroidAutoSize:v1.2.1'
}
2. 配置全局设计图尺寸
在 AndroidManifest.xml 的 <application> 标签内添加 meta-data 配置你的设计图尺寸(单位通常为 dp):

xml
<manifest>
<application>
<meta-data
android:name="design_width_in_dp"
android:value="360"/> <!-- 根据你的设计图宽度填写,例如 360 dp -->
<meta-data
android:name="design_height_in_dp"
android:value="640"/> <!-- 根据你的设计图高度填写,例如 640 dp -->
</application>
</manifest>
这里的 value 值需要你根据设计师提供的设计图来定。常见做法是:

如果设计图是 1920x1080 px,通常除以 3(因为 1920/3=640, 1080/3=360),可配置为 360 dp (宽) x 640 dp (高)。

如果设计图是 1280x720 px,通常除以 2(1280/2=640, 720/2=360),同样可配置为 360 dp (宽) x 640 dp (高)。

关键在于:design_width_in_dp 和 design_height_in_dp 的单位必须是 dp。如果设计师只给了 px 尺寸,你需要换算:dp = px / (dpi / 160)。如果不知道设备 DPI,粗略地将 px 尺寸除以 2 或 3 也是一种办法。

3. 初始化 SDK
在你的自定义 Application 类的 onCreate() 方法中进行初始化:

kotlin
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// 初始化 AutoSize
AutoSize.initCompatMultiProcess(this) // 处理多进程:cite[1]

// 可选:进行高级配置
AutoSizeConfig.getInstance()
.setLog(false) // 设置是否打印日志,默认 true
.setBaseOnWidth(true) // 默认 true,表示以宽度为基准适配。false 则以高度为基准:cite[2]
.setUseDeviceSize(false) // 是否使用设备的实际尺寸做适配,默认为 false:cite[1]
.setExcludeFontScale(false) // 是否屏蔽系统字体大小对 AndroidAutoSize 的影响, 默认为 false:cite[1]
}
}
别忘了在 AndroidManifest.xml 中通过 android:name 属性指定你的 Application 类。

🛠 高级用法与自定义配置
选择布局单位
AutoSize 支持两种类型的布局单位:

单位类型 单位 特点 适用场景
主单位 dp, sp 侵入性低,推荐在新项目中使用。会影响三方库页面、控件及系统控件的布局。
副单位 pt, in, mm 侵入性高,不会影响其他使用 dp 布局的三方库或系统控件。


⚠️ 注意事项
初始化时机:AutoSize 的初始化应在 Application 的 onCreate() 中尽早完成。

设计图尺寸:务必正确配置 design_width_in_dp 和 design_height_in_dp,这是准确适配的基础。

页面自定义:对于与全局设计图尺寸不一致或不需要适配的页面,记得使用 CustomAdapt 或 CancelAdapt 进行个性化配置。

布局预览:在 Android Studio 中布局时,你可能需要创建对应的模拟设备以获得更准确的预览效果。具体创建方法可参考官方文档或社区分享。

ProGuard 混淆:如果启用了代码混淆,请在混淆规则文件(proguard-rules.pro)中添加以下规则:

bash
-keep class me.jessyan.autosize.** { *; }
-keep interface me.jessyan.autosize.** { *; }




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 public interface WarningListener {
void onWarning(String message);
}


public static void inputData(WarningListener listener) throws IOException {

if(listener != null){
listener.onWarning(secondOutput+"");
}
}



activity或fragment中调用
class.inputData(new class.WarningListener() {
@Override
public void onWarning(@NonNull String message) {
runOnUiThread(() -> txt_msg.append(message + "\n"));
}
});

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
在安卓中,安全使用线程池是保证应用性能、避免 ANR 和内存泄漏的重要手段。

1️⃣ 为什么要用线程池

避免频繁创建和销毁线程,线程创建成本高。

限制线程数量,避免系统过载。

复用线程,提高性能。

安卓开发中常用线程池有两类:

Executors 提供的便捷方法:如 newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor。

自定义 ThreadPoolExecutor:灵活配置核心线程数、最大线程数、队列大小、拒绝策略。

2️⃣ 常用线程池写法
① 固定线程池(FixedThreadPool)
ExecutorService fixedPool = Executors.newFixedThreadPool(4); // 固定4个线程

fixedPool.execute(() -> {
// 耗时任务
doHeavyTask();
});


适合:任务数量大但线程数可控的情况。

② 缓存线程池(CachedThreadPool)
ExecutorService cachedPool = Executors.newCachedThreadPool();

cachedPool.execute(() -> {
doHeavyTask();
});


特点:有新任务就创建线程,空闲线程 60s 后销毁。

适合:执行短时异步任务,数量不确定。

风险:任务过多会无限开线程 → OOM。

③ 单线程池(SingleThreadExecutor)
ExecutorService singlePool = Executors.newSingleThreadExecutor();
singlePool.execute(() -> {
// 顺序执行任务
});


特点:任务按顺序执行,保证顺序性。

适合:任务必须按顺序执行的场景,比如写文件、写数据库。

④ 自定义线程池(ThreadPoolExecutor)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // 核心线程数
8, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100), // 队列大小
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);


优点:灵活可控。

拒绝策略:

AbortPolicy → 抛异常

CallerRunsPolicy → 在调用者线程执行

DiscardPolicy → 丢弃任务

DiscardOldestPolicy → 丢弃最老任务

3️⃣ 安全使用线程池的注意点

避免 UI 更新在子线程

executor.execute(() -> {
// 耗时任务
String result = doHeavyTask();

// UI更新
runOnUiThread(() -> textView.setText(result));
});


避免线程泄漏

Activity/Fragment 销毁时要关闭线程池:

@Override
protected void onDestroy() {
super.onDestroy();
executor.shutdownNow(); // 停止所有任务
}


使用有限队列和核心线程数

防止任务无限堆积,导致 OOM。

结合生命周期感知组件

可以用 ViewModel + LiveData / RxJava / Coroutine 来管理线程和 UI 更新。

4️⃣ 推荐组合方式

IO密集型任务(网络请求、文件读写):

ExecutorService ioPool = Executors.newCachedThreadPool();


CPU密集型任务(计算):

int cpuCount = Runtime.getRuntime().availableProcessors();
ExecutorService cpuPool = Executors.newFixedThreadPool(cpuCount);