如何创建更加灵活的App | 大众点评 屠毅敏

47
Android DevCamp Produced by CSDN Website: http://devcamp.csdn.net / Weibo: http://weibo.com/cmdnclub/ Thursday, August 2, 12

description

移动产品迭代包袱重,大众点评讲述成功和失败经验:《如何创建更加灵活的App》| 大众点评 屠毅敏 | Android DevCamp 主题简介:Android和iOS原生应用的部署特性决定了其无法像Web开发那样灵活多变,同时造成了产品迭代速度缓慢,线上问题无法及时解决,旧版本的历史包袱越来越重等一系列的问题。在尽可能小的影响App体验的前提下如何创建出更加灵活多变的App,在应用上线后能够及时做出调整,是点评一直在努力尝试解决的问题。本次演讲将讲述点评近两年在远程配置、Native+HTML、脚本语言、动态加载等技术实现方案上的尝试及成功或失败的经验总结。 讲师简介:屠毅敏,点评网最早的手机应用开发之一,也是早期点评手机应用的主要开发者。07年底开始接触Android,08年开始iPhone应用开发。09年加入点评之前一直是个人开发者,目前在点评主要负责移动应用架构设计及新技术研发。

Transcript of 如何创建更加灵活的App | 大众点评 屠毅敏

Page 1: 如何创建更加灵活的App | 大众点评 屠毅敏

Android DevCampProduced by CSDN

Website: http://devcamp.csdn.net/Weibo: http://weibo.com/cmdnclub/

Thursday, August 2, 12

Page 2: 如何创建更加灵活的App | 大众点评 屠毅敏

创建更加灵活的App

Thursday, August 2, 12

Page 3: 如何创建更加灵活的App | 大众点评 屠毅敏

• 无法消灭旧版本,版本维护周期过长

• 基于版本的迭代速度缓慢

• 上线后无法调整

• Bug造成的影响无法即使消除

• 。。。

Why

Thursday, August 2, 12

Page 4: 如何创建更加灵活的App | 大众点评 屠毅敏

What Matters

• 需求驱动

• 成本 与 收益

Thursday, August 2, 12

Page 5: 如何创建更加灵活的App | 大众点评 屠毅敏

案例• 2010年,当时用户体验最好,用户数最多的地图类应用是Google Maps

• 但是Google Maps面临最大的问题是速度慢,经常被墙

• 国内的地图应用成为主流只是时间问题

• 如何在外部环境发生变化后及时调整?

Thursday, August 2, 12

Page 6: 如何创建更加灵活的App | 大众点评 屠毅敏

需求• 避免弹出Pick Activity对话框,根据用户手机有没有安装该地图应用决定默认打开哪款地图。优先级如下:

• 查看地图是⼀一个非常关键的功能点 ,优先级需要随时调整,并在已发布的客户端也同时生效

Priority List: 1. Google Maps 2. Google Maps (Brut) 3. 百度地图 4. 图吧地图 5. Mini Map ...

Thursday, August 2, 12

Page 7: 如何创建更加灵活的App | 大众点评 屠毅敏

问题• 每款地图应用都定义了自己的Intent调用方式

• 对于未知的地图应用,如何创建对应的Intent?

• 是否需要针对地图应用的版本号来采取不同的优先级策略?

Thursday, August 2, 12

Page 8: 如何创建更加灵活的App | 大众点评 屠毅敏

最初方案• 把策略和创建Intent交给服务器端解决

• bla bla ...

Thursday, August 2, 12

Page 9: 如何创建更加灵活的App | 大众点评 屠毅敏

把精力放在解决80%的用户需求上

Thursday, August 2, 12

Page 10: 如何创建更加灵活的App | 大众点评 屠毅敏

最终方案• 放弃支持未知地图应用

• 放弃支持版本号判断

• 把Priority List加入远程配置中

• 客户端负责顺序判断,并创建地图应用对应的Intent

Thursday, August 2, 12

Page 11: 如何创建更加灵活的App | 大众点评 屠毅敏

配置

Remote

{ ver:”20120701”, mapList: [ “Baidu”, “Google”, “Google(Brut)”, “Mapbar”, ... ]}

http://api.myserver.com/config?client=myapp&version=1.0&...

cached on local disk

Thursday, August 2, 12

Page 12: 如何创建更加灵活的App | 大众点评 屠毅敏

配置

Remote

{ ver:”20120701”, mapList: [ “Baidu”, “Google”, “Google(Brut)”, “Mapbar”, ... ]}

http://api.myserver.com/config?client=myapp&version=1.0&...

cached on local disk

User

{ mapList: [ “Baidu” ]}

stored on local disk

SharedPreference

Thursday, August 2, 12

Page 13: 如何创建更加灵活的App | 大众点评 屠毅敏

配置

Default

MyApp.apk |-res |-raw |-default

{ ver: “20120101”, mapList: [ “Google”, “Google(Brut)”, “Baidu”, “Mapbar”, ... ]}

Remote

{ ver:”20120701”, mapList: [ “Baidu”, “Google”, “Google(Brut)”, “Mapbar”, ... ]}

http://api.myserver.com/config?client=myapp&version=1.0&...

cached on local disk

User

{ mapList: [ “Baidu” ]}

stored on local disk

SharedPreference

Thursday, August 2, 12

Page 14: 如何创建更加灵活的App | 大众点评 屠毅敏

配置• 简单实用,可以满足大部分的简单需求

• 新版本提示

• 特定功能的开启或关闭

• 产品运营相关

• 等等

Thursday, August 2, 12

Page 15: 如何创建更加灵活的App | 大众点评 屠毅敏

HTML5Hybrid Application

Thursday, August 2, 12

Page 16: 如何创建更加灵活的App | 大众点评 屠毅敏

PhoneGap详见 http://phonegap.com

Thursday, August 2, 12

Page 17: 如何创建更加灵活的App | 大众点评 屠毅敏

Why Not?• PhoneGap要做到和Native Activity的整合较困难

• 2010年的手机性能运行PhoneGap的界面流畅度不好

• 好的Web前端设计师太难招了

• 那个时候还没有PhoneGap

Thursday, August 2, 12

Page 18: 如何创建更加灵活的App | 大众点评 屠毅敏

首页

Activity Stack

活动推广(HTML)

列表商户详情

dianping://home

http://dianping.com/a1.html

dianping://list?ids=12,32,44

dianping://info?id=12

Thursday, August 2, 12

Page 19: 如何创建更加灵活的App | 大众点评 屠毅敏

URL Scheme• dianping://shopinfo?id=123456

<activity android:name="com.dianping.find.ui.activity.ShopInfoActivity" android:configChanges="orientation|keyboardHidden" android:label="商户信息" android:screenOrientation="nosensor" > <intent-filter> <action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.DEFAULT" />

<data android:host="shopinfo" android:scheme="dianping" /> </intent-filter> </activity>

Thursday, August 2, 12

Page 20: 如何创建更加灵活的App | 大众点评 屠毅敏

URL Scheme• HTML

• Intent

<a href="dianping://shopinfo?id=123456">查看商户详情</a>

Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("dianping://shopinfo?id=123456")); startActivity(i);

Thursday, August 2, 12

Page 21: 如何创建更加灵活的App | 大众点评 屠毅敏

Simple is best• 概念简单,就跟打开网站⼀一样,很容易跟别人解释清楚。

• 实现简单,几行代码就能实现。

• 跨平台,iOS和Android都可以采用。

• 微信、支付宝等程序间的交互。

Thursday, August 2, 12

Page 22: 如何创建更加灵活的App | 大众点评 屠毅敏

团购• 需求变数多

• 主要业务由网站开发团队负责,移动只提供框架

• 用HTML开发,需要同时支持iOS和Android

• HTML能够向Java代码获取信息,如用户账户等信息

• 能够指定在不同的Activity中打开不同的页面

Thursday, August 2, 12

Page 23: 如何创建更加灵活的App | 大众点评 屠毅敏

数据传递• Android Javascript Bridge

• WebView URL Override (Android & iOS)

public boolean shouldOverrideUrlLoading(WebView view, String url)

public void addJavascriptInterface(Object object, String name)

http://myapp.com/checkout?user=!&token=!

http://myapp.com/checkout?user=123&token=e3a12f54c123

Thursday, August 2, 12

Page 24: 如何创建更加灵活的App | 大众点评 屠毅敏

Activity Stack管理• URL Override

• 支持压入堆栈和抛出堆栈

• 。。。

Thursday, August 2, 12

Page 25: 如何创建更加灵活的App | 大众点评 屠毅敏

出现问题• 不同Android版本的WebView有不⼀一致的行为

• 列表页面高度逐渐增大,UI相应速度缓慢直至完全不响应

• jQuery或MooTools当时没有为移动设备做优化,类库太重,耗费资源严重。

• 在不同的Activity和WebView中打开网页导致严重依赖Cookie,管理成本高。

Thursday, August 2, 12

Page 26: 如何创建更加灵活的App | 大众点评 屠毅敏

最终• 用户普遍不认可HTML的开发方式,各种抱怨

• 浏览率低,订单转化率低,支付成功率低,各种低

• 损失的都是钱那

Thursday, August 2, 12

Page 27: 如何创建更加灵活的App | 大众点评 屠毅敏

惨痛的教训• 用户体验通常是Native比HTML好

• Web开发和移动开发还是有显著区别的,沟通成本很高

• 如果HTML只是临时方案,不要做过多的框架设计,但是⼀一定要考虑今后迁移到Native的可行性

• 考虑成本和收益

Thursday, August 2, 12

Page 28: 如何创建更加灵活的App | 大众点评 屠毅敏

脚本语言Lua & Python

Thursday, August 2, 12

Page 29: 如何创建更加灵活的App | 大众点评 屠毅敏

脚本语言的问题• dalvik下无法创建新的类

• Java是静态类型,脚本语言调用需要指定类型,代码非常冗余

• 使用C/C++实现的Lua和Python解释器面临的问题

• 无法直接在Lua/Python中引用Java对象(内存地址变化)

• 需要经过JNI来中转所有调用,中间层实现成本较高

• 使用Java实现的Lua和Python解释器效率较低,且可靠性未知

Java Lua/PythonJNI

Thursday, August 2, 12

Page 30: 如何创建更加灵活的App | 大众点评 屠毅敏

动态加载Dalvik Executable File

& Resources

Thursday, August 2, 12

Page 31: 如何创建更加灵活的App | 大众点评 屠毅敏

DexClassLoader

DexClassLoader dcl = new DexClassLoader( "/sdcard/dex.apk", "/sdcard/dexout/", null, super.getClassLoader());Class c = dcl.loadClass("com.package.MyClass");Object myObj = c.newInstance();

Thursday, August 2, 12

Page 32: 如何创建更加灵活的App | 大众点评 屠毅敏

定位项目需求• 初步证明android.location.LocationManager存在优化空间

• 进⼀一步的优化需要通过线上数据及反馈进行迭代

• 迭代周期为1~2周⼀一次

• 每次迭代都需要在线上环境做A/B Test以验证有效

Thursday, August 2, 12

Page 33: 如何创建更加灵活的App | 大众点评 屠毅敏

定位服务

dianping.apk

interface LocationService

class DexLocationServiceWrapperimplements LocationService

dynamic.apk

class LocationServiceImplimplements LocationService

{ private LocationService mService;}

Thursday, August 2, 12

Page 34: 如何创建更加灵活的App | 大众点评 屠毅敏

上线后• 完全摆脱了App上线周期和版本的限制

• 新老版本的App都运行着最新版本的定位服务

• 不同App运行着最新版本的定位服务,不需要再merge了

• 团队可以独立运作

• 偶尔还可以帮忙解个线上Bug。。

Thursday, August 2, 12

Page 35: 如何创建更加灵活的App | 大众点评 屠毅敏

下⼀一步• 所有代码都动态加载

• 如何动态加载⼀一个Activity?

Thursday, August 2, 12

Page 36: 如何创建更加灵活的App | 大众点评 屠毅敏

动态加载Activity

Application

mBase : ContextImpl

mPackageInfo : LoadedApk

mClassLoader : PathClassLoader

Thursday, August 2, 12

Page 37: 如何创建更加灵活的App | 大众点评 屠毅敏

DEMOActivity Override

Thursday, August 2, 12

Page 38: 如何创建更加灵活的App | 大众点评 屠毅敏

缺陷• 用了很多Hacking,不能保证未来系统的兼容性

• Activity必须要在AndroidManifest.xml中注册,所以无法通过动态加载的方法新增Activity

• Resources资源文件没有解决

Thursday, August 2, 12

Page 39: 如何创建更加灵活的App | 大众点评 屠毅敏

重新思考• Activity继承自ContextWrapper,每个Activity都有能力改变自己的上下文环境。

• Android 3.0 引入了救星Fragment。Fragment就是小⼀一号的Activity,但也能够满足需求了。

• Activity作为Fragment运行的容器,提供ClassLoader和Resources相关的环境。

Thursday, August 2, 12

Page 40: 如何创建更加灵活的App | 大众点评 屠毅敏

代码

Activity.getClassLoader()

资源

Activity.getAssets()

Activity.getResources()

Activity.getTheme()

Thursday, August 2, 12

Page 41: 如何创建更加灵活的App | 大众点评 屠毅敏

DEMOFragment Loader

Thursday, August 2, 12

Page 42: 如何创建更加灵活的App | 大众点评 屠毅敏

Resources重复问题• Android的资源文件通过R.java来索引

• 手动分配不同的id,在不同层级的apk中通过res/values/public.xml来分配

• 通过重载AssetManager中的隐藏函数来区分不同区段的id,并返回对应apk包中的资源

• (需要特殊的编译方式)

Thursday, August 2, 12

Page 43: 如何创建更加灵活的App | 大众点评 屠毅敏

数据类型• 采用Java类封装数据的方式过于繁重

• 建议采用松散数据类型在各个模块之间进行交互(类似JSONObject)

Thursday, August 2, 12

Page 44: 如何创建更加灵活的App | 大众点评 屠毅敏

其他问题• 模块粒度问题

• Intent如何表示

• 损失的Activity属性如何弥补

Thursday, August 2, 12

Page 45: 如何创建更加灵活的App | 大众点评 屠毅敏

优点• 采用插件的模式,功能模块独立开发,独立上线

• 开发彻底解耦

• 解决了编译时间过长的问题

• 主程序apk包大小可以减小

Thursday, August 2, 12

Page 46: 如何创建更加灵活的App | 大众点评 屠毅敏

HTML + DexJavascript Bridge

Thursday, August 2, 12

Page 47: 如何创建更加灵活的App | 大众点评 屠毅敏

Thanks

Thursday, August 2, 12