当前位置: 首页 > news >正文

网站的开发建设要做什么免费可信网站认证

网站的开发建设要做什么,免费可信网站认证,广元市建设局网站,舟山网络公司网站建设公司Crash率是衡量一个App好坏的重要指标之一#xff0c;如果你忽略了它的存在#xff0c;它就会愈演愈烈#xff0c;最后造成大量用户的流失#xff0c;进而给公司带来无法估量的损失。本文讲述美团外卖Android客户端团队在将App的Crash率从千分之三做到万分之二过程中所做的大… Crash率是衡量一个App好坏的重要指标之一如果你忽略了它的存在它就会愈演愈烈最后造成大量用户的流失进而给公司带来无法估量的损失。本文讲述美团外卖Android客户端团队在将App的Crash率从千分之三做到万分之二过程中所做的大量实践工作抛砖引玉希望能够为其他团队提供一些经验和启发。 面临的挑战和成果 面对用户使用频率高外卖业务增长快Android碎片化严重这些问题美团外卖Android App如何持续的降低Crash率是一项极具挑战的事情。通过团队的全力全策美团外卖Android App的平均Crash率从千分之三降到了万分之二最优值万一左右Crash率统计方式Crash次数/DAU。 美团外卖自2013年创建以来业务就以指数级的速度发展。美团外卖承载的业务从单一的餐饮业务发展到餐饮、超市、生鲜、果蔬、药品、鲜花、蛋糕、跑腿等十多个大品类业务。目前美团外卖日完成订单量已突破2000万成为美团点评最重要的业务之一。美团外卖客户端所承载的业务模块越来越多产品复杂度越来越高团队开发人员日益增加这些都给App降低Crash率带来了巨大的挑战。 Crash的治理实践 对于Crash的治理我们尽量遵守以下三点原则 由点到面。一个Crash发生了我们不能只针对这个Crash的去解决而要去考虑这一类Crash怎么去解决和预防。只有这样才能使得这一类Crash真正被解决。异常不能随便吃掉。随意的使用try-catch只会增加业务的分支和隐蔽真正的问题要了解Crash的本质原因根据本质原因去解决。catch的分支更要根据业务场景去兜底保证后续的流程正常。预防胜于治理。当Crash发生的时候损失已经造成了我们再怎么治理也只是减少损失。尽可能的提前预防Crash的发生可以将Crash消灭在萌芽阶段。常规的Crash治理 常规Crash发生的原因主要是由于开发人员编写代码不小心导致的。解决这类Crash需要由点到面根据Crash引发的原因和业务本身统一集中解决。常见的Crash类型包括空节点、角标越界、类型转换异常、实体对象没有序列化、数字转换异常、Activity或Service找不到等。这类Crash是App中最为常见的Crash也是最容易反复出现的。在获取Crash堆栈信息后解决这类Crash一般比较简单更多考虑的应该是如何避免。下面介绍两个我们治理的量比较大的Crash。 NullPointerException NullPointerException是我们遇到最频繁的造成这种Crash一般有两种情况 对象本身没有进行初始化就进行操作。对象已经初始化过但是被回收或者手动置为null然后对其进行操作。针对第一种情况导致的原因有很多可能是开发人员的失误、API返回数据解析异常、进程被杀死后静态变量没初始化导致我们可以做的有 对可能为空的对象做判空处理。养成使用NonNull和Nullable注解的习惯。尽量不使用静态变量万不得已使用SharedPreferences来存储。考虑使用Kotlin语言。针对第二种情况大部分是由于Activity/Fragment销毁或被移除后在Message、Runnable、网络等回调中执行了一些代码导致的我们可以做的有 Message、Runnable回调时判断Activity/Fragment是否销毁或被移除加try-catch保护Activity/Fragment销毁时移除所有已发送的Runnable。封装LifecycleMessage/Runnable基础组件并自定义Lint检查提示使用封装好的基础组件。在BaseActivity、BaseFragment的onDestory()里把当前Activity所发的所有请求取消掉。IndexOutOfBoundsException 这类Crash常见于对ListView的操作和多线程下对容器的操作。 针对ListView中造成的IndexOutOfBoundsException经常是因为外部也持有了Adapter里数据的引用(如在Adapter的构造函数里直接赋值)这时如果外部引用对数据更改了但没有及时调用notifyDataSetChanged()则有可能造成Crash对此我们封装了一个BaseAdapter数据统一由Adapter自己维护通知 同时也极大的避免了The content of the adapter has changed but ListView did not receive a notification这两类Crash目前得到了统一的解决。 另外很多容器是线程不安全的所以如果在多线程下对其操作就容易引发IndexOutOfBoundsException。常用的如JDK里的ArrayList和Android里的SparseArray、ArrayMap同时也要注意有一些类的内部实现也是用的线程不安全的容器如Bundle里用的就是ArrayMap。 系统级Crash治理 众所周知Android的机型众多碎片化严重各个硬件厂商可能会定制自己的ROM更改系统方法导致特定机型的崩溃。发现这类Crash主要靠云测平台配合自动化测试以及线上监控这种情况下的Crash堆栈信息很难直接定位问题。下面是常见的解决思路 尝试找到造成Crash的可疑代码看是否有特异的API或者调用方式不当导致的尝试修改代码逻辑来进行规避。通过Hook来解决Hook分为Java Hook和Native Hook。Java Hook主要靠反射或者动态代理来更改相应API的行为需要尝试找到可以Hook的点一般Hook的点多为静态变量同时需要注意Android不同版本的API类名、方法名和成员变量名都可能不一样所以要做好兼容工作Native Hook原理上是用更改后方法把旧方法在内存地址上进行替换需要考虑到Dalvik和ART的差异相对来说Native Hook的兼容性更差一点所以用Native Hook的时候需要配合降级策略。如果通过前两种方式都无法解决的话我们只能尝试反编译ROM寻找解决的办法。我们举一个定制系统ROM导致Crash的例子根据Crash平台统计数据发现该Crash只发生在vivo V3Max这类机型上Crash堆栈如下 java.lang.RuntimeException: An error occured while executing doInBackground()at android.os.AsyncTask$3.done(AsyncTask.java:304)at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)at java.util.concurrent.FutureTask.setException(FutureTask.java:222)at java.util.concurrent.FutureTask.run(FutureTask.java:242)at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)at java.lang.Thread.run(Thread.java:818) Caused by: java.lang.NullPointerException: Attempt to invoke interface method int java.util.List.size() on a null object referenceat android.widget.AbsListView$UpdateBottomFlagTask.isSuperFloatViewServiceRunning(AbsListView.java:7689)at android.widget.AbsListView$UpdateBottomFlagTask.doInBackground(AbsListView.java:7665)at android.os.AsyncTask$2.call(AsyncTask.java:292)at java.util.concurrent.FutureTask.run(FutureTask.java:237)... 4 more我们发现原生系统上对应系统版本的AbsListView里并没有UpdateBottomFlagTask类因此可以断定是vivo该版本定制的ROM修改了系统的实现。我们在定位这个Crash的可疑点无果后决定通过Hook的方式解决通过源码发现AsyncTask$SerialExecutor是静态变量是一个很好的Hook的点通过反射添加try-catch解决。因为修改的是final对象所以需要先反射修改accessFlags需要注意ART和Dalvik下对应的Class不同代码如下 public static void setFinalStatic(Field field, Object newValue) throws Exception {field.setAccessible(true);Field artField Field.class.getDeclaredField(artField);artField.setAccessible(true);Object artFieldValue artField.get(field);Field accessFlagsFiled artFieldValue.getClass().getDeclaredField(accessFlags);accessFlagsFiled.setAccessible(true);accessFlagsFiled.setInt(artFieldValue, field.getModifiers() ~Modifier.FINAL);field.set(null, newValue);} private void initVivoV3MaxCrashHander() {if (!isVivoV3()) {return;}try {setFinalStatic(AsyncTask.class.getDeclaredField(SERIAL_EXECUTOR), new SafeSerialExecutor());Field defaultfield AsyncTask.class.getDeclaredField(sDefaultExecutor);defaultfield.setAccessible(true);defaultfield.set(null, AsyncTask.SERIAL_EXECUTOR);} catch (Exception e) {L.e(e);} }美团外卖App用上述方法解决了对应的Crash但是美团App里的外卖频道因为平台的限制无法通过这种方式于是我们尝试反编译ROM。 Android ROM编译时会将framework、app、bin等目录打入system.img中system.img是Android系统中用来存放系统文件的镜像 (image)文件格式一般为yaffs2或ext。但Android 5.0开始支持dm-verity后system.img不再提供而是提供了三个文件system.new.datsystem.patch.datsystem.transfer.list因此我们首先需要通过上述的三个文件得到system.img。但我们将vivo ROM解压后发现厂商将system.new.dat进行了分片如下图所示 经过对system.transfer.list中的信息和system.new.dat 1 2 3 … 文件大小对比研究发现一些共同点system.transfer.list中的每一个block数*4KB 与对应的分片文件的大小大致相同,故大胆猜测vivo ROM对system.patch.dat分片也只是单纯的按block先后顺序进行了分片处理。所以我们只需要在转化img前将这些分片文件合成一个system.patch.dat文件就可以了。最后根据system.img的文件系统格式进行解包拿到framework目录其中有framework.jar和boot.oat等文件因为Android4.4之后引入了ART虚拟机会预先把system/framework中的一些jar包转换为oat格式所以我们还需要将对应的oat文件通过ota2dex将其解包获得dex文件之后通过dex2jar和jd-gui查看源码。 OOM OOM是OutOfMemoryError的简称在常见的Crash疑难排行榜上OOM绝对可以名列前茅并且经久不衰。因为它发生时的Crash堆栈信息往往不是导致问题的根本原因而只是压死骆驼的最后一根稻草。 导致OOM的原因大部分如下 内存泄漏大量无用对象没有被及时回收导致后续申请内存失败。大内存对象过多最常见的大对象就是Bitmap几个大图同时加载很容易触发OOM。内存泄漏 内存泄漏指系统未能及时释放已经不再使用的内存对象一般是由错误的程序代码逻辑引起的。在Android平台上最常见也是最严重的内存泄漏就是Activity对象泄漏。Activity承载了App的整个界面功能Activity的泄漏同时也意味着它持有的大量资源对象都无法被回收极其容易造成OOM。 常见的可能会造成Activity泄漏的原因有 匿名内部类实现Handler处理消息可能导致隐式持有的Activity对象无法回收。Activity和Context对象被混淆和滥用在许多只需要Application Context而不需要使用Activity对象的地方使用了Activity对象比如注册各类Receiver、计算屏幕密度等等。View对象处理不当使用Activity的LayoutInflater创建的View自身持有的Context对象其实就是Activity这点经常被忽略在自己实现View重用等场景下也会导致Activity泄漏。对于Activity泄漏目前已经有了一个非常好用的检测工具LeakCanary它可以自动检测到所有Activity的泄漏情况并且在发生泄漏时给出十分友好的界面提示同时为了防止开发人员的疏漏我们也会将其上报到服务器统一检查解决。另外我们可以在debug下使用StrictMode来检查Activity的泄露、Closeable对象没有被关闭等问题。 大对象 在Android平台上我们分析任一应用的内存信息几乎都可以得出同样的结论占用内存最多的对象大都是Bitmap对象。随着手机屏幕尺寸越来越大屏幕分辨率也越来越高1080p和更高的2k屏已经占了大半份额为了达到更好的视觉效果我们往往需要使用大量高清图片同时也为OOM埋下了祸根。 对于图片内存优化我们有几个常用的思路 尽量使用成熟的图片库比如Glide图片库会提供很多通用方面的保障减少不必要的人为失误。根据实际需要也就是View尺寸来加载图片可以在分辨率较低的机型上尽可能少地占用内存。除了常用的BitmapFactory.Options#inSampleSize和Glide提供的BitmapRequestBuilder#override之外我们的图片CDN服务器也支持图片的实时缩放可以在服务端进行图片缩放处理从而减轻客户端的内存压力。 分析App内存的详细情况是解决问题的第一步我们需要对App运行时到底占用了多少内存、哪些类型的对象有多少个有大致了解并根据实际情况做出预测这样才能在分析时做到有的放矢。Android Studio也提供了非常好用的Memory Profiler堆转储和分配跟踪器功能可以帮我们迅速定位问题。AOP增强辅助 AOP是面向切面编程的简称在Android的Gradle插件1.5.0中新增了Transform API之后编译时修改字节码来实现AOP也因为有了官方支持而变得非常方便。 在一些特定情况下可以通过AOP的方式自动处理未捕获的异常 抛异常的方法非常明确调用方式比较固定。异常处理方式比较统一。和业务逻辑无关即自动处理异常后不会影响正常的业务逻辑。典型的例子有读取Intent Extras参数、读取SharedPreferences、解析颜色字符串值和显示隐藏Window等等。这类问题的解决原理大致相同我们以Intent Extras为例详细介绍一下。读取Intent Extras的问题在于我们非常常用的方法 Intent#getStringExtra 在代码逻辑出错或者恶意攻击的情况下可能会抛出ClassNotFoundException异常而我们平时在写代码时又不太可能给所有调用都加上try-catch语句于是一个更安全的Intent工具类应运而生理论上只要所有人都使用这个工具类来访问Intent Extras参数就可以防止此类型的Crash。但是面对庞大的旧代码仓库和诸多的业务部门修改现有代码需要极大成本还有更多的外部依赖SDK基本不可能使用我们自己的工具类此时就需要AOP大展身手了。 我们专门制作了一个Gradle插件只需要配置一下参数就可以将某个特定方法的调用替换成另一个方法 WaimaiBytecodeManipulator {replacements(android/content/Intent.getIntExtra(Ljava/lang/String;I)Icom/waimai/IntentUtil.getInt(Landroid/content/Intent;Ljava/lang/String;I)I,android/content/Intent.getStringExtra(Ljava/lang/String;)Ljava/lang/String;com/waimai/IntentUtil.getString(Landroid/content/Intent;Ljava/lang/String;)Ljava/lang/String;,android/content/Intent.getBooleanExtra(Ljava/lang/String;Z)Zcom/waimai/IntentUtil.getBoolean(Landroid/content/Intent;Ljava/lang/String;Z)Z,...)} }上面的配置就可以将App代码包括第三方库里所有的Intent.getXXXExtra调用替换成IntentUtil类中的安全版实现。当然并不是所有的异常都只需要catch住就万事大吉如果真的有逻辑错误肯定需要在开发和测试阶段及时暴露出来所以在IntentUtil中会对App的运行环境做判断Debug下会将异常直接抛出开发同学可以根据Crash堆栈分析问题Release环境下则在捕获到异常时返回对应的默认值然后将异常上报到服务器。 依赖库的问题 Android App经常会依赖很多AAR 每个AAR可能有多个版本打包时Gradle会根据规则确定使用的最终版本号默认选择最高版本或者强制指定的版本而其他版本的AAR将被丢弃。如果互相依赖的AAR中有不兼容的版本存在的问题在打包时是不能发现的只有在相关代码执行时才会出现会造成NoClassDefFoundError、NoSuchFieldError、NoSuchMethodError等异常。如图所示order和store两个业务库都依赖了platform.aar一个是1.0版本一个是2.0版本默认最终打进APK的只有platform 2.0版本这时如果order库里用到的platform库里的某个类或者方法在2.0版本中被删除了运行时就可能发生异常虽然SDK在升级时会尽量做到向下兼容但很多时候尤其是第三方SDK是没法得到保证的在美团外卖Android App v6.0版本时因为这个原因导致热修复功能丧失因此为了提前发现问题我们接入了依赖检查插件Defensor。 Defensor在编译时通过DexTask获取到所有的输入文件(也就是被编译过的class文件)然后检查每个文件里引用的类、字段、方法等是否存在。 除此之外我们写了一个Gradle插件SVD(strict version dependencies)来对那些重要的SDK的版本进行统一管理。插件会在编译时检查Gradle最终使用的SDK版本是否和配置中的一致如果不一致插件会终止编译并报错并同时会打印出发生冲突的SDK的所有依赖关系。 Crash的预防实践 单纯的靠约定或规范去减少Crash的发生是不现实的。约定和规范受限于组织架构和具体执行的个人很容易被忽略只有靠工程架构和工具才能保证Crash的预防长久的执行下去。 工程架构对Crash率的影响 在治理Crash的实践中我们往往忽略了工程架构对Crash率的影响。Crash的发生大部分原因是源于程序员的不合理的代码而程序员工作中最直接的接触的就是工程架构。对于一个边界模糊层级混乱的架构程序员是更加容易写出引起Crash的代码。在这样的架构里面即使程序员意识到导致某种写法存在问题想要去改善这样不合理的代码也是非常困难的。相反一个层级清晰边界明确的架构是能够大大减少Crash发生的概率治理和预防Crash也是相对更容易。这里我们可以举几个我们实践过的例子阐述。 业务模块的划分 原来我们的Crash基本上都是由个别同学关注解决的团队里的每个同学都会提交可能引起Crash的代码如果负责Crash的同学因为某些事情暂时没有关注App的Crash率那么造成Crash的同学也不会知道他的代码引起了Crash。 对于这个问题我们的做法是App的业务模块化。业务模块化后每个业务都有都有唯一包名和对应的负责人。当某个模块发生了Crash可以根据包名提交问题给这个模块的负责人让他第一时间进行处理。业务模块化本身也是工程架构优先需要考虑的事情之一。 页面跳转路由统一处理页面跳转 对外卖App而言使用过程中最多的就是页面间的跳转而页面间跳转经常会造成ActivityNotFoundException例如我们配了一个scheme但对方的scheme路径已经发生了变化又例如我们调用手机上相册的功能而相册应用已被用户自己禁用或移除了。解决这一类Crash其实也很简单只需要在startActivity增加ActivityNotFoundException异常捕获即可。但一个App里启动Activity的地方几乎是随处可见无法预测哪一处会造成ActivityNotFoundException。 我们的做法是将页面的跳转都通过我们封装的scheme路由去分发。这样的好处是通过scheme路由在工程架构上所有业务都是解耦模块间不需要相互依赖就可以实现页面的跳转和基本类型参数的传递同时由于所有的页面跳转都会走scheme路由我们只需要在scheme路由里一处加上ActivityNotFoundException异常捕获即可解决这种类型的Crash。路由设计示意图如下 网络层统一处理API脏数据 客户端的很大一部分的Crash是因为API返回的脏数据。比如当API返回空值、空数组或返回不是约定类型的数据App收到这些数据就极有可能发生空指针、数组越界和类型转换错误等Crash。而且这样的脏数据特别容易引起线上大面积的崩溃。 最早我们的工程的网络层用法是页面监听网络成功和失败的回调网络成功后将JSON数据传递给页面页面解析Model初始化View如图所示。这样的问题就是网络虽然请求成功了但是JSON解析Model这个过程可能存在问题例如没有返回数据或者返回了类型不对的数据而这个脏数据导致问题会出现在UI层直接反应给用户。 根据上图我们可以看到由于网络层只承担了请求网络的职责没有承担数据解析的职责数据解析的职责交给了页面去处理。这样使得我们一旦发现脏数据导致的Crash就只能在网络请求的回调里面增加各种判断去兼容脏数据。我们有几百个页面补漏完全补不过来。通过几个版本的重构我们重新划分了网络层的职责如图所示 从图上可以看出重构后的网络层负责请求网络和数据解析如果存在脏数据的话在网络层就会发现问题不会影响到UI层返回给UI层的都是校验成功的数据。这样改造后我们发现这类的Crash率有了极大的改善。 大图监控 上面讲到大对象是导致OOM的主要原因之一而Bitmap是App里最常见的大对象类型因此对占用内存过大的Bitmap对象的监控就很有必要了。 我们用AOP方式Hook了三种常见图片库的加载图片回调方法同时监控图片库加载图片时的两个维度 加载图片使用的URL。外卖App中除静态资源外所有图片都要求发布到专用的图片CDN服务器上加载图片时使用正则表达式匹配URL除了限定CDN域名之外还要求所有图片加载时都要添加对应的动态缩放参数。最终加载出的图片结果也就是Bitmap对象。我们知道Bitmap对象所占内存和其分辨率大小成正比而一般情况下在ImageView上设置超过自身尺寸的图片是没有意义的所以我们要求显示在ImageView中的Bitmap分辨率不允许超过View自身的尺寸为了降低误报率也可以设定一个报警阈值。开发过程中在App里检测到不合规的图片时会立即高亮出错的ImageView所在的位置并弹出对话框提示ImageView所在的Activity、XPath和加载图片使用的URL等信息如下图辅助开发同学定位并解决问题。在Release环境下可以将报警信息上报到服务器实时观察数据有问题及时处理。 Lint检查 我们发现线上的很多Crash其实可以在开发过程中通过Lint检查来避免。Lint是Google提供的Android静态代码检查工具可以扫描并发现代码中潜在的问题提醒开发人员及早修正提高代码质量。 但是Android原生提供的Lint规则如是否使用了高版本API远远不够缺少一些我们认为有必要的检测也不能检查代码规范。因此我们开始开发自定义Lint目前我们通过自定义Lint规则已经实现了Crash预防、Bug预防、提升性能/安全和代码规范检查这些功能。如检查实现了Serializable接口的类其成员变量包括从父类继承的所声明的类型都要实现Serializable接口可以有效的避免NotSerializableException强制使用封装好的工具类如ColorUtil、WindowUtil等可以有效的避免因为参数不正确产生的IllegalArgumentException和因为Activity已经finish导致的BadTokenException。 Lint检查可以在多个阶段执行包括在本地手动检查、编码实时检查、编译时检查、commit时检查以及在CI系统中提Pull Request时检查、打包时检查等如下图所示。更详细的内容可参考《美团外卖Android Lint代码检查实践》。 资源重复检查 在之前的文章《美团外卖Android平台化架构演进实践》中讲述了我们的平台化演进过程在这个过程中大家很大的一部分工作是下沉但是下沉不完全就会导致一些类和资源的重复类因为有包名的限制不会出现问题。但是一些资源文件如layout、drawable等如果同名则下层会被上层覆盖这时layout里view的id发生了变化就可能导致空指针的问题。为了避免这种问题我们写了一个Gradle插件通过hook MergeResource这个Task拿到所有library和主库的资源文件如果检查到重复则会中断编译过程输出重复的资源名及对应的library name同时避免有些资源因为样式等原因确实需要覆盖因此我们设置了白名单。同时在这个过程中我们也拿到了所有的的图片资源可以顺手做图片大小的本地监控如下图所示 Crash的监控止损的实践 监控 在经过前面提到的各种检查和测试之后应用便开始发布了。我们建立了如下图的监控流程来保证异常发生时能够及时得到反馈并处理。首先是灰度监控灰度阶段是增量Crash最容易暴露的阶段如果这个阶段没有很好的把握住会使得增量变存量从而导致Crash率上升。如果条件允许的话可以在灰度期间制定一些灰度策略去提高这个阶段Crash的暴露。例如分渠道灰度、分城市灰度、分业务场景灰度、新装用户的灰度等等尽量覆盖所有的分支。灰度结束之后便开始全量在全量的过程中我们还需要一些日常Crash监控和Crash率的异常报警来防止突发情况的发生例如因为后台上线或者运营配置错误导致的线上Crash。除此之外还需要一些其他的监控例如之前提到的大图监控来避免因为大图导致的OOM。具体的输出形式主要有邮件通知、IM通知、报表。 止损 尽管我们在前面做了那么多但是Crash还是无法避免的例如在灰度阶段因为量级不够有些Crash没有被暴露出来又或者某些功能客户端比后台更早上线而这些功能在灰度阶段没有被覆盖到这些情况下如果出现问题就需要考虑如何止损了。 问题发生时首先需要评估重要性如果问题不是很严重而且修复成本较高可以考虑在下个版本再修复相反如果问题比较严重对用户体验或下单有影响时就必须要修复。修复时首先考虑业务降级主要看该部分异常的业务是否有兜底或者A/B策略这样是最稳妥也是最有效的方式。如果业务不能降级就需要考虑热修复了目前美团外卖Android App接入的热修复框架是自研的Robust可以修复90%以上的场景热修成功率也达到了99%以上。如果问题发生在热修复无法覆盖的场景就只能强制用户升级。强制升级因为覆盖周期长同时影响用户的体验只在万不得已的情况下才会使用。 展望 Crash的自我修复 我们在做新技术选型时除了要考虑是否能满足业务需求、是否比现有技术更优秀和团队学习成本等因素之外兼容性和稳定性也非常重要。但面对国内非富多彩的Android系统环境在体量百万级以上的的App中几乎不可能实现毫无瑕疵的技术方案和组件所以一般情况下如果某个技术实现方案可以达到0.01‰以下的崩溃率而其他方案也没有更好的表现我们就认为它是可以接受的。但是哪怕仅仅十万分之一的崩溃率也代表还有用户受到影响而我们认为Crash对用户来说是最糟糕的体验尤其是涉及到交易的场景所以我们必须本着每一单都很重要的原则尽最大努力保证用户顺利执行流程。 实际情况中有一些技术方案在兼容性和稳定性上做了一定妥协的场景往往是因为考虑到性能或扩展性等方面的优势。这种情况下我们其实可以再多做一些进一步提高App的可用性。就像很多操作系统都有“兼容模式”或者“安全模式”很多自动化机械机器都配套有手动操作模式一样App里也可以实现备用的降级方案然后设置特定条件的触发策略从而达到自动修复Crash的目的。 举例来讲Android 3.0中引入了硬件加速机制虽然可以提高绘制帧率并且降低CPU占用率但是在某些机型上还是会有绘制错乱甚至Crash的情况这时我们就可以在App中记录硬件加速相关的Crash问题或者使用检测代码主动检测硬件加速功能是否正常工作然后主动选择是否开启硬件加速这样既可以让绝大部分用户享受硬件加速带来的优势也可以保障硬件加速功能不完善的机型不受影响。 还有一些类似的可以做自动降级的场景比如 部分使用JNI实现的模块在SO加载失败或者运行时发生异常则可以降级为Java版实现。RenderScript实现的图片模糊效果也可以在失败后降级为普通的Java版高斯模糊算法。在使用Retrofit网络库时发现OkHttp3或者HttpURLConnection网络通道失败率高可以主动切换到另一种通道。这类问题都需要根据具体情况具体分析如果可以找到准确的判定条件和稳定的修复方案就可以让App稳定性再上一个台阶。 特定Crash类型日志自动回捞 外卖业务发展迅速即使我们在开发时使用各种工具、措施来避免Crash的发生但Crash还是不可避免。线上某些怪异的Crash发生后我们除了分析Crash堆栈信息之外还可以使用离线日志回捞、下发动态日志等工具来还原Crash发生时的场景帮助开发同学定位问题但是这两种方式都有它们各自的问题。离线日志顾名思义它的内容都是预先记录好的有时候可能会漏掉一些关键信息因为在代码中加日志一般只是在业务关键点在大量的普通方法中不可能都加上日志。动态日志Holmes存在的问题是每次下发只能针对已知UUID的一个用户的一台设备对于大量线上Crash的情况这种操作并不合适因为我们并不能知道哪个发生Crash的用户还会再次复现这次操作下发配置充满了不确定性。 我们可以改造Holmes使其支持批量甚至全量下发动态日志记录的日志等到发生特定类型的Crash时才上报这样一来可以减少日志服务器压力同时也可以极大提高定位问题的效率因为我们可以确定上报日志的设备最后都真正发生了该类型Crash再来分析日志就可以做到事半功倍。 总结 业务的快速发展往往不可能给团队充足的时间去治理Crash而Crash又是App最重要的指标之一。团队需要由一个个Crash个例去探究每一个Crash发生的最本质原因找到最合理解决这类Crash的方案建立解决这一类Crash的长效机制而不能饮鸩止渴。只有这样随着版本的不断迭代我们才能在Crash治理之路上离目标越来越近。 参考资料 Crash率从2.2%降至0.2%这个团队是怎么做到的Android运行时ART加载OAT文件的过程分析Android动态日志系统HolmesAndroid Hook技术防范漫谈美团外卖Android Lint代码检查实践作者简介 维康美团高级工程师2016年校招加入美团目前作为外卖Android App主力开发主要负责App Crash治理和集成构建相关工作。少杰美团高级工程师2017年加入美团目前作为外卖Android App技术负责人主要负责App监控相关工作。晓飞美团技术专家2015年加入美团是外卖Android的早期开发者之一目前作为外卖Android App负责人主要负责版本管理和业务架构。招聘 美团外卖诚招Android、iOS、FE高级/资深工程师和技术专家Base北京、上海、成都欢迎有兴趣的同学投递简历到wukai05meituan.com。
http://wiki.neutronadmin.com/news/283835/

相关文章:

  • 酒店建设网站的意义超酷网站
  • 有没有代做模型的网站大数据营销精准营销
  • 杭州网站推广优化公司免费的短视频app大全下载
  • 游戏网站服务器租用淘词神器
  • 网站后台口令网页的后台管理系统
  • 泽成seo网站排名963中华室内设计网
  • 网站制作公司网站源码群晖wordpress性能
  • 制作网站建设的公司简单个人网站模板
  • 做本地分类信息网站赚钱吗图书网站开发介绍
  • 优秀企业网站欣赏wordpress 站内链接
  • 怎样给网站做国内网站建设的趋势是怎样的
  • 360搜索怎么做网站自然优化如何给网站增加内链
  • 如何在国外社交网站上做原单外贸大多数软件仍然是定制开发的
  • 手机端快速建站工具旅游公共信息服务网站建设及服务质量标准
  • html网站模板免费网站后台用什么开发
  • 网站解决方案设计企业网站建设费用入什么科目
  • 做电影网站涉及的侵权问题网站建设现在什么服务器比较好
  • 网站建设百度知道安卓编程软件
  • 网站建设合同 技术合同域名是不是网址的地址
  • 建材 东莞网站建设谁有做爰网站号
  • 黄村专业网站开发公司深圳58网站建设
  • 网站联盟的收益模式同国外做贸易的网站
  • 大学网站建设的意义05网答案大全
  • 2008系统怎么做网站景观设计公司名称
  • 适合大学生做兼职的网站有哪些网页生成二维码生成器
  • nas做流媒体网站某某网站建设策划书2000字
  • 做封面网站对php网站开发技术课程总结
  • 南宁隆安网站建设软件技术开发工程师
  • 湛江建设局网站镇江外贸型网站建设
  • 腾讯云网站托管互联网外包公司有哪些