-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path面试准备
679 lines (471 loc) · 24.6 KB
/
面试准备
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
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
Handler机制
通过不断的在与之对应的MessageQueue中取Message对象来处理
主要应用于线程间通信
在子线程中使用 应该手动调用 Looper.prepare() Looper.loop()
及在执行完成后调用Looper.quit()否则会造成内存泄漏
主线程中的looper对象是在ActivityThread中的main()函数中创建的
所以无需手动创建 可直接创建Handler对象进行使用
runOnUiThread() view.post() 等方法都是通过Handler进行的线程切换
为什么不会阻塞主线程?
一个线程执行完代码就会销毁,正是因为死循环才确保主线程不会退出 一直存活
真正会卡死主线程的的操作是在回调方法中操作时间过长,会导致掉帧,甚至ANR Looper.loop()方法本身不会导致应用卡死。
主线程的消息循环机制是什么?
在进入死循环之前便创建了新的线程binder线程(具体指ApplicationThread)用来接收 系统服务AMS发来的消息 然后通过Handler
发给主线程
Activity的生命周期都是依靠Looper.loop()方法
ActivityThread通过ApplicationThread和AMS进行进程间通讯,
AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,
然后ApplicationThread会向H发送消息,H
收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,
即切换到主线程中去执行,
这个过程就是,主线程的消息循环模型。
Android性能优化 UI优化 内存泄漏 OOM 处理方案?
Retrofit RxJAVA Glide Dagger2?
Kotlin哪些需要注意的?关键字?
设计模式?
事件分发机制?
MVP MvvM?
Android UI 优化
vsync
1.卡顿的主要原因是UI页面的过度绘制 overdraw
解决方法
1.可以打开开发者选项中的gpu过度绘制选项 然后减少不必要的background
2.使用<include>标签 <viewstub>标签默认不会显示 也不会占用位置(viewstub所加载的布局是不可以使用merge标签的)
<merge>标签布局顶节点是Framelayout并且不需要设置background等。或者被include的时候,外层布局与父容器相同。
3.自定义view减少布局嵌套 优化onDraw()方法
4.减少布局中的view
a.使用SpannableStringBuilder替换多个Textview
b.使用LinearLayout自带的divider实现分割线
c.使用Space控件 onDraw是空实现因此只占位置不去渲染。
掉帧检测方案
Looper android 使用消息机制进行UI更新,UI线程有个Looper,在其loop方法中会不断取出message,
调用其绑定的Handler在UI线程执行,如果在handler的dispatchMessage方法里有耗时操作,就会发生卡顿。
可以使用Choreographer(编舞者)协调动画、输入和绘图的时间(api>16)
Choreographer从显示子系统接收定时脉冲如垂直同步,然后安排下一帧的渲染工作。
开发过程中并不直接使用Choreograhper,但是我们可以使用Choreograhper.Framecallback回调的方式,获取每一帧的
时间,通过计算两帧的时间差,如果大于16ms,说明发生了丢帧。
//为Choreographer设置一个回调,当一帧开始渲染时触发。
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
long lastFrameTimeNanos = 0;
long currentFrameTimeNanos = 0;
@Override
public void doFrame(long frameTimeNanos) {
if (lastFrameTimeNanos == 0) {
lastFrameTimeNanos = frameTimeNanos;
}
currentFrameTimeNanos = frameTimeNanos;
long diffMs = TimeUnit.MILLISECONDS.convert(currentFrameTimeNanos - lastFrameTimeNanos, TimeUnit.NANOSECONDS);
lastFrameTimeNanos = currentFrameTimeNanos;
if (diffMs == 0) {
diffMs = (long) 16.7;
}
if (isShowFPS) {
long current = System.currentTimeMillis();
if (current - mLastFPSRefreshTs > refreshInterval) {
int fps = (int) (1000 / diffMs);
refreshFPS(fps);
mLastFPSRefreshTs = current;
}
}
if (diffMs > 16.7f) {
long droppedCount = (long) (diffMs / 16.7f);
if (droppedCount > 1) {
System.out.println("掉帧数 : " + droppedCount);
}
}
if (UiBlockLogMonitor.getInstance().isMonitor()) {
UiBlockLogMonitor.getInstance().stopMonitor();
}
if (isDetectContinue) {
UiBlockLogMonitor.getInstance().startMonitor();
Choreographer.getInstance().postFrameCallback(this);
}
}
});
问题检测工具 systrace
systrace.py是一个命令行工具,位于.../sdk/platform-tools/systrace目录下。
在应用运行时,它可以帮我们收集和分析所有进程的计时信息,包含CPU调度、应用线程、磁盘活动等
Android内核信息,然后生成一份html报告。
systrace对检测应用UI表现非常有效,因为它可以帮你分析你的代码和帧率来试别出问题区域,然后提供可能的解决方案。
ok That need install PY (you know what I'm saying right?)
Traceview
Android sdk内置工具 以图形的形式展示代码的执行时间、次数及调用栈。
GPU过度绘制调试模式
主线程耗时操作
1json数据解析耗时
2文件操作(获取所属渠道名称)
3Binder通信
4正则匹配
5相机操作:初始化、预览、停止预览、释放
6组件初始化(推送)
7循环删除、创建View
8Webview首次初始化
处理方案 异步-》缓存-》替代方案-》保持原状
主线程挂起
1异步线程与主线程竞争cpu资源
Thread.currentThread().setPriority() Process.setThreadPriority()
Process.setThreadPriority(Process.myTid(), Process.THREAD_PRIORITY_BACKGROUND);
同时提高主线程优先级 Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
2频繁GC使主线程挂起 -》后续内存优化
冷白屏启动
设置欢迎页logo
Android 内存优化
避免出现内存泄漏、内存溢出(OOM)、内存空间占用过大等问题最终导致crash
Android内存管理机制分为三层
其中负责进程内存的有
Application Framework
Linux内核
负责对象变量内存的是 Dalvik虚拟机
Android的内存管理=对进程、对象、变量进行内存分配与回收
针对进程的内存策略
a.内存分配策略
由ActivityManagerService集中管理所有进程的内存分配
b.回收策略
步骤一 Application Framework 决定回收的进程类型
Android中的进程是托管的;当进程空间紧张时,会按进程优先级从低到高的顺序进行自动回收。
空->后台->服务->可见->前台
步骤二 Linux内核真正回收具体进程
1.ActivityManagerService对所有进程进行评分(评分放到变量adj中)
2.更新评分到Linux内核
3.由Linux内核完成真正的回收
内存回收策略
由Java垃圾回收器GC的内存释放=垃圾回收算法
1主要有标记清除法
2复制算法
3标记-整理算法
4分代收集算法
常见的内存问题&解决方案
1内存泄漏
2内存抖动
3图片Bitmap相关
4代码质量&数量
5日常不正确使用
内存泄漏原因->本该回收的内存没有回收->持有引用者的生命周期>被引用者的生命周期
常见情况
1集合类
2Static关键字修饰的成员变量
3非静态内部类/匿名类
4资源对象使用后未关闭
内存抖动原因
频繁创建大量临时的小对象
导致频繁GC造成卡顿 导致内存碎片 导致无法分配内存 导致OOM
应避免创建大量临时的小对象
图片Bitmap优化
1使用完毕后释放图片资源 Bitmap.recycle() 采用软引用
2根据分辨率适配&缩放图片 设置多套图片资源 BitmapFactory.decodeResource()
3按需选择合适的编码方式
4设置图片缓存 三级缓存
代码质量&数量
a1减少不必要的类&对象2减少引用不必要的库3使用代码混淆4慎用sp
b1数据结构 使用性能高的数据结构
c1使用占用内存小的数据类型 避免使用枚举
d数据对象引用 根据场景选择不同的引用类型
1强引用:不希望被回收
2软引用:缓存机制(即实现内存敏感的数据缓存,如图片,网页缓存等)
3弱引用:防止内存泄漏保护对象引用
4虚引用:跟踪对象被垃圾回收器回收的活动
常见使用
优化lisview
少用service
少用依赖注入框架
少用多进程
largeHeap = 'true' 通知虚拟机获取更大的内存
额外小技巧
1获取当前可使用的内存大小ActivityManager.getMemoryClass()方法可获取当前应用可用的内存大小
2获取当前的内存使用情况 onTrimMemory()获取应用程序当前内存使用情况 以内存级别进行试别(android4.0后)
3当应用程序变为隐藏状态时,释放内存。 当应用程序跳转到其他app&试图不再显示时
具体操作实现当前activity的onTrimMemory()方法 当用户离开视图时会得到通知
若返回的参数=TRIM_MEMORY_UI_HIDDEN即代表视图变为隐藏状态,则可释放视图所占资源。
辅助内存优化分析工具
MAT(Memory Analysis Tools)
HeapViewer
Allocation Tracker
Memory Monitor
LeakCanary
自定义view过程
重写构造方法
onMeasure()测量-> onLayout()布局-> onDraw()绘制
事件分发机制
activity(phoneWindow)
dispatchTouchEvent -> onTouchEvent
-> viewgroup ->dispatchTouchEvent -> onIntercept -> onTouchEvent
-> view ->dispatchTouchEvent -> onTouchEvent
Retrofit+Okhttp3+RxJava
Retrofit通过动态代理的方式 拿到ApiService对象 然后通过注解的方式拿到request所需要的参数 并整合成
完整的request 然后发给ok
ok使用责任链模式 可以添加自定义的网络拦截器 实现全局添加请求头等参数 最后加入请求队列 发起请求。
RxJava主要用来对返回的数据进行处理变换 采用观察者模式 以流的形式就行数据的处理 包括线程切换等。
RxJava使用
Glide使用
Dagger2使用及原理
设计模式(单例、建造者、工厂、享元)
Kotlin语言
object 指 单例对象 companion object 伴生对象 一个类中只能有一个
data 指纯数据类 构造函数最少有一个参数
高阶函数 (type alias 别名 声明函数类型)
1.T.run 执行block()并 返回block() 不会返回调用对象
2.T.apply 执行完block() 返回T对象本身
3.T.let 会执行block(T) 返回block(T)
4.T.also 会执行block(T) 并返回T对象本身
5.with(T){T.block()} 会执行T.block() 并返回最后一次 T.block()的返回值
扩展函数 扩展属性
Java泛型 不支持 协变 逆变
Kotlin DSL特性支持...
扩展函数 扩展属性
带接收者的lambda表达式(高阶函数)
invoke函数调用约定
Recyclerview缓存机制
MVC MVP MVVM
Model代表数据层
View代表UI层
ViewModel代表中间的两层之间的桥梁
Jetpack???
AAC Android Architecture Components
DataBinding Navigation LiveData Room Paging ViewModel WorkManager LifeCycles
HashMap底层实现
红黑树
内存模型 GC原理
组件化
其实就是分层
插件化
Hook技术什么的...
屏幕适配
最小宽度限定符 sw-375dp
头条适配方案
动态更改density 达到适配的效果
优点 接入成本低 效果最好
缺点 对老项目不太友好
头条适配方案 动态修改density的值 确保
DataBinding
注解 BindingAdapter BindingConversion
Webview与X5Webview区别
当然是内核不同了,X5是腾讯自己写的内核,所以每次启动时需要进行初始化,需要下载。如果不成功就是用的系统内核
js交互方式
内存泄漏有哪些
JVM内存模式
栈 堆 常量池 方法区
Kotlin协程 好处 底层原理?
对线城的又一次封装
静态内部类会不会内存泄漏?
不会 因为内存不在一块
Android 10新特性 及适配方案
1 全新的兼容库 androidX
2 存储方式发生改变 分区存储 应用只能访问应用私有目录及共享目录 且不能访问由其他应用创建的非媒体文件
2.1 受影响的变更
2.1.1图片位置信息获取不到 因为位置对于用户来说属于敏感信息
在manifest中申请ACCESS_MEDIA_LOCATION
调用MediaStore setRequireOriginal(Uri uri)接口更新图片Uri
2.1.2访问数据
MediaStore.Files应用分区存储模式下,MediaStore.Files 集合只能够获取媒体文件信息(图片、音频、视频),
获取不到非media(pdf、office、doc、txt等)文件。
2.1.3File Path路径访问受影响接口
开启分区存储新特性, Andrioid 10不能够通过File Path路径直接访问共享目录下资源,
以下接口通过File 路径操作文件资源,功能会受到影响,应用需要使用MediaStore或者SAF方式访问。
2.2兼容模式
应用未完成外部存储适配工作,可以临时以兼容模式运行, 兼容模式下应用申请存储权限,即可拥有外部存储完整目录访问权限,
通过Android10之前文件访问方式运行,以下两种方法设置应用以兼容模式运行。
2.2.1 AndroidManifest中申明
targetSDK r大于等于Android 10(API level 29), 在manifest中设置requestLegacyExternalStorage属性为true。
2.2.2、判断兼容模式接口
//返回值
//true : 应用以兼容模式运行
//false:应用以分区存储特性运行
Environment.isExternalStorageLegacy();
2.3 适配方案
分区存储适配包含文件迁移以及文件访问兼容性适配两个部分:
1)文件迁移
文件迁移是将应用共享目录文件迁移到应用私有目录或者Android10要求的media集合目录。
针对只有应用自己访问并且应用卸载后允许删除的文件,需要迁移文件到应用私有目录文件,可以通过File path方式访问文件资源,降低适配成本。
允许其他应用访问,并且应用卸载后不允许删除的文件,文件需要存储在共享目录,应用可以选择是否进行目录整改,将文件迁移到Android10要求的media集合目录。
2)文件访问兼容性
共享目录文件不能够通过File path方式读取,需要使用MediaStore API或者Storage Access Framework框架进行访问。
2.4 适配指导
AndroidQ中使用ContentResolver进行文件的增删改查。
1)获取(创建)私有目录下的文件夹
//在自身目录下创建apk文件夹
File apkFile = context.getExternalFilesDir("apk");
2)创建私有目录文件
生成需要下载的路径,通过输入输出流读取写入
String apkFilePath = context.getExternalFilesDir("apk").getAbsolutePath();
File newFile = new File(apkFilePath + File.separator + "demo.apk");
OutputStream os = null;
try {
os = new FileOutputStream(newFile);
if (os != null) {
os.write("file is created".getBytes(StandardCharsets.UTF_8));
os.flush();
}
} catch (IOException e) {
} finally {
try {
if (os != null) {
os.close();
}catch (IOException e1) {
}
}
3)创建共享目录文件夹
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = context.getContentResolver();
ContentValues values = new ContentValues();
values.put(MediaStore.Downloads.DISPLAY_NAME, fileName);
values.put(MediaStore.Downloads.DESCRIPTION, fileName);
//设置文件类型
values.put(MediaStore.Downloads.MIME_TYPE, "application/vnd.android.package-archive");
//注意MediaStore.Downloads.RELATIVE_PATH需要targetVersion=29,
//故该方法只可在Android10的手机上执行
values.put(MediaStore.Downloads.RELATIVE_PATH, "Download" + File.separator + "apk");
Uri external = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
Uri insertUri = resolver.insert(external, values);
return insertUri;
}else{
...
}
4)在共享目录指定文件夹下创建文件
主要是在公共目录下创建文件或文件夹拿到本地路径uri,不同的Uri,可以保存到不同的公共目录中。接下来使用输入输出流就可以写入文件。
重点:AndroidQ中不支持file://类型访问文件,只能通过uri方式访问。
/**
* 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法
*/
private Uri createImageUri() {
String status = Environment.getExternalStorageState();
// 判断是否有SD卡,优先使用SD卡存储,当没有SD卡时使用手机存储
if (status.equals(Environment.MEDIA_MOUNTED)) {
return getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());
} else {
return getContext().getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues());
}
}
5)通过MediaStore API读取公共目录下的文件
if (cursor != null && cursor.moveToFirst()) {
do {
...
int _id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));
Uri imageUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, _id);
...
} while (!cursor.isLast() && cursor.moveToNext());
} else {
...
}
// 通过uri获取bitmap
public Bitmap getBitmapFromUri(Context context, Uri uri) {
ParcelFileDescriptor parcelFileDescriptor = null;
FileDescriptor fileDescriptor = null;
Bitmap bitmap = null;
try {
parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
if (parcelFileDescriptor != null && parcelFileDescriptor.getFileDescriptor() != null) {
fileDescriptor = parcelFileDescriptor.getFileDescriptor();
//转换uri为bitmap类型
bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (parcelFileDescriptor != null) {
parcelFileDescriptor.close();
}catch (IOException e) {
}
}
return bitmap;
}
6)使用MediaStore删除文件
context.getContentResolver().delete(fileUri, null, null);
3 设备ID
3.1 IMEI等设备信息
从Android10开始普通应用不再允许请求权限android.permission.READ_PHONE_STATE。而且,无论你的App是否适配过Android Q(既targetSdkVersion是否大于等于29),均无法再获取到设备IMEI等设备信息。
受影响的API:
Build.getSerial();
TelephonyManager.getImei();
TelephonyManager.getMeid()
TelephonyManager.getDeviceId();
TelephonyManager.getSubscriberId();
TelephonyManager.getSimSerialNumber();
targetSdkVersion<29 的应用,其在获取设备ID时,会直接返回null
targetSdkVersion>=29 的应用,其在获取设备ID时,会直接抛出异常SecurityException
如果您的App希望在Android 10以下的设备中仍然获取设备IMEI等信息,可按以下方式进行适配:
<uses-permission android:name="android.permission.READ_PHONE_STATE"
android:maxSdkVersion="28"/>
3.2 Mac地址随机分配
从Android10开始,默认情况下,在搭载 Android 10 或更高版本的设备上,系统会传输随机分配的 MAC 地址。(即从Android 10开始,普通应用已经无法获取设备的真正mac地址,标识设备已经无法使用mac地址)
3.3 如何标识设备唯一性
3.3.1 Google解决方案:如果您的应用有追踪非登录用户的需求,可用ANDROID_ID来标识设备。
ANDROID_ID生成规则:签名+设备信息+设备用户
ANDROID_ID重置规则:设备恢复出厂设置时,ANDROID_ID将被重置
String androidId = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
3.3.2 信通院统一SDK(OAID)
统一标识依据电信终端产业协会(TAF)、移动安全联盟(MSA)联合推 出的团体标准《移动智能终端补充设备标识规范》开发,移动智能终端补充设备标识体系统一调用 SDK 集成设备厂商提供的接口,并获得主流设备厂商的授权。
移动安全联盟(MSA)组织中国信息通信研究院(以下简称“中国信通院”)与终端生产企业、互联网企业共同研究制定了“移动智能终端补充设备标识体系”,定义了移动智能终端补充设备标识体系的体系架构、功能要求、接口要求以及安全要求,使设备生产企业统一开发接口,为移动应用开发者提供统一调用方式,方便移动应用接入,降低维护成本。
1)SDK获取
MSA 统一 SDK 下载地址:
移动安全联盟官网,http://www.msa-alliance.cn/
2)接入方式
解压miit_mdid_sdk_v1.0.13.rar,
把 miit_mdid_1.0.13.aar 拷贝到项目中,并设置依赖。
将 supplierconfig.json 拷贝到项目 assets 目录下,并修改里边对应 内容,特别是需要设置 appid 的部分。需要设置 appid 的部分需要去对应的厂 商的应用商店里注册自己的 app。
{
"supplier":{
"xiaomi":{
"appid":"***"
},
"huawei":{
"appid":"***"
}
...
}
}
在初始化方法中调用JLibrary.InitEntry
try {
JLibrary.InitEntry(FoundationContextHolder.getContext());
} catch (Throwable e) {
}
实例化MSA SDK
public static void initMSASDK(Context context){
int code = 0;
try {
code = MdidSdkHelper.InitSdk(context,true,listener);
if (code == ErrorCode.INIT_ERROR_MANUFACTURER_NOSUPPORT){//1008611,不支持的厂商
}else if (code == ErrorCode.INIT_ERROR_DEVICE_NOSUPPORT){//1008612,不支持的设备
}else if (code == ErrorCode.INIT_ERROR_LOAD_CONFIGFILE){//1008613,加载配置文件失败
}else if (code == ErrorCode.INIT_ERROR_RESULT_DELAY){//1008614,信息将会延迟返回,获取数据可能在异步线程,取决于设备
}else if (code == ErrorCode.INIT_HELPER_CALL_ERROR){//1008615,反射调用失败
}
//code可记录异常供分析
}catch (Throwable throwable){
}
}
static IIdentifierListener listener = new IIdentifierListener() {
@Override
public void OnSupport(boolean support, IdSupplier idSupplier) {
try{
isSupport = support;
if (null != idSupplier && isSupport){
//是否支持补充设备标识符获取
oaid = idSupplier.getOAID();
aaid = idSupplier.getAAID();
vaid = idSupplier.getVAID();
}else {
...
}
}catch (Exception e){
}
}
};
通过以上方法获取到OAID等设备标识之后,即可作为唯一标识使用。
4、明文HTTP限制
当SDK版本大于API 28时,默认限制了HTTP请求,并出现相关日志“java.net.UnknownServiceException: CLEARTEXT communication to xxx not permitted by network security policy“。
该问题有两种解决方案:
1)在AndroidManifest.xml中Application节点添加如下代码
<application android:usesCleartextTraffic="true">
2)在res目录新建xml目录,已建的跳过 在xml目录新建一个xml文件network_security_config.xml,然后在AndroidManifest.xml中Application添加如下节点代码。
android:networkSecurityConfig="@xml/network_config"
network_config.xml(命名随机)
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
Dagger2的依赖注入与ButterKnife的有什么区别?
Dagger2的@Singleston与自己手写的单例有什么区别?
如果没有在application中进行统一获取 app component的话 会是局部单例 要全局单例需要获取application中统一的app component
Android9.0与10.0分别更新了什么
事件分发机制?