做另一面的自己


  • 首页

  • 分类

  • 关于

  • 归档

  • 搜索
close
做另一面的自己

混得好的年轻人都有这5种特质

发表于 2017-01-04 |

Stay hungry,Stay young

道理我懂,具体怎么才能做到呢

张一鸣算是 80 后中绝对的佼佼者。
1983年出生的张一鸣 ,在2005年从南开大学毕业后,至今参与创办了 5 家公司,2013年,他先后入选《福布斯》“中国30位30岁以下的创业者”和《财富》“中国40位40岁以下的商业精英”,是目前国内互联网行业最受关注的青年领袖之一。
2016年7月26日,艾瑞发布2016中国独角兽企业估值榜单,今日头条以92.3亿美金的估值位列第 6。
张一鸣的成功中,他对人才的重视功不可没。
张一鸣作为面试官,过去10年里,面试过小2000个年轻人,这些年轻人最初水平都差不多,但后来的发展差别非常大。
近日,张一鸣在“2016今日头条Bootcamp”上对公司研发&产品部门应届毕业生发表了题为《Stay hungry, Stay young》的演讲,其中他分享了:
毕业多年后大家是怎么拉开差距的?
为何年轻人容易在毕业后很快就遇到了成长的天花板?
我是如何在毕业第2年就成了管理四五十人团队的主管?
这10年我遇到的优秀年轻人都有哪些特质?

张一鸣演讲整理:

一、 为何毕业多年后,原本水平差不多的同学都拉开了差距?

大家好!各位都非常年轻,我今天来的时候挺有压力。因为我毕业快11年了,看到你们,真是觉得“长江后浪推前浪”。我去年参加了武汉的校招,感觉新一代年轻人的素质确实都非常好。我昨天就在想,今天应该跟大家分享什么。想了想,先把题目拟出来,把乔布斯的“Stay hungry, Stay foolish”,改成“Stay hungry, Stay young”。

我想跟大家分享一下我自己毕业后的工作经历和体会。另外,我作为面试官,过去10年里,可能面试过小2000个年轻人。有的和我在一家公司,有的去了别家公司,他们发展差别其实非常大。从算法层面上讲,我们把这叫做“正例”和“负例”。我想分享一下:为什么“正例”和“负例”发展差别这么大?

什么是“Stay hungry, Stay young”?“Stay hungry”,大家都知道,就是好奇心、求知若渴、上进心。但为什么要说“Stay young”?

我觉得年轻人有很多优点:做事不设条条框框,没有太多自我要维护,经常能打破常规,非常努力、不妥协、不圆滑世故。

10年过去了,有的年轻人,依然保持着这些很好的特质。我觉得这就算“Stay young”。

“Stay young”的人基本没有到天花板,一直保持着自我的成长。相反,很多人毕业后提高了技能,但到一个天花板后,就不再成长了。

二、 我先分享我的个人经历:我是如何在毕业第2年就成了管理四五十人团队的主管?

2005年,我从南开大学毕业,加入了一家公司叫酷讯。我是最早期加入的员工之一,一开始只是一个普通工程师,但在工作第 2 年,我在公司管了四五十个人的团队,负责所有后端技术,同时也负责很多产品相关的工作。

有人问我:为什么你在第一份工作就成长很快?是不是你在那个公司表现特别突出?

其实不是。当时公司招聘标准也很高。跟我同期入职的,我记得就有两个清华计算机系的博士。

那我是不是技术最好?是不是最有经验?我发现都不是。后来我想了想,当时自己有哪些特质。

1、我工作时,不分哪些是我该做的、哪些不是我该做的。我做完自己的工作后,对于大部分同事的问题,只要我能帮助解决,我都去做。当时,Code Base中大部分代码我都看过了。新人入职时,只要我有时间,我都给他讲解一遍。通过讲解,我自己也能得到成长。

还有一个特点,工作前两年,我基本上每天都是十二点一点回家,回家以后也编程到挺晚。确实是因为有兴趣,而不是公司有要求。所以我很快从负责一个抽取爬虫的模块,到负责整个后端系统,开始带一个小组,后来带一个小部门,再后来带一个大部门。

2、我做事从不设边界。当时我负责技术,但遇到产品上有问题,也会积极地参与讨论、想产品的方案。很多人说这个不是我该做的事情。但我想说:你的责任心,你希望把事情做好的动力,会驱动你做更多事情,让你得到很大的锻炼。

