【腾讯Bugly干货分享】Android 插件技术实战总结

本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/1p5Y0f5XdVXN2EZYT0AM_A

前言

安卓应用开发的大量难题,其实最后都需要插件技术去解决。

现今插件技术的使用非常普遍,比如微信、QQ、淘宝、天猫、空间、携程、大众点评、手机管家等等这些大家在熟悉不过的应用都在使用。

插件技术可以给项目开发带来巨大的好处,比如:并行高效开发、模块解耦、解除单个dex函数不能超过65535的限制、动态更新升级、按需加载等等。

本文的目的是从一个典型的复杂项目中总结出较为全面与完整的安卓插件技术。

掌握好插件技术,需要如下的安卓基础和相关知识,例如:

1. Android应用程序安装,加载过程

2. Android应用运行机制,生命周期调用原理

3. Android应用资源编译打包原理

4. Android应用读取资源原理

5. Android系统AMS、PMS、NMS等系统服务的运作原理

6. 增量更新

7. HOOK等技术

插件技术知识领域如图:

这些技术中每一个点都需要大篇幅内容才能完全讲清楚。不过,好在Android是开源的,每一个插件技术涉及到的技术点都可以翻阅源码进行进一步的研究。下面我从当前所负责的一个插件化项目(PACEWEAR手表助手)经历,来梳理一下插件技术的应用及核心内容。

项目的困惑

PACEWEAR手表助手原自腾讯TOS的智能穿戴项目。

因为目前大部分智能手表和手环还不能独立联网通讯,须通过蓝牙连接手机,借助手机的网络来完成一系列业务功能。PACEWEAR手表助手就是这么一个手机软件,帮助智能穿戴设备使用手机网络,并通过蓝牙连接的方式完成对智能穿戴设备的各种配置和管理。

PACEWEAR手表助手项目开始初期,业务并没有大面积铺开,三四个工程师还算跑的比较顺利,随着项目的进展,主工程框架、登录、配对、设置、ota、市场、天气、地图、运动、音乐、健康管理、支付、应用管理、表盘管理等功能不断加入,参与的人也慢慢变多,问题也就多了起来,维护越来越困难,总结有如下几点:

  1. 工程频繁报方法数超65535
  2. 多个模块在同一个app中开发代码耦合,架构冗余,牵一发而动全身
  3. 人员效率低下,时间往往花费在沟通,构建问题处理上
  4. 分工不明确,灰色地带重复逻辑比较多
  5. 业务与业务之间互相调用,不够独立
  6. 问题跟进原来越繁琐,牵扯人数众多
  7. 功能越来越多,目前这种开发方式不可持续
  8. 连接的手表和手环设备种类越来越多

针对以上问题虽然我们考虑过动态加载jar、Html5等措施来缓解,但最终还是没能彻底从根本上解决这些问题,一直在苦恼着整个项目团队...

寻找适合项目的插件框架

这种情况下我们很快意识到需要引入插件化的开发模式,才能一劳永逸地这解决这一系列问题。

引入Dynamic-load-apk插件框架

团队在2015年中开始引入了Dynamic-load-apk(后面简称DyLA)框架,这套框架是从App应用层解决加载插件的问题:创建一个继承自Activity的ProxyActivity类,然后让插件中的所有Activity都继承自ProxyActivity,并重写Activity所有的方法。然而在功能上,仅支持Activity组件,这个是这套框架最大的短板;另外基于这套框架进行的插件应用开发,依赖条件复杂[需要内置jar包,组件必须实现ProxyActivity的所有接口]、调试困难等各种问题。重重约束是的项目插件化业务进展及其缓慢,比如支付模块两个同事开发了两个月最后发现很多需求没法实现,最终不得不放弃插件化;健康模块开发不到两周的同事开始抓狂,被各种问题不断折腾着(为啥不能联调、为什么这个要特殊处理、为什么这里资源找不到等等)。最后仅有健康、Yiya语音极少数几个模块勉强插件化。随着项目的进展,业务模块的不断增多,当初的问题不但没有得到解决,反而增加了对DyLA模块的维护,这个状态一直持续到了2016下半年9月。

预研适合项目的插件框架

PACEWEAR手表助手项目团队在9月份初对比了一些开源插件框架的能力:

同时评估了他们的优缺点,最后确定基于APF进行开发一套适合PACEWEAR手表助手的插件框架。

然而,仅是支持application和四大组件还远远不能满足PACEWEAR手表助手项目的要求,PACEWEAR手表助手有二十多个业务模块,第一批需要进行插件化的就有十五个,由不同的同事进行开发负责,而且有些业务还需要和第三方进行交互对接...因此,团队要能高效的将PACEWEAR手表助手项目完成插件化并且让所有插件业务都符合产品需求稳定的运行,对插件框架要求首先就需要做到基于框架开发的插件应用功能对齐原生,这样框架就需要:

  1. 支持application、四大组件(activity4个LaunchMode)、so、fragment、notification、toast等基础能力
  2. 支持联调插件应用
  3. 支持加载本地网页等
  4. 支持插件自定义控件和样式
  5. 组件进程配置等原生应用程序的能力;

同时需要这套框架支持将宿主的基础能力:设备账号信息、和手表通讯、统计上报、文件传输、网络、ota、控件库及宿主的资源共享给插件应用;

另外需要将插件运行时间及在宿主中的显示与宿主完全解耦。不然插件的调整必然要影响到宿主的代码调整,这可不是一个明智的落地方案。

综合上面的要求及项目进行过程中的调整,经过进一个月的努力,这套框架终于预研成功,正式应用到PACEWEAR手表助手项目上。

