下面列出全部的信号量以及所代表的含义:

#define SIGHUP 1 // 终端连接结束时发出(不管正常或非正常) #define SIGINT 2 // 程序终止(例如Ctrl-C) #define SIGQUIT 3 // 程序退出(Ctrl-) #define SIGILL 4 // 执行了非法指令,或者试图执行数据段,堆栈溢出 #define SIGTRAP 5 // 断点时产生,由debugger使用 #define SIGABRT 6 // 调用abort函数生成的信号,表示程序异常 #define SIGIOT 6 // 同上,更全,IO异常也会发出 #define SIGBUS 7 // 非法地址,包括内存地址对齐出错,比如访问一个4字节的整数, 但其地址不是4的倍数 #define SIGFPE 8 // 计算错误,比如除0、溢出 #define SIGKILL 9 // 强制结束程序,具有最高优先级,本信号不能被阻塞、处理和忽略 #define SIGUSR1 10 // 未使用,保留 #define SIGSEGV 11 // 非法内存操作,与 SIGBUS不同,他是对合法地址的非法访问, 比如访问没有读权限的内存,向没有写权限的地址写数据 #define SIGUSR2 12 // 未使用,保留 #define SIGPIPE 13 // 管道破裂,通常在进程间通信产生 #define SIGALRM 14 // 定时信号, #define SIGTERM 15 // 结束程序,类似温和的 SIGKILL,可被阻塞和处理。通常程序如 果终止不了,才会尝试SIGKILL #define SIGSTKFLT 16 // 协处理器堆栈错误 #define SIGCHLD 17 // 子进程结束时, 父进程会收到这个信号。 #define SIGCONT 18 // 让一个停止的进程继续执行 #define SIGSTOP 19 // 停止进程,本信号不能被阻塞,处理或忽略 #define SIGTSTP 20 // 停止进程,但该信号可以被处理和忽略 #define SIGTTIN 21 // 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号 #define SIGTTOU 22 // 类似于SIGTTIN, 但在写终端时收到 #define SIGURG 23 // 有紧急数据或out-of-band数据到达socket时产生 #define SIGXCPU 24 // 超过CPU时间资源限制时发出 #define SIGXFSZ 25 // 当进程企图扩大文件以至于超过文件大小资源限制 #define SIGVTALRM 26 // 虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间. #define SIGPROF 27 // 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间 #define SIGWINCH 28 // 窗口大小改变时发出 #define SIGIO 29 // 文件描述符准备就绪, 可以开始进行输入/输出操作 #define SIGPOLL SIGIO // 同上,别称 #define SIGPWR 30 // 电源异常 #define SIGSYS 31 // 非法的系统调用

一般关注SIGILL(执行了非法指令,或者试图执行数据段,堆栈溢出), SIGABRT(调用abort函数生成的信号,表示程序异常), SIGBUS(非法地址,包括内存地址对齐出错,比如访问一个4字节的整数, 但其地址不是4的倍数), SIGFPE, SIGSEGV, SIGSTKFLT, SIGSYS即可。

要订阅异常发生的信号,最简单的做法就是直接用一个循环遍历所有要订阅的信号,对每个信号调用sigaction()。

注意

JNI_OnLoad是最适合安装信号初始函数的地方。建议在上报时调用Java层的方法统一上报。Native崩溃捕获注册。

6、崩溃分析流程

首先,应收集崩溃现场的一些相关信息,如下:

1、崩溃信息

进程名、线程名崩溃堆栈和类型有时候也需要知道主线程的调用栈

2、系统信息

系统运行日志

/system/etc/event-log-tags

机型、系统、厂商、CPU、ABI、Linux版本等

注意,我们可以去寻找共性问题,如下:

设备状态是否root是否是模拟器

3、内存信息

系统剩余内存

/proc/meminfo

当系统可用内存小于MemTotal的10%时,OOM、大量GC、系统频繁自杀拉起等问题非常容易出现。

应用使用内存

包括Java内存、RSS、PSS

PSS和RSS通过/proc/self/smap计算,可以得到apk、dex、so等更详细的分类统计。

虚拟内存

获取大小:

/proc/self/status

获取其具体的分布情况:

/proc/self/maps

需要注意的是,对于32位进程,32位CPU,虚拟内存达到3GB就可能会引起内存失败的问题。如果是64位的CPU,虚拟内存一般在3~4GB。如果支持64位进程,虚拟内存就不会成为问题。