我当时是工程师,但参与产品的经历,对我后来转型做产品有很大帮助。我参与商业的部分,对我现在的工作也有很大帮助。记得在07年底,我跟公司的销售总监一起去见客户。这段经历让我知道:怎样的销售才是好的销售。当我组建头条招人时,这些可供参考的案例,让我在这个领域不会一无所知。

以上就是我刚毕业时的特点。

三、 10年观察,我遇到的优秀的年轻人都有这5大特质!

后来,我陆续加入到各种创业团队。在这个过程中,我跟很多毕业生共处过,现在还和他们很多人保持联系。跟大家分享一下,我看到的一些好和不好的情况。总结一下,这些优秀年轻人有哪些特质呢?

第一,有好奇心,能够主动学习新事物、新知识和新技能。

今天不太谦虚,我把自己当做正例,然后再说一个负例。我有个前同事,理论基础挺好,但每次都是把自己的工作做完就下班了。他在这家公司呆了一年多,但对网上的新技术、新工具都不去了解。所以他非常依赖别人。当他想要实现一个功能,他就需要有人帮他做后半部分,因为他自己只能做前半部分——如果是有好奇心的人,前端、后端、算法都去掌握、至少有所了解的话,那么很多调试分析,自己一个人就可以做。

第二,对不确定性保持乐观。

比方说头条最开始时,我跟大家讲:我们要做1亿的日启动次数。(当然,现在不止1亿了,我们现在的日启动次数已经差不多5亿。)很多人觉得,你这家小公司怎么可能做得到呢?大公司才能做得好。所以他就不敢努力去尝试。只有乐观的人会相信,会愿意去尝试。其实我加入酷讯时也是这样。那家公司当时想做下一代搜索引擎(最后也没有做成,只做了旅游的垂直搜索)。我不知道其他人怎么想的,我自己觉得很兴奋。我确实没有把握,也不知道怎么做,但当时就去学,就去看所有这些相关东西。我觉得最后也许不一定做成,或者没有完全做到,但这个过程也会很有帮助——只要你对事情的不确定性保持乐观,你会更愿意去尝试。

第三,不甘于平庸。我们在座各位,在同学中已经非常优秀了。

但我想说,其实走向社会后,应该再设定更高的标准。我见到很多大学期间的同学、一起共事的同事中,有很多非常不错的人才,技术、成绩都比我好。但10年过去,很多人没有达到我的预期:我觉得他应该能做得很好,但他却没有做到。
很多人毕业后,目标设定就不高了。我回顾了一下,发现有同事加入银行IT部门:有的是毕业后就加入,有的是工作一段时间后加入。为什么我把这个跟“不甘于平庸”挂在一起呢?因为他们很多人加入,是为了快点解决北京户口,或者当时有些机构有分房补助,可以购买经济适用房。后来我就在想一个问题,如果自己不甘于平庸,希望做得非常好的话,其实不会为这些东西担心:是否有北京户口,是否能买上一套经济适用房?如果一个人一毕业,就把目标定在这儿:在北京市五环内买一个小两居、小三居,把精力都花在这上面,那么工作就会受到很大影响。他的行为会发生变化,不愿意冒风险。

比如我见到以前的朋友,他业余做一些兼职,获取一些收入。那些兼职其实没有什么技术含量,而且对本职工作有影响,既影响他的职业发展,也影响他的精神状态。我问他为什么,他说,哎,快点出钱付个首付。我觉得他看起来是赚了,其实是亏的。

不甘于平庸很重要。我说不平庸,并不是专门指薪酬要很高或者技术很好,而是你对自己的标准一定要高。也许你前两年变化得慢,但10年后再看,肯定会非常不一样。

第四,不傲娇,要能延迟满足感。

我在这里举个反例:两个我印象比较深刻的年轻人,素质、技术都蛮不错,也都挺有特点。我当时是他们的主管,发现他们在工作中deliver的情况始终不好。他们觉得其他同事比他们做得差,其实不是:他们确实可以算作在当时招的同事里面TOP 20%,但误以为自己是TOP 1%。所以很多基础一点的工作,比如要做一个调试工具,他就不愿意做,或者需要跟同事配合的工作,他就配合得不好。

本来都是资质非常好的人才,人非常聪明、动手能力也强,但没有控制好自己的傲娇情绪。我觉得这和“不甘于平庸”不矛盾。“不甘于平庸”是你目标要设得很高,“不傲娇”是你对现状要踏实。

这2000个样本当中,我见到很多我原来觉得很好的,其实没有我想象中的发展好,我原来觉得不好的,其实超出我的预期。这里我也举个例子:

当时我们有个做产品的同事,也是应届生招进来,当时大家都觉得他不算特别聪明,就让他做一些比较辅助的工作,统计一下数据啊做一下用户反弹啊之类。但现在,他已经是一个十亿美金公司的副总裁。

后来我想想,他的特点就是肯去做,负责任,从来不推诿,只要他有机会承担的事情,他总尽可能地做好。每次也不算做得特别好,但我们总是给他反馈。他去了那家公司后,从一个用户量不到10万的边缘频道负责起来,把这个频道越做越好。由于这是一个边缘频道,没有配备完整的团队,所以他一个人承担了很多职责,也得到了很多锻炼。

第五,对重要的事情有判断力。

选什么专业、选什么公司、选什么职业、选什么发展路径,自己要有判断力,不要被短期选择而左右。上面一些例子,也都涵盖了这一点。比如当时很多人愿意去外企,不愿意去新兴的公司。06、07年,很多师弟、师妹问我职业选择,我都建议他们去百度,不要去IBM、微软。但实际上,很多人都是出于短期考虑:外企可能名气大、薪酬高一点。

虽然这个道理,大家都听过很多遍。刚毕业时薪酬差三五千块,真的可以忽略不计。短期薪酬差别并不重要。但实际上,能摆脱这个、能有判断力的人,也不是特别多。

这些就是我想跟大家分享的。谢谢!

==张一鸣 10 年感悟:==

一、 不断为公司招揽牛逼人才是创始人最重要的任务之一,他曾为了挖角工程师亲自勾搭两个月。
张一鸣在拿到创业邦年度创业人物称号发表演讲时说:除了吃饭、睡觉,就是招人。
作为一个创业公司的创始人,我花最多的时间一直在招聘上。我夜归很晚的时候大部分原因是去见候选人,有时候甚至从下午聊到凌晨。我相信并不是每一个CEO都是好HR,但是我自己在努力做一个认真诚恳的HR。
今日头条早期没有什么资源,张一鸣曾为了挖一位微软前资深工程师,像调查记者一样了解对方。
张一鸣不断打电话,打听他的信息。初步了解情况以后,张一鸣想尽办法加上对方的微博微信。他会偶尔点赞、评论互动,提升自己好感度。“前期信息收集越仔细,他越会觉得你对他的工作、公司环境乃至他那个部门的特点都如此了解,这样你和他的对话就能产生共鸣,他对你的信任度会提高。”

好几天的调查、两个月的沟通,交成朋友再开始介绍自己的公司。最终,他在一家咖啡馆成功邀请这位工程师加盟今日头条。

“每次要做之前我都觉得挺累的,但是招到人了又会很兴奋。”张一鸣说。

二、 舍得为人才和发展花钱,把公司搬到帝都中心、发住房补贴,让员工远离郊区。
张一鸣说:年轻人工作生活应该住在市中心,哪怕房子小一点,在市区有更多的活动和交流,下班之后也不需要浪费大好时光和宝贵经理挤地铁。年纪轻轻不要着急在郊区,尤其房山、沙河、天通苑之类的远郊定居,买了房网其实也建议搬到市区来。

张一鸣做了两点:一是把公司搬倒了帝都中心知春路;二是给每个员工每月1500元的住房补贴,今日头条每年在这方面的补助高达3000万元。
“就近居住和加不加班没关系,节省的时间用于健身读书看电影也很好。”张一鸣称。

三、 自省是创业路上的导师:适度、可管理的沮丧让我保持清醒。
我很早以前就意识到,公司规模扩大了,CEO 角色也很容易就会陷入到一个不利的局面里——公司周围很少有人能够给自己提有效的要求和批评。这时候创业者的自知、自制力和反省能力就变得尤为重要,能从轻微的意见和异常中发现自己的问题并修正,就是一种慎独。
这种氛围下,创业者很容易会滋生出不自觉的满足感,这种满足感会像病毒一样影响、侵入我们的深入思考能力,这个时候引入适度、可管理的沮丧,反而能让人保持清醒。

四、 创业过程中做决策要三思而后行,三思的迭代要快。
这是张一鸣创业路上体会最深的感悟之一:
创业路上会有很多不确定性,让我们决策时纠结犹豫,说好听一点就是“三思”。
三思没有问题,是人之常情,作为一个不那么激进的创业者,我也常有这种状态。但用产品经理的话来讲,三思后的迭代速度必须快。这也是一种创业必须要有的心态。
随着公司规模的扩大,我越来越倾向于把公司当做一个产品来看待:产品迭代要快,公司迭代也要快,毕竟这个社会给你的时间窗口其实是有限的。把公司当做产品,提前设想好各个环节的迭代度,努力去做后你会发现,整个公司的迭代速度也会大大提升。

做另一面的自己

重写Button实现图片drawableTop跟文字一起居中

发表于 2017-01-02 |

开发中遇到不想在Button外套一层布局,加深布局层次,原生drawableTop属性不能使其居中,需要重写Button的onDraw方法。
直接上代码:

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
public class DrawableVerticalButton extends Button {
public DrawableVerticalButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
canvas = getTopCanvas(canvas);
super.onDraw(canvas);
}
private Canvas getTopCanvas(Canvas canvas) {
Drawable[] drawables = getCompoundDrawables();
if (drawables == null) {
return canvas;
}
Drawable drawable = drawables[1];// 上面的drawable
if(drawable == null){
drawable = drawables[3];// 下面的drawable
}
float textSize = getPaint().getTextSize();
int drawHeight = drawable.getIntrinsicHeight();
int drawPadding = getCompoundDrawablePadding();
float contentHeight = textSize + drawHeight + drawPadding;
int topPadding = (int) (getHeight() - contentHeight);
setPadding(0, topPadding , 0, 0);
float dy = (contentHeight - getHeight())/2;
canvas.translate(0, dy);
Log.i("DrawableTopButton", "setPadding(0,"+topPadding+",0,0");
Log.i("DrawableTopButton", "translate(0,"+dy+")");
return canvas;
}
}

做另一面的自己

Mac 上 android 反编译apk完整流程

发表于 2016-12-14 |

直接进入正题,如果要在mac下反编绎apk跟在window下还是有点区别的,但是大体原理是一样的。首先下载三个工具:
Apktool
dex2jar
jd-gui

分别介绍下这几个应用在mac下的安装
Apktool,官方有明确的文档说明:
Download Mac wrapper script (Right click, Save Link As apktool)
Download apktool-2
Rename downloaded jar to apktool.jar
Move both files (apktool.jar & apktool) to /usr/local/bin (root needed)
Make sure both files are executable (chmod a+x filename)
Try running apktool via cli

这样你就配置了bash权限,可以执行apktool命令,回车可以查看其命令集

1
apktool d file.apk

这样就能生成一个file的文件夹,就生成了资源文件,smile文件,如果修改了生成apk文件

1
apktool b file

上面的build和dist就是回编译apk过程中生成的东西,编译出来的apk在dist目录下,打开build/apk文件夹会发现少了original文件夹下的META-INF文件夹,也就意味着dist里的apk文件是没有签名的。

dex2jar,我们把.apk重命名成.zip,解压,取出classess.dex。我们要用dex2jar将apk转成jar文件,实质是将apk里的classes.dex转成jar。然后进到dex2jar这个文件夹下运行:

1
sh d2j-dex2jar.sh classes.dex

可能会出现

1
d2j-dex2jar.sh: line 36: ./d2j_invoke.sh: Permission denied

这时候就将dex2jar里的d2j_invoke.sh/d2j-dex2jar.sh增加可执行权限

1
2
chmod +x ./d2j_invoke.sh
sh d2j-dex2jar.sh classes.dex

这样就生成了classes-dex2jar.jar,

打开JD-GUI,将hongbao-dex2jar.jar拖进去就看到源码了。 通过看Java源码对比smali文件,修改后回编译就ok了!
如何手动给apk增加签名?

回编译后的apk是安装不成功的,总是提示
Failure [INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION] 实质是没签名。

1,生成签名:

1
keytool -genkey -keystore filename.keystore -keyalg RSA -validity 10000 -alias aliasname

注意:上面-keystore后面跟的是签名文件的名字,而-alias是别名,一般情况下-keystore后面跟-alias是一样的,但其实两者没有关系,这也是我故意搞成不一样的原因。
2,为apk增加签名:

1
jarsigner -digestalg SHA1 -sigalg MD5withRSA -tsa -verbose -keystore filename.keystore -signedjar filename-signed.apk filename.apk aliasname

注意:
1,最后的”aliasname”就是-alias后面带的,必须保持一致。
2,如果不带-digestalg SHA1 -sigalg MD5withRSA签名后的apk安装也是不成功的,说INSTALL_PARSE_FAILED_NO_CERTIFICATES的错误,如果不带-tsa会报一个时间方面的警告。

做另一面的自己

手机浏览器广告拦截实现及自动化测试

发表于 2016-12-14 |

1 广告拦截产品需求

1.1 在页面加载过程中进行拦截(包括重新载入),做到不会让广告闪现后才被拦截

1.2 目前我们的拦截规则直接使用猎豹的规则list进行修改,通过JS注入和拦截请求的方式进行广告拦截,规则要求服务端可控,浏览器本地会存一份"广告拦截规则"。

1.3 广告拦截更新机制

规则文件名:libadblockrules(联盟广告通用);libadblockextrules(根据域名修改css样式); libadwhitedomainrules(白名单规则,运营需求有些网页不拦截广告)

规则更新时机:浏览器启动后,检测更新"广告拦截规则";

规则更新逻辑:规则在浏览器本地有个时间戳,向服务post本地规则的时间戳进行比对,当服务器规则的时间戳新于本地的则下载新的规则进行更新,反之则不进行更新;

新规则应用时机(何时在本地起效):更新完成规则后,下次启动浏览器时应用。

拦截提示:设置界面有提示模块

2 广告拦截实现

2.1 广告拦截中文社区

2.2 ABP官方: https://adblockplus.org/en/source 这里可以下载ABP官方源码(开源组织一直在维护)

2.3 规则文件:
http://abpchina.org/forum/forum.php?mod=viewthread&tid=29667&extra=page%3D1

ABP for android 项目已开源,github地址: https://github.com/adblockplus/adblockplusandroid

2.4 ABP官方实现:

通过设置代理,拦载请求,广告请求地址与拦截规则进行匹配,匹配到规则成功就不发送请求,自然就不会加载到请求到客户端,这种拦截通用于整个手机的

2.5 手机浏览器广告拦截具体实现:

广告规则:应用首次启动,拷贝包里的默认规则到应用的data/data/package目录下,非首次启动直接读取本地规则,将其保存到缓存;同时向服务器请求拦截规则,成功请求下来保存文件,时间戳分别保存,请求在JsHandler类请求参数如下:
Requst params:

1
appname=androidbrowser;exrules_version=1473247972;channel=UMENG_CHANNEL_VALUE;whitelist_version=1473058756;abprules_version=

拦截时机:核心处理类在AdBlockApi

拦截有两种方式:

1).WebView加载的时候注入javascript修改网页css样式

在WebView的回调

1
onProgressChange()mAdblock.onProgressChanged(view);

和

1
onPageFinishedmAdblock.onPageFinished(view)

,两个回调中注入广告的js,主要是拦截一些banner广告和通用规则拦截导致的网页内部css样式错乱进行调整;其由通用js和规则文件js中拼接而成,具体可看代码实现。

2). shouldInterceptRequest拦载请求

Webview在加载的时候资源的每个请求以及js请求都会进行这个回调,客户端可以在这个回调中做拦截处理

1
mAdblock.shouldInterceptRequest(view, url, mCurrentState.mUrl)

原理是将请求中的8位一组分割,然后与缓存下来的通用规则列表通过正则来匹配,匹配成功则进行拦截,否则不拦截。匹配的效率跟资源url的长度也和正则匹配效率有关系,这也是广告拦截会影响网页加载速度的一个潜在风险。规则解析满足大部分官方提供的标准以便适用官方规则:

https://adblockplus.org/zh_CN/filters,自己添加通用规则也按照此文档。

这两种方式同时时行保证了页面在被拦载导致页面错乱的情况下可以进行调整;服务器端控制规则可以保证运营需求,合作客户不拦,页面拦截异常时加白名单控制等情况(白名单针对域名)。

3 广告拦截维护

两份默认规则文件都放在asset/js目录下,libadblockextrules根据官方的书写规则即可,libadblockextrules的写法与官方有差异。这里举个例子(以逗号分隔):一条规则只单行显示

1
.weibo.cn,1,1,ad_banner

第一位:要拦截的网站的域名

第二位:要拦截元素的tag,0表示class,1表示id

第三位:要执行拦截操作的事件action,1表示删除节点;3表示删除上一个节点;4表示下一个节点;5表示自定义事件,用于修改复杂css样式,js自己定义

第四位:元素节点的id名称或者class名称,或者当action为5时自己定义的js字符串

日常维护这两个规则文件即可,然后提交给测试服务器。

4 广告拦截自动化

为了提升运营批量定时检查广告的效率和准确度,很需要为广告拦截写一个自动化去发现广告,截图,方便运营查看。

  1. 集成开源android测试框架Robotium:

    1
    androidTestCompile 'com.jayway.android.robotium:robotium-solo:5.5.4'
  2. 在app androidTest包下声明一个类继承ActivityInstrumentationTestCase2

  3. 重写setup() tearDown等方法,通过Solo类来获取要测试的activity的view控件,模拟用户操作,操作其子控件,Robotium提供了官方api(下载即可),书写脚本,然后在手机上运行。

做另一面的自己

QZone视频下载如何做到多快好省

发表于 2016-12-12 |

Qzone的日均视频播放量已经突破了10亿,其中Android端的播放量在总播放量中的占比超过70%,相比年初,播放量的增长了超过10倍。视频下载是整个视频播放的基础,如果下载侧出问题,则会造成整个视频播放的失败,这就对我们的视频下载提出了非常高的要求。

基于此,我们将视频下载总结为”多快好省”四个方面,以下载成功率、首次缓冲时长和缓冲概率为主要的技术指标对视频下载进行优化。具体参数的优化结果见下表1,经过长时间的打磨,我们的视频下载模块的下载成功率已经达到了99.9%,视频的首次缓冲时长1.2秒,二次缓冲概率低于1%,取得了良好的效果。下面我将从”多快好省”这四个方面,对我们主要的优化工作进行论述。

表1: 下载相关技术指标优化前后对比
技术指标 | 下载成功率 | 首次缓冲时长 | 缓冲概率
—|——|——|——|———|—
优化前 | 97.1% | 2s | 15%
优化后 | 99.9% | 1.2s | 0.9%

  1. 多

在10亿这个量级下,除了保证下载的成功率和下载速度这些主要参数之外,对于整个下载流程的监控、处理异常情况显得格外重要。为了提升视频的下载成功率、稳定性,监控整个下载流程,提升用户体验,我们采用本地代理的方式进行视频下载。
在Android手机上播放在线视频,最简单的方式就是实例化一个MediaPlayer, 将视频的URL通过setDataSource()设置给播放器,之后调用prepareAsync()和start()遍可以开始播放视频。这种方式非常简单,但其中最大的问题就是整个过程中的数据流完全由MediaPlayer控制,我们无法控制下载和播放的过程,也就导致我们没有办法提高成功率,优化用户体验。因此,Android侧的视频下载一般采用本地代理的方案实现。本地代理的方案即是指在播放视频的时候,将视频的URL转换为本地URL(127.0.0.1开头),在播放器通过本地URL请求视频数据时,本地代理截获这次请求,在经过本地的处理逻辑后,向服务器或者本地缓存请求数据。本地代理在获得视频数据之后,将数据转发给播放器。

相比起直接由播放器请求数据,本地代理的优势是数据流由本地代理控制,我们可以在本地代理中加入缓存、预加载、防盗链等业务逻辑,这可以极大的提升视频下载的成功率,减少视频的缓冲时间,从而提升用户体验。

传统的本地代理方案确实解决了播放器直连带来的问题,但同样也会产生一些问题,视频下载和播放的业务逻辑复杂,过多的逻辑和下载本身耦合,给开发的过程带来极大的不便,并且这样也不容易接入第三方的下载器和对下载过程进行监控。因此,在经历了两个版本的迭代之后,我们将整个下载过程进行重构。这次重构使得下载各模块的职责明确,便于开发、维护以及接入第三方的下载,也为我们后续的优化打下基础,重构之后的方案会在之后单独成文介绍。

  1. 快

国外SmartBear的研究表明,57%的用户在3秒没有加载完网页时就会放弃。在视频播放上,加快视频的加载速度,减少播放过程中的卡顿,对提高用户观看视频的体验有极大的帮助。经过我们长期的优化,现在Qzone视频播放的接近秒开,缓冲概率下降到不到1%,这极大的提升了用户体验,也从侧面提升了我们的视频播放量。

在这数据提升的背后,我们主要做了几个方面的工作:

2.1:防盗链预拉取

盗链播放在国内非常普遍,而盗链会使平台资源流失,增加带宽成本,不利于平台的长期发展,国内大部分视频服务提供商都在一定程度上做了防盗链。防盗链的主要过程是后台下发的视频URL,在正式播放之前,需要通过URL中的部分参数,加上一些本地参数,向后台拉取真正播放的URL, 这些真正播放的URL都带有时效性,这种方式可以从一定程度上避免盗链行为。但通过防盗链接口拉取真实的播放URL需要时间,这也在一定程度上延长了用户感知的视频加载时间。针对这种情况,我们对防盗链的模块进行了改造,引入预拉取机制,将防盗链的拉取与播放解耦,对用户的播放行为进行预判,在用户播放视频的过程中提前拉取并缓存之后视频的URL, 从而减少了因为拉取防盗链URL造成的视频缓冲时间。

2.2:数据预加载:

从MediapPlayer的源码可以发现(AwesomePlayer.cpp), MediaPlayer需要下载5秒的数据才会开始播放视频,按照现在的外网平局下载速度计算,该过程的耗时在接近1秒,因此对于数据进行预加载是减少视频首次缓冲非常重要的方法。但视频数据的预加载不能跟当前播放的视频抢下载带宽,因此我们选择以当前播放视频的播放进度和数据缓存量为维度,当两者同时达到一个阀值时开始下载下一个视频的数据。在实践的过程中,我们还发现,因为一些编码格式的原因,MediaPlayer在播放视频之前可能会请求一部分尾部数据,因此,视频预加载还会加载一部分尾部数据,最大限度的保证预加载的效果。

2.3:缓存改造

MediaPlayer加载本地视频的效率远高于在线下载,因此,缓存的命中率会直接影响到视频缓冲的速度。最初的缓存方案是针对单个视频按照顺序缓存,这样实现简单,但存在的问题就是无法对于播放空洞(非顺序播放场景,例如拖动、续播等)进行缓存,这降低了视频的缓存率和缓存命中率,增加了带宽成本和视频的缓冲时长。之后我们针对缓存模块进行了改造,将顺序缓存改为分片缓存,即将单个视频的缓存按照一定大小进行分片,在遇到数据空洞或者缓存数据量达我们设置的单片缓存上限时,开启下一个分片缓存,确保可以缓存所有的下载数据。这样改造之后极大的提升了缓存命中率,降低了首次缓冲时间和二次缓冲的概率。

2.4:性能优化

梳理下载和播放过程中整体的流程,通过工具排查流程中长耗时的点和优化过程中的逻辑,减少不必要的耗时和操作,并将部分耗时逻辑移入子线程;优化时序,将例如图片加载、缓存IO等重逻辑执行的时机后移,以及对视频播放关联度不高的逻辑使用懒加载。这样可以降低对于视频播放,特别是视频缓冲过程中,CPU和IO的占用,使得系统能够调度更多的资源在解封装、解码、渲染等与播放、下载直接相关的操作上,进而减少这部分的耗时。

  1. 好

下载的成功率是保证视频观看体验的基础,目前Qzone的视频下载成功率已经提升至99.9%,跟主要命令字的成功率相当。国内的移动网络环境错综复杂,不仅要处理断网、慢速、抖动等网络本身的题,还要处理跨网、运营商劫持等国情问题。下载成功率的提升过程非常艰难,我们在其中主要做了以下的工作:
3.1: IP直出与竞速

通过IP直出减少了DNS劫持的可能性;对于下层代理的视频下载下发多组IP,通过竞速计算本地最佳IP,使用最佳IP进行直出下载;上层代理对于下层代理的整个下载过程进行监控,在监测到下载速度缓慢或者异常情况时(IP连接失败、数据读取超时等)立即切换下载IP,减少用户的视频加载时间。通过IP直出、竞速和切换,提高了下载的连接、数据读取成功率,减少了因DNS劫持导致下载失败的概率,同时提高了下载速度。

3.2:对于链接失效(403)进行处理

上文(2.1章节)中提到Qzone视频播放的链接均是经过防盗链处理,带有播放效期的链接,这就使得,在实际播放的场景中,很可能出现用户希望播放某视频时,跟随后台下发的视频链接已经过期失效的情况,如果不进行处理,则会极大降低下载的成功率。针对这种情况,我们根据视频的不同来源,对于每种情况进行异化处理,通过向后台重新拉取链接或者本地计算Key,解决了因连接中的Key失效导致视频无法播放的问题。

3.3:下载命令字拉取接入私有通道

Qzone很早就开始采用维纳斯(WNS, Wireless Network Service)私有通道方案进行网络数据传输,相比使用最多的Http方案,WNS通过长连接、IP直出、接入点优化、数据压缩等方法,提高了网络连接的成功率和稳定性。为了提高主要命令字的拉取成功率,我们对原有用Http拉取命令字的方式进行改造,使用WNS通道包裹原来的Http数据包,在后台通过WNS通道收到原有的请求后再将请求分发至对应的后台,具体的流程如下图4所示。这样通过低成本的改造(不需要修改原有协议,后台直接透传基本没有开发工作量),借由WNS提升了整个通道的传输质量,进而也提高了视频的下载成功率。

3.4:下载速率动态调节

在移动网络下,发生网络抖动和网络切换经常发生,但网络不稳定,对下载来说是非常致命的。为了应对网络抖动和网络切换,下层代理会监听当前的网络变化,监控当前的下载速度。下层代理在下载数据时,为了减少对于别的业务影响,不会占用全部的带宽,但当发生频繁的网络切换时,下载代理会主动突破速度的限制,尽可能快的在网络情况良好时下载数据,给之后的播放留下足够的数据Buffer,保证整体播放的流畅性。

  1. 省

互联网的视频服务提供商在国内盈利极其困难,除了近几年视频市场竞争越来越激烈,版权费居高不下之外,国内高昂的带宽成本也是重要原因。因此,如何在保证视频质量的前提下,尽可能减少下载流量,减少下载而产生的带宽成本,对于我们来说也是非常重要的工作。在这部分,我们主要的工作如下:

4.1: 流量控制

为了保证用户观看的流畅性,减少视频缓冲,视频数据下载的可播放量与当前观看的时间点之前会保持一定的Buffer,在整个播放过程中,通过动态调节下载速度,这个Buffer的大小基本保持不变,并且Buffer的大小可以动态调节。在流量高峰的时间段,我们会通过后台进行流量控制,减少Buffer,也就减少了高峰时段整体的下载量。带宽的计费标准一般按照高峰流量计费,减少了高峰时段的流量,也就降低了带宽成本。具体流控如下图,高峰时段视频缓冲M秒,非高峰时段缓冲N秒,N>M,两个参数均可由后台控制。

4.2: H265编码

H265是新一代视频编码标准,相比原有使用H264编码的视频,具有更高的压缩比,在画质近似的前提下,H265编码的视频文件体积只有H264的一半甚至更少,因此,播放H265编码的视频能极大减少带宽消耗。但H265现阶段主要存在的问题是终端编码耗时过长,后台编码过于消耗资源,以及在Android手机上,软解码(Android支持H265硬解码的机型较少,并且硬解码的兼容性问题相比软解更多)带来的耗电、发热以及兼容性问题。目前,Qzone通过自解码播放器,在经过大量的兼容性测试之后,已经在超过100款主流Android手机上实现了H265视频的软解播放。空间视频H265和H264编码的下载带宽对比以及之后的预期情况如下图6所示,可以明显看出,通过H265编码极大的降低了我们的视频下载带宽成本。

另外在2.3中论述对于视频缓存的分片改造,同时提升了下载数据缓存的使用率和命中率,也了减少了我们的视频下载带宽。

  1. 总结

经过长时间的优化,Qzone视频业务,包括下载成功率、播放成功率、缓冲概率、首次缓冲时长等在内的主要技术指标,均得到了大幅度的提升,达到了我们的预期,也为Qzone视频点播和直播业务的持续发力铺平了道路。但技术优化是一个长期的过程,目前Qzone的视频播放已经开始启用自解码播放器,逐步替换原生的MediaPlayer,之后我们还会通过播放器多实例,编解码,参数调节等方式进一步提升视频下载成功率,压缩视频缓冲时间,减少缓冲概率。也欢迎各位多使用Qzone体验我们在各个场景中的视频播放,如果对体验或者技术优化的建议和意见,欢迎交流。

做另一面的自己

代码Review流程与操作步骤

发表于 2016-12-11 |

fork 主仓库,创建自己的仓库

主仓库地址:http://172.16.0.245:2345/user/android_browser_codereview

拉取子仓库地址到本地

1
git clone ******(自己fork 之后的仓库地址)

跟踪主仓库

1
git remote add upstream git@172.16.0.245:user/android_browser_codereview.git

更新主仓库代码

1
git fetch upstream

合并主仓库分支到本地分支

1
git merge upstream/master

提交本地仓库代码到主仓库

1.登录gitlab ,选中个人仓库
2.发起 Merge Requests
3.选择 Source branch
4.选择 Target branch

参考资料1

使用 git rebase 避免無謂的 merge

git pull –rebase
加上 rebase 的意思是,會先 1.把本地 repo. 從上次 pull 之後的變更暫存起來 2. 回復到上次 pull 時的情況 3. 套用遠端的變更 4. 最後再套用剛暫存下來的本地變更

.git/config 中配置

1.user 信息配置
在 config 文件中添加如下信息,用户名和邮箱地址替换为自己的账号以及邮箱

1
2
3
[user]
name = user
email = user@2345.com

2.指定分支 使用 git pull –rebase

1
2
3
4
[branch "master"]
remote = origin
merge = refs/heads/master
rebase = true

3.所有分支添加 git pull –rebase

1
2
[branch]
autosetuprebase = always

12
stareme

stareme

敢问路在何方

16 日志
3 分类
GitHub Weibo
© 2021 stareme
由 Hexo 强力驱动
主题 - NexT.Pisces