这套框架就叫TwsPluginFramework框架(后面简称TPF框架,已经开源:https://github.com/rickdynasty/TwsPluginFramework )。

这套框架相比业界其他插件框架能力对比如下:

另外Hook系统服务的安全隐患是不可预知的,因此TwsPluginFramework框架尽可能少的对系统服务等进行hook处理。

TPF框架原理

插件技术的实现原理是源于Android系统(Android系统本身就是一套插件框架,运行在这个系统之上的应用就是一个个的”插件应用”)对应用的管理机制:安装(Install)、运行(Running)、卸载(Uninstall)。

运行在TPF框架之上的插件应用和android应用程序又有所不同,不同点主要有下面几点:

  1. 应用程序的安装有android系统负责完成,而插件应用的安装流程由插件框架负责完成;
  2. 插件应用没有走系统的安装流程,组件等信息没有被注册到系统里面,要使插件应用能正常的运行,插件框架需要将这些插件应用内部的组件全部“合法化”;
  3. 插件应用的卸载也不走系统的卸载流程,而是由宿主负责完成的。

上面三个流程中安装、卸载基本和系统的处理方式是一样的。而运行就一样,插件应用程序的运行需要经过“插件框架”这个中间层进行合法化后才能运行在系统里面,这个合法化过程就需要做很多事,下面会重点讲解,先来看一下插件控件的这几个流程和系统的差别:

系统应用管理机制示例图

TPF框架插件应用管理机制示意图

插件框架是插件化项目的核心,它运行在宿主应用里面。宿主程序在启动过程中的第一件事就是将插件框架加载好,以便接下来可以运行插件应用里面的业务。

插件框架是插件应用的承载体,负责了插件应用的安装、运行、卸载管理。因插件应用并不是直接安装在系统里面,因此插件框架就必须承载android系统的这一系列能力:

  1. 必须自己去识别插件应用并完成拷贝解析工作
  2. 必须给插件应用组件赋予android系统正常的生命才能让插件应用正常运行。
  3. 必须自己去清理将要卸载的应用数据和正在运行的功能及组件。

剖析TPF框架

下面我就从加载TPF插件框架、安装插件应用程序、运行插件应用程序、卸载插件应用程序四个环节详细讲述一下TPF框架内幕。

加载TPF插件框架

宿主程序在启动过程中的首要事情就是将插件框架加载好,以便接下来可以将插件应用正常的运作起来。插件框架在整个项目工程中扮演的是一个极其核心的角色:除了负责所有插件应用的安装卸载,还需要赋予插件应用组件一个合法的身份。

在android系统中,应用程序运行的背后有很多服务在维持这些组件的运作,比如ActivityManagerService、PackageManagerService、WindowManagerService、NotificationManagerService等以及应用程序背后的ActivityThread等等,这些都是TPF框架需要Hook的范围内容。

具体的流程如下:

为了让插件应用内部的组件合法化,插件框架需要对应用程序做一些HOOK处理,以便让插件的组件能正常运行。

安装插件应用程序

插件应用程序要能够运行在宿主里面,首先得经过安装这个过程让宿主知道当前这个插件应用的信息,然后插件框架就会将当前插件解压拷贝到指定目录以便后面的运行需要。

在TwsPluginFramework框架中,插件包就是一个应用程序apk。对插件信息的收集方式和系统一样,通过解析AndroidManifest.xml来收集应用信息,包括版本、sdk、application、四大组件等等。

具体的流程如下:

这个过程基本和应用程序的安装过程无异,只是插件应用程序的显示图标等内容直接由插件框架在解析的过程中获取并拷贝到私有目录下面。

运行插件应用程序

运行插件内部的任何组件之前,首先得加载好插件的代码和资源,然后就在构建插件的上下文以及Application等信息,TwsPluginFramework框架启动插件的流程图如下:

类加载

在TwsPluginFramework框架中,通过DexClassLoader来加载插件应用的代码, DexClassLoade的使用示意图如下:

TwsPluginFramework框架在构建插件应用的ClassLoader的时候会指定其父ClassLoader为宿主的。这样插件内部就可以直接访问宿主的代码内容。

资源加载

在TwsPluginFramework框架中资源的加载和系统一样,也是通过AssetManager的addAssetPath/addAssetPaths方法进行处理的,只是这两个方法是隐藏的,得用通过反射来调用。

在TwsPluginFramework框架里,在构建插件应用上下文Resource的时候,将宿主的资源与插件的资源合并在一起了。这样做的好处就是插件应用可以共享宿主的资源数据。

对于插件框架来说,如何处理插件资源和宿主资源是一个非常纠结的选择:

然而,资源合并方案就得处理资源ID冲突问题,在TwsPluginFramework框架里面是通过修改AAPT来指定插件应用资源的package id,从而达到区分宿主和插件的资源id的目的。

生命周期

插件应用程序是运行在插件框架这个中间层上面的,而非直接运行在android系统里的。也正因为如此,插件框架就需得自己去完成应用程序包的内容加载以及组件的生命赋予工作。

在Android的世界里面,应用的组件是有“生命”的,比如:activity、service、BroadcastReceive、application等,这种“生命”是由Android系统所赋予的。

对于应用程序来说,只要在AndroidManifest.xml里面注册便可以轻易获得这种生命,因为应用的I(安装)R(运行)U(卸载)是由安装系统来承载的。而对于插件应用的I(安装)R(运行)U(卸载)是由运行在宿主里面的插件框架来承载的。仅因这一点的差别,使得插件应用内部的组件如果不做一些特殊处理,系统是不会给予它们“生命”的。

在TwsPluginFramework框架里面,插件的组件是拥有真正生命周期,完全交由系统管理、非反射代理。插件应用并没有经过系统安装,内部的组件并没有注册到系统里面。那TPF是怎么做到让插件里面的组件也能让系统给没被注册的插件应用组件拥有完整生命周期的?

答案就在TPF框架里面的两个计策: 偷梁换柱、瞒天过海。

瞒天过海:在宿主中提前申明好多个组件,在向系统请求启动的过程中用这些预先申明号的组件去做请求,等系统的校验流程结束后换回成目标的插件组件,从而达到瞒过系统。

瞒天过海环节需要在宿主中申明好用来做替身的receiver、service(多个)[独立进程的单独配置多个]、activity(多个) [不同single模式的单独配置多个]。

偷梁换柱:为了让系统能够按着我们的意愿在组件启时将目标插件组件替换成宿主中预先申明号的对应组件,等系统校验环节过了在换回成目标插件组件,我们就需要替换掉应用程序空间一些重要的处理对象,比如:ActivityThread里面负责应用程序与系统交互的Instrumentation对象以及组件处理流程的回调Handler.Callback等。

下面就以基本组件的启动流程来描述一下这两个计策:

Activity

Activity生命周期大家在熟悉不过了,可是在onCreate之前系统做很多你所不知道的事。

从点击桌面图标(或者出发启动一个activity)到这个应用activity组件进入onCreate()

这个环节是解决插件组件activity完整生命周期的关键。这个环节在TwsPluginFramework框架内部的处理流程:

从开始执行execStartActivity到最终将Activity对象new出来这个过程,系统层会去校验需要启动的activity的合法性[是否有在应用的AndroidManifest.xml里面注册]以及按启动要求创建activity对象。了解了这点就可以很好的绕过系统的约束,达到需要的目的。

Service

stopService、bindService以及sendBroadcast的流程和startService是一样的,这里就不赘述了。

卸载插件应用程序

当前插件应用要下架或者需要更新到新版本的时候,就需要将当前的插件应用给卸载掉。这个过程和Android系统卸载应用程序是一样的。

和插件应用安装过程相反,这个过程就是清理记录在宿主插件框架里面的信息、删除代码和资源同时停止所有该插件正在运行的组件及服务。

流程如下:

显示协议框架

TPF框架将插件在宿主中的调用时机及显示入口完全与宿主解耦,也就是说插件应用的调整不需调整宿主程序的任何代码。这些都归功于TPF提供了一套显示协议框架,插件应用只需要知道显示协议的使用就可以,显示协议(可以根据项目需求自定义,下面是输出给PACEWEAR手表助手插件应用项目的规范) 的概要如下:

显示位置pos: 1 Hotseat; 2 MyWatchFragment; 3 ActionBarMenu; 4 其他
分隔符: # 分割DisplayConfig; @ 分割DisplayConfig的属性; = 属性赋值; / 分割属性值
图标资源icon:统一使用 模块名_[hotseat or watch_fragment or menu]_描述信息.png 配置在AndroidManifest.xml不需要带后缀。 【normal/focus/press/...】
标题title:中文/英文 也可以只配置一个
显示内容content:如果是fragment 直接配置name,其他的配置类名信息

内容类型ctype:1 fragment; 2 activity; 3 service; 4 application; 5 view
插件启动时机: 1 手动触发 2 随DM启动 3 配对成功后
 插件依赖: 1 已安装的app 2 已安装的插件

ActionBar 配置只在显示位置是Hotseat的前提下可用
ActionBar标题ab-title:actionbar标题 中文/英文 也可以只配置一个 暂不支持subTitle
ActionBar右侧按钮显示内容ab-rbtncontent:actionbar右侧按钮点击触发显示内容
ActionBar右侧按钮显示内容类型ab-rbtnctype:  触发显示内容 的类型 1 fragment; 2 activity; 3 service; 4 application; 5 view(当前只支持activity,如果是activity可以不配置)
ActionBar右侧按钮内容ab-rbtnres: 显示在按钮上的内容根据类型不同而不一样(类型1 文本;类型2 图标
ActionBar右侧按钮内容ab-rbtnrestype:1、文本按钮(res配置中英文String) 2、ImageButton(res配置图标) 

更多详细的内容请移步到https://github.com/rickdynasty/TwsPluginFramework

TPF框架给项目团队带来的好处

当前PACEWEAR手表助手项目除宿主应用外还有15个(业务)插件应用,PACEWEAR手表助手仅仅是一个包含基础功能和插件框架的调度平台。后续所有新增加的业务都会议插件应用的方式集成进来,宿主基本不用care到底有哪些业务会集成进来。而且当前PACEWEAR手表助手项目计划将其他两个产品项目合并进来成一个平台产品。这一切的改善很大部分是TPF带来的,下面总结了一下TPF框架的好处:

  1. 业务模块完全解耦,不再有调整一个模块而影响到另一个甚至多个模块的情况。
  2. 各个业务的插件应用开发、编译各自进行,开发效率大幅度提升,从而缩短开发周期。
  3. 业务插件可单独动态更新升级,不需要重启PACEWEAR手表助手便可生效。
  4. 对于宿主 — PACEWEAR手表助手来说,可以按需求加载需要的插件应用,这样本来多个相似的产品线就可以合成一个,大幅度降低人力成本。
  5. 不再被65535困扰。
  6. 团队协作更和谐。
  7. ...

TPF框架一路走过的经典Bug

Theme/Style异常

Log截图:

这类问题主要出现在第一套区分资源ID方案(通过public.xml的public-padding特性来处理)上,这类问题的根本原因是:android系统处理应用资源,在底层处理ResourceTable的bag资源的出现了异常。

Android资源管理机制是一个非常复杂的课题(包括:资源打包、资源加载、资源寻找,每一块又分java层和C层),有兴趣兴趣的可以去翻一下源码,在线地址:http://androidxref.com 。简单来说这个问题:“就是style不同于其他资源,style本身是不创建资源的,它仅仅是一个资源的应用集合,而系统访问资源是通过偏移量的方式去获取资源。这种方式在同一个packageID的段来说,只要style是连续的就ok。但是如果不符合这个要求,那上面的问题就会出现。”

在TPF的第一套区分资源ID方案中,通过public.xml的public-padding特性来区分资源id,不难做到让style连续,但要做到多个插件工程并发的情况下做到连续却是基本不可能。这也是为什么TPF放弃了这套方案的原因。

明白了其中的原因,要解决这类问题也就简单了。

解决方案:尽可能的符合系统规则,在同一个packageID段内让相同type的资源ID连续就行。当前通过修改aapt来指定资源的packageID是一个很好的方式。

ClassNotFound

严格来说这个不是TPF框架的问题,TPF框架在处理加载代码上完全是按着系统的规格要求。把这类问题拿出来放这里,只是因为在项目开发过程中插件工程反馈之类问题不较多。

出现ClassNotFound,无非两种情况:1、类被混淆了 2、类不在当前ClassLoader的可视范围内。

解决方案:

  1. 混淆的很容有处理,找出来不做混淆就行。
  2. 不在ClassLoader可视范围内这个就需要注意一下,插件的ClassLoader父类是宿主的ClassLoader,这个自然就不存在插件内部范文不了的情况。在TPF里面多次出现这个问题的主要原因在共享库的更新上:TPF提供了一套共享库,这套库里面包括了一套控件、宿主基础能力、和手表通讯、网络、文件传输等等一系列共性的内容,在开发阶段难免会对内容进行变更处理,而有些插件工程如果长时间没有更新,那就有可能出现ClassNotFound的问题。这样就需要在调整的时候做兼容,同事插件开发同事及时更新sdk。

Resources$NotFoundException

在TPF里面,插件是可以直接访问宿主提供的共享资源,然而这仅仅只能满足插件内部的逻辑流程。

  1. 但在极少特定机型(比如:vivo)里面会比较奇葩的存在这类问题。

    解决方案:插件的上下文以及Resources对象(PluginResourceWrapper)都是由TPF构造的。在插件的PluginResourceWrapper内部进行重定向到宿主就可以了。

  2. 但对于Notification等这些系统的通用服务也是会出这类问题。这些服务内部通过id获取资源,最终是会落到宿主的上下文上面。而对于宿主来说,插件的资源是不可见,自然就没法通过插件的resID来获取插件的资源。

    解决方案:像Notification这类的系统服务,如果需要传递资源id到系统里面进行处理获取资源,一律使用宿主的资源id。

备注:情况②没法用情况①的方式进行处理的原因这里简单描述一下:应用程序在启动的过程中,在application被关联之前Resources就创建好了,而且这个Resources对象在ContextImpl里面还是final类型,这样再java层就没法实施偷梁换柱的方式进行替换处理。

项目进展过程中更多的bug记录请移步:https://github.com/rickdynasty/TwsPluginFramework_Doc

TwsPluginFramework(TPF)框架现已经开源:

https://github.com/rickdynasty/TwsPluginFramework



更多精彩内容欢迎关注腾讯 Bugly的微信公众账号:

腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!

时间: 2024-10-28 19:19:41

【腾讯Bugly干货分享】Android 插件技术实战总结的相关文章

【腾讯Bugly干货分享】Android Linker 与 SO 加壳技术

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57e3a3bc42eb88da6d4be143 作者:王赛 1. 前言 Android 系统安全愈发重要,像传统pc安全的可执行文件加固一样,应用加固是Android系统安全中非常重要的一环.目前Android 应用加固可以分为dex加固和Native加固,Native 加固的保护对象为 Native 层的 SO 文件,使用加壳.反调试.混淆.VM 等手段增加SO文件的反编译难

【腾讯Bugly干货分享】浅谈Android自定义锁屏页的发车姿势

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57875330c9da73584b025873 一.为什么需要自定义锁屏页 锁屏作为一种黑白屏时代就存在的手机功能,至今仍发挥着巨大作用,特别是触屏时代的到来,锁屏的功用被发挥到了极致.多少人曾经在无聊的时候每隔几分钟划开锁屏再关上,孜孜不倦,其酸爽程度不亚于捏气泡膜.确实,一款漂亮的锁屏能为手机增色不少,但锁屏存在的核心目的主要是三个:保护自己手机的隐私,防止误操作,在不关闭

【腾讯Bugly干货分享】移动App入侵与逆向破解技术-iOS篇

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e0acc896e9ebb6865f321 如果您有耐心看完这篇文章,您将懂得如何着手进行app的分析.追踪.注入等实用的破解技术,另外,通过"入侵",将帮助您理解如何规避常见的安全漏洞,文章大纲: 简单介绍ios二进制文件结构与入侵的原理 介绍入侵常用的工具和方法,包括pc端和手机端 讲解黑客技术中的静态分析和动态分析法 通过一个简单的实例,来介绍如何综合运用砸

【腾讯Bugly干货分享】TRIM:提升磁盘性能,缓解Android卡顿

Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处.在业内,Android 手机一直有着“越用越慢”的口碑.根据第三方的调研数据显示,有77%的 Android 手机用户承认自己曾遭遇过手机变慢的影响.他们不明白为什么购买之初“如丝般顺滑”的 Android 手机,在使用不到一年之后都会“卡顿”得让人抓狂!根据我们初步的测试数据,手机长期所使用产生的磁盘碎片可以使得磁盘的写入效率下降为

【腾讯bugly干货分享】Android自绘动画实现与优化实战——以Tencent OS录音机波形动

前言 本文为腾讯bugly的原创内容,非经过本文作者同意禁止转载,原文地址为:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1180 我们所熟知的,Android 的图形绘制主要是基于 View 这个类实现. 每个 View 的绘制都需要经过 onMeasure.onLayout.onDraw 三步曲,分别对应到测量大小.布局.绘制. Android 系统为了简化线程开发,降低应用开发的难度,将这三个过程都放在应用的主线程(UI 线程)

【腾讯bugly干货分享】微信Android热补丁实践演进之路

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1264&extra=page%3D1 继插件化后,热补丁技术在2015年开始爆发,目前已经是非常热门的Android开发技术.其中比较著名的有淘宝的Dexposed.支付宝的AndFix以及QZone的超级热补丁方案.微信对热补丁技术的研究并不算早,大约开始于2015年6月.经过研究与尝试现有的各个方案,我们发现它

【腾讯Bugly干货分享】Android Patch 方案与持续交付

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57a31921ac3a1fb613dd40f3 Android 不仅系统版本众多,机型众多,而且各个市场都各有各的政策和审核速度,每次发布一个版本对于开发同学来讲都是一种漫长的煎熬.相比于 iOS 两三天就能达到 80% 的覆盖速度而言,Android 应用版本升级至少需要两周才能达到 80% 的升级率,严重阻碍了版本迭代速度.也导致市场上 App 版本分散,处理 bug 和投

【腾讯Bugly干货分享】Android UI:机智的远程动态更新策略

Android UI:机智的远程动态更新策略 作者:王金波    腾讯Bugly特约撰稿人 1问题描述 做过Android开发的人都遇到过这样的问题:随着需求的变化,某些入口界面通常会出现 UI的增加.减少.内容变化.以及跳转界面发生变化等问题.每次发生变化都要手动修改代码,而入口界面通常具有未读信息提醒这样的"小红点"逻辑:一旦UI变化,"小红点"逻辑也要重新计算.如果不同的RD来维护这些代码,耦合性非常高,出错概率也很大.本文以自选股的个人页卡为例(界面如下图所

【腾讯Bugly干货分享】从0到1打造直播 App

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5811d42e7fd6ec467453bf58 作者:李智文 概要 分享内容: 互联网内容载体变迁历程,文字--图片/声音--视频--VR/AR----..从直播1.0秀场时代(YY),2.0游戏直播(斗鱼.虎牙.熊猫)到如今全民直播3.0泛生活娱乐时代(映客.花椒),国外直播app(Meerkat .Periscope),随着VA/AR/MR提出的沉浸式视听体验,直播4.0时