4、资源信息

如果应用堆内存和设备内存比较充足,但还出现内存分配失败,则可能跟资源泄漏有关。

文件句柄fd

获取fd的限制数量:

/proc/self/limits

一般单个进程允许打开的最大句柄个数为1024,如果超过800需将所有fd和文件名输出日志进行排查。

线程数

获取线程数大小:

/proc/self/status

一个线程一般占2MB的虚拟内存,线程数超过400个比较危险,需要将所有tid和线程名输出到日志进行排查。

JNI

容易出现引用失效、引用爆表等崩溃。

通过DumpReferenceTables统计JNI的引用表,进一步分析是否出现JNI泄漏等问题。

补充加油站:dumpReferenceTables的出处

在dalvik.system.VMDebug类中,是一个native方法,亦是static方法;在JNI中可以这么调用

jclass vm_class = env->FindClass(“dalvik/system/VMDebug”); jmethodID dump_mid = env->GetStaticMethodID( vm_class, “dumpReferenceTables”, “()V” ); env->CallStaticVoidMethod( vm_class, dump_mid );

5、应用信息

崩溃场景关键操作路径其它跟自身应用相关的自定义信息:运行时间、是否加载补丁、是否全新安装或升级。

6、崩溃分析流程

接下来进行崩溃分析:

1、确定重点

确认严重程度优先解决Top崩溃或对业务有重大影响的崩溃:如启动、支付过程的崩溃Java崩溃:如果是OOM,需进一步查看日志中的内存信息和资源信息Native崩溃:查看signal、code、fault addr以及崩溃时的Java堆栈

常见的崩溃类型有:

SIGSEGV:空指针、非法指针等SIGABRT:ANR、调用abort推出等

如果是ANR,先看主线程堆栈、是否因为锁等待导致,然后看ANR日志中的iowait、CPU、GC、systemserver等信息,确定是I/O问题或CPU竞争问题还是大量GC导致的ANR。

注意,当从一条崩溃日志中无法看出问题原因时,需要查看相同崩溃点下的更多崩溃日志,或者也可以查看内存信息、资源信息等进行异常排查。

2、查找共性

机型、系统、ROM、厂商、ABI这些信息都可以作为共性参考,对于下一步复现问题有明确指引。

3、尝试复现

复现之后再增加日志或使用Debugger、GDB进行调试。如不能复现,可以采用一些高级手段,如xlog日志、远程诊断、动态分析等等。

补充加油站:系统崩溃解决方式

1、通过共性信息查找可能的原因2、尝试使用其它使用方式规避3、Hook解决

7、实战:使用Breakpad捕获native崩溃

首先,这里给出《Android开发高手课》张绍文老师写的crash捕获示例工程,工程里面已经集成了Breakpad 来获取发生 native crash 时候的系统信息和线程堆栈信息。下面来详细介绍下使用Breakpad来分析native崩溃的流程:

1、示例工程是采用cmake的构建方式,所以需要先到Android Studio中SDK Manager中的SDK Tools下下载NDK和cmake。

2、安装实例工程后,点击CRASH按钮产生一个native崩溃。生成的 crash信息,如果授予Sdcard权限会优先存放在/sdcard/crashDump下,便于我们做进一步的分析。反之会放到目录 /data/data/com.dodola.breakpad/files/crashDump中。

3、使用adb pull命令将抓取到的crash日志文件放到电脑本地目录中:

adb pull /sdcard/crashDump/***.dmp > ~/Documents/crash_log.dmp

4、下载并编译Breakpad源码,在src/processor目录下找到minidump_stackwalk,使用这个工具将dmp文件转换为txt文件:

// 在项目目录下clone Breakpad仓库 git clone https://github.com/google/breakpad.git

// 切换到Breakpad根目录进行配置、编译 cd breakpad ./configure && make

// 使用src/processor目录下的minidump_stackwalk工具将dmp文件转换为txt文件 ./src/processor/minidump_stackwalk ~/Documents/crashDump/crash_log.dmp >crash_log.txt

5、打开crash_log.txt,可以得到如下内容:

Operating system: Android 0.0.0 Linux 4.4.78-perf-g539ee70 #1 SMP PREEMPT Mon Jan 14 17:08:14 CST 2019 aarch64 CPU: arm64 8 CPUs

GPU: UNKNOWN

Crash reason: SIGSEGV /SEGV_MAPERR Crash address: 0x0 Process uptime: not available

Thread 0 (crashed) 0 libcrash-lib.so + 0x650

其中我们需要的关键信息为CPU是arm64的,并且crash的地址为0x650。接下来我们需要将这个地址转换为代码中对应的行。

6、使用ndk 中提供的addr2line来根据地址进行一个符号反解的过程。

如果是arm64的so使用 $NDKHOME/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-addr2line。

如果是arm的so使用 $NDKHOME/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-addr2line。

由crash_log.txt的信息可知,我们机器的cpu架构是arm64的,因此需要使用aarch64-linux-android-addr2line这个命令行工具。该命令的一般使用格式如下: // 注意:在mac下 ./ 代表执行文件 ./aarch64-linux-android-addr2line -e 对应的.so 需要解析的地址

上述中对应的.so文件在项目编译之后,会出现在Chapter01-master/sample/build/intermediates/merged_native_libs/debug/out/lib/arm64-v8a/libcrash-lib.so这个位置,由于我的手机CPU架构是arm64的,所以这里选择的是arm64-v8a中的libcrash-lib.so。接下来我们使用aarch64-linux-android-addr2line这个命令:

./aarch64-linux-android-addr2line -f -C -e ~/Documents/open-project/Chapter01-master/sample/build/intermediates/merged_native_libs/debug/out/lib/arm64-v8a/libcrash-lib.so 0x650

参数含义: -e --exe=:指定需要转换地址的可执行文件名。 -f --functions:在显示文件名、行号输出信息的同时显示函数名信息。 -C --demangle[=style]:将低级别的符号名解码为用户级别的名字。

结果输出为:

Crash() /Users/quchao/Documents/open-project/Chapter01-master/sample/src/main/cpp/crash.cpp:10

由此,我们得出crash的代码行为crash.cpp文件中的第10行,接下来根据项目具体情况进行相应的修改即可。

Tips:这是从事NDK开发(音视频、图像处理、OpenCv、热修复框架开发)同学调试native层错误时经常要使用的技巧,强烈建议熟练掌握。

6、疑难Crash解决方案

最后,笔者这里再讲解下一些疑难Crash的解决方案。

问题1:如何解决Android 7.0 Toast BadTokenException?

参考Android 8.0 try catch的做法,代理Toast里的mTN(handler)就可以实现捕获异常。

问题2:如何解决 SharedPreference apply 引起的 ANR 问题

apply为什么会引起ANR?

SP 调用 apply 方法,会创建一个等待锁放到 QueuedWork 中,并将真正的数据持久化封装成一个任务放到异步队列中执行,任务执行结束会释放锁。Activity onStop 以及 Service 处理 onStop,onStartCommand 时,执行 QueuedWork.waitToFinish() 等待所有的等待锁释放。

如何解决?

所有此类 ANR 都是经由 QueuedWork.waitToFinish() 触发的,只要在调用此函数之前,将其中保存的队列手动清空即可。

具体是Hook ActivityThrad的Handler变量,拿到此变量后给其设置一个Callback,Handler 的 dispatchMessage 中会先处理 callback。最后在 Callback 中调用队列的清理工作,注意队列清理需要反射调用 QueuedWork。

注意

apply 机制本身的失败率就比较高(1.8%左右),清理等待锁队列对持久化造成的影响不大。

问题3:如何解决TimeoutExceptin异常?

它是由系统的FinalizerWatchdogDaemon抛出来的。

这里首先介绍下看门狗 WatchDog,它 的作用是监控重要服务的运行状态,当重要服务停止时,发生 Timeout 异常崩溃,WatchDog 负责将应用重启。而当关闭 WatchDog(执行stop()方法)后,当重要服务停止时,也不会发生 Timeout 异常,是一种通过非正常手段防止异常发生的方法。

规避方案

stop方法,在Android 6.0之前会有线程同步问题。 因为6.0之前调用threadToStop的interrupt方法是没有加锁的,所以可能会有线程同步的问题。

注意:Stop的时候有一定概率导致即使没有超时也会报timeoutexception。

缺点

只是为了避免上报异常采取的一种hack方案,并没有真正解决引起finialize超时的问题。

问题4:如何解决输入法的内存泄漏?

通过反射将输入法的两个View置空。

7、进程保活

我们可以利用SyncAdapter提高进程优先级,它是Android系统提供一个账号同步机制,它属于核心进程级别,而使用了SyncAdapter的进程优先级本身也会提高,使用方式请Google,关联SyncAdapter后,进程的优先级变为1,仅低于前台正在运行的进程,因此可以降低应用被系统杀掉的概率。

8、总结

对于App的Crash优化,总的来说,我们需要考虑以下四个要点:

1、重在预防:重视应用的整个流程、包括开发人员的培训、编译检查、静态扫描、规范的测试、灰度、发布流程等2、不应该随意使用try catch去隐藏问题:而应该从源头入手,了解崩溃的本质原因,保证后面的运行流程。3、解决崩溃的过程应该由点到面,考虑一类崩溃怎么解决。4、崩溃与内存、卡顿、I/O内存紧密相关

三、ANR优化

1、ANR监控实现方式

1、使用FileObserver监听 /data/anr/traces.txt的变化

缺点

高版本ROM需要root权限。

解决方案

海外Google Play服务、国内Hardcoder。

2、监控消息队列的运行时间

卡顿监控原理:

利用主线程的消息队列处理机制,应用发生卡顿,一定是在dispatchMessage中执行了耗时操作。我们通过给主线程的Looper设置一个Printer,打点统计dispatchMessage方法执行的时间,如果超出阀值,表示发生卡顿,则dump出各种信息,提供开发者分析性能瓶颈。

为卡顿监控代码增加ANR的线程监控,在发送消息时,在ANR线程中保存一个状态,主线程消息执行完后再Reset标志位。如果在ANR线程中收到发送消息后,超过一定时间没有复位,就可以任务发生了ANR。

缺点

无法准确判断是否真正出现ANR,只能说明APP发生了UI阻塞,需要进行二次校验。校验的方式就是等待手机系统出现发生了Error的进程,并且Error类型是NOT_RESPONDING(值为2)。

在每次出现ANR弹框前,Native层都会发出signal为SIGNAL_QUIT(值为3)的信号事件,也可以监听此信号。

无法得到完整ANR日志隶属于卡顿优化的方式

3、需要考虑应用退出场景

主动自杀Process.killProcess()、exit()等。崩溃系统重启系统异常、断电、用户重启等:通过比较应用开机运行时间是否比之前记录的值更小。被系统杀死被LMK杀死、从系统的任务管理器中划掉等。

注意

由于traces.txt上传比较耗时,所以一般线下采用,线上建议综合ProcessErrorStateInfo和出现ANR时的堆栈信息来实现ANR的实时上传。

2、ANR优化

ANR发生原因:没有在规定的时间内完成要完成的事情。

ANR分类

发生场景

Activity onCreate方法或Input事件超过5s没有完成;BroadcastReceiver前台10s,后台60s;ContentProvider 在publish过超时10s;Service前台20s,后台200s。

发生原因

主线程有耗时操作复杂布局IO操作被子线程同步锁block被Binder对端blockBinder被占满导致主线程无法和SystemServer通信得不到系统资源(CPU/RAM/IO)

从进程角度看发生原因有:

当前进程:主线程本身耗时或者主线程的消息队列存在耗时操作、主线程被本进程的其它子线程所blocked远端进程:binder call、socket通信

Andorid系统监测ANR的核心原理是消息调度和超时处理。

ANR排查流程

1、Log获取

1、抓取bugreport

adb shell bugreport > bugreport.txt

2、直接导出/data/anr/traces.txt文件

adb pull /data/anr/traces.txt trace.txt

2、搜索“ANR in”处log关键点解读

发生时间(可能会延时10-20s) pid:当pid=0,说明在ANR之前,进程就被LMK杀死或出现了Crash,所以无法接受到系统的广播或者按键消息,因此会出现ANR cpu负载Load: 7.58 / 6.21 / 4.83

代表此时一分钟有平均有7.58个进程在等待 1、5、15分钟内系统的平均负荷 当系统负荷持续大于1.0,必须将值降下来 当系统负荷达到5.0,表面系统有很严重的问题

cpu使用率

CPU usage from 18101ms to 0ms ago 28% 2085/system_server: 18% user + 10% kernel / faults: 8689 minor 24 major 11% 752/android.hardware.sensors@1.0-service: 4% user + 6.9% kernel / faults: 2 minor 9.8% 780/surfaceflinger: 6.2% user + 3.5% kernel / faults: 143 minor 4 major

上述表示Top进程的cpu占用情况。

注意

如果CPU使用量很少,说明主线程可能阻塞。

3、在bugreport.txt中根据pid和发生时间搜索到阻塞的log处

----- pid 10494 at 2019-11-18 15:28:29 -----

4、往下翻找到“main”线程则可看到对应的阻塞log

“main” prio=5 tid=1 Sleeping | group=“main” sCount=1 dsCount=0 flags=1 obj=0x746bf7f0 self=0xe7c8f000 | sysTid=10494 nice=-4 cgrp=default sched=0/0 handle=0xeb6784a4 | state=S schedstat=( 5119636327 325064933 4204 ) utm=460 stm=51 core=4 HZ=100 | stack=0xff575000-0xff577000 stackSize=8MB | held mutexes=

上述关键字段的含义如下所示:

tid:线程号sysTid:主进程线程号和进程号相同Waiting/Sleeping:各种线程状态nice:nice值越小,则优先级越高,-17~16schedstat:Running、Runable时间(ns)与Switch次数utm:该线程在用户态的执行时间(jiffies)stm:该线程在内核态的执行时间(jiffies)sCount:该线程被挂起的次数dsCount:该线程被调试器挂起的次数self:线程本身的地址

补充加油站:各种线程状态

需要注意的是,这里的各种线程状态指的是Native层的线程状态,关于Java线程状态与Native线程状态的对应关系如下所示:

enum ThreadState { // Thread.State JDWP state kTerminated = 66, // TERMINATED TS_ZOMBIE Thread.run has returned, but Thread* still around kRunnable, // RUNNABLE TS_RUNNING runnable kTimedWaiting, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout kSleeping, // TIMED_WAITING TS_SLEEPING in Thread.sleep() kBlocked, // BLOCKED TS_MONITOR blocked on a monitor kWaiting, // WAITING TS_WAIT in Object.wait() kWaitingForLockInflation, // WAITING TS_WAIT blocked inflating a thin-lock kWaitingForTaskProcessor, // WAITING TS_WAIT blocked waiting for taskProcessor kWaitingForGcToComplete, // WAITING TS_WAIT blocked waiting for GC kWaitingForCheckPointsToRun, // WAITING TS_WAIT GC waiting for checkpoints to run kWaitingPerformingGc, // WAITING TS_WAIT performing GC kWaitingForDebuggerSend, // WAITING TS_WAIT blocked waiting for events to be sent kWaitingForDebuggerToAttach, // WAITING TS_WAIT blocked waiting for debugger to attach kWaitingInMainDebuggerLoop, // WAITING TS_WAIT blocking/reading/processing debugger events kWaitingForDebuggerSuspension, // WAITING TS_WAIT waiting for debugger suspend all kWaitingForJniOnLoad, // WAITING TS_WAIT waiting for execution of dlopen and JNI on load code kWaitingForSignalCatcherOutput, // WAITING TS_WAIT waiting for signal catcher IO to complete kWaitingInMainSignalCatcherLoop, // WAITING TS_WAIT blocking/reading/processing signals kWaitingForDeoptimization, // WAITING TS_WAIT waiting for deoptimization suspend all kWaitingForMethodTracingStart, // WAITING TS_WAIT waiting for method tracing to start kWaitingForVisitObjects, // WAITING TS_WAIT waiting for visiting objects kWaitingForGetObjectsAllocated, // WAITING TS_WAIT waiting for getting the number of allocated objects kWaitingWeakGcRootRead, // WAITING TS_WAIT waiting on the GC to read a weak root kWaitingForGcThreadFlip, // WAITING TS_WAIT waiting on the GC thread flip (CC collector) to finish kStarting, // NEW TS_WAIT native thread started, not yet ready to run managed code kNative, // RUNNABLE TS_RUNNING running in a JNI native method kSuspended, // RUNNABLE TS_RUNNING suspended by GC or debugger };

其它分析方法:Java线程调用分析方法

先使用jps命令列出当前系统中运行的所有Java虚拟机进程,拿到应用进程的pid。然后再使用jstack命令查看该进程中所有线程的状态以及调用关系,以及一些简单的分析结果。

3、关于ANR的一些常见问题

1、sp调用apply导致anr问题?

虽然apply并不会阻塞主线程,但是会将等待时间转嫁到主线程。

2、检测运行期间是否发生过异常退出?

在应用启动时设定一个标志,在主动自杀或崩溃后更新标志 ,下次启动时检测此标志即可判断。

4、理解ANR的触发流程

broadcast跟service超时机制大抵相同,但有一个非常隐蔽的技能点,那就是通过静态注册的广播超时会受SharedPreferences(简称SP)的影响。

当SP有未同步到磁盘的工作,则需等待其完成,才告知系统已完成该广播。并且只有XML静态注册的广播超时检测过程会考虑是否有SP尚未完成,动态广播并不受其影响。

对于Service, Broadcast, Input发生ANR之后,最终都会调用AMS.appNotResponding。对于provider,在其进程启动时publish过程可能会出现ANR, 则会直接杀进程以及清理相应信息,而不会弹出ANR的对话框。

1、AMS.appNotResponding流程

输出ANR Reason信息到EventLog. 也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信息。收集并输出重要进程列表中的各个线程的traces信息,该方法较耗时。输出当前各个进程的CPU使用情况以及CPU负载情况。将traces文件和 CPU使用情况信息保存到dropbox,即data/system/dropbox目录(ANR信息最为重要的信息)。根据进程类型,来决定直接后台杀掉,还是弹框告知用户。

2、AMS.dumpStackTraces流程

1、收集firstPids进程的stacks:

第一个是发生ANR进程;第二个是system_server;其余的是mLruProcesses中所有的persistent进程。

2、收集Native进程的stacks。(dumpNativeBacktraceToFile)

依次是mediaserver,sdcard,surfaceflinger进程。

3、收集lastPids进程的stacks:

依次输出CPU使用率top 5的进程;

注意

上述导出每个进程trace时,进程之间会休眠200ms。

四、移动端业务高可用方案建设

1、业务高可用重要性

关于业务高可用重要性有如下五点:

高可用性能业务侧重于用户功能完整可用真实影响收入

2、业务高可用方案建设

业务高可用方案建设需要注意的点比较繁杂,但是总体可以归结为如下几点:

数据采集梳理项目主流程、核心路径、关键节点Aop自动采集、统一上报报警策略:阈值报警、趋势报警、特定指标报警、直接上报(或底阈值)异常监控单点追查:需要针对性分析的特定问题,全量日志回捞,专项分析兜底策略配置中心、功能开关跳转分发中心(组件化路由)

3、移动端容灾方案

灾包括:

性能异常业务异常

传统流程:

用户反馈、重新打包、渠道更新、不可接受。

容灾方案建设

关于容灾方案的建设主要可以细分为以下七点,下面,我们分别来了解下。

1、功能开关

配置中心,服务端下发配置控制

针对场景

功能新增代码改动

2、统跳中心

界面切换通过路由,路由决定是否重定向Native Bug不能热修复则跳转到临时H5页面

3、动态化修复

热修复能力,可监控、灰度、回滚、清除。

4、推拉结合、多场景调用保证到达率

5、Weex、RN增量更新

6、安全模式

微信读书、蘑菇街、淘宝、天猫等“重运营”的APP都使用了安全模式保障客户端启动流程,启动失败后给用户自救机会。先介绍一下它的核心特点:

根据Crash信息自动恢复,多次启动失败重置应用为安装初始状态严重Bug可阻塞性热修复

安全模式设计

配置后台:统一的配置后台,具备灰度发布机制

1、客户端能力:

在APP连续Crash的情况下具备分级、无感自修复能力具备同步热修复能力具备指定触发某项特定功能的能力具体功能注册能力,方便后期扩展安全模式

2、数据统计及告警

统一的数据平台 最后看一下《Android框架体系架构(高级UI+FrameWork源码)》学习需要的所有知识点的思维导图。在刚刚那份学习笔记里包含了下面知识点所有内容!文章里已经展示了部分!如果你正愁这块不知道如何学习或者想提升学习这块知识的学习效率,那么这份学习笔记绝对是你的秘密武器!

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频 如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)

了下面知识点所有内容!文章里已经展示了部分!如果你正愁这块不知道如何学习或者想提升学习这块知识的学习效率,那么这份学习笔记绝对是你的秘密武器!

[外链图片转存中…(img-7pUud4ZC-1711020541698)]

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。 [外链图片转存中…(img-gg5vkfPE-1711020541698)] [外链图片转存中…(img-VXDmr1JF-1711020541699)] [外链图片转存中…(img-GsH2FtUP-1711020541700)] [外链图片转存中…(img-zmRBu4pW-1711020541700)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频 如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android) [外链图片转存中…(img-ueNJbX8l-1711020541701)]

文章链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: