五、走向本地化:未来的一千万个应用来自何方
移动应用现象是全球性。Apple和Google已成功推出数亿台iPhone和Android设备,在120多个国家建立开发者到消费者的直接发布渠道(即应用商店)。由于设备和应用发布的全球性,开发者可通过全球应用商店,从阿塞拜疆到赞比亚(英文排序的一头一尾的两国家)实现全球发布。在这精简推出市场的路径中存在众多挑战和机遇。
对某些知名度高的应用需求是全球性的,如Facebook,Google地图或愤怒的小鸟,很容易渗入本地市场。对另一些应用,如出租车预约,电影时间表或餐店预约应用,可很好地服务于美国消费者,但无法用于在欧洲或亚洲城市的本地商务环境或文化中。不同的语言、文化、商务环境、销售渠道,法律法规、品牌和本地消费者行为意味着很多应用在进入本地市场时需要进行适配,同时也意味着很多本地应用需求目前得不到满足。中国、巴西和俄罗斯是主要市场难以渗透的典型例子,对全球移动应用开发者有很大机遇。
应用经济中,我们认为最畅销应用的全球需求仍主导绝大部分地区的下载量。同时,本地应用的地区需求将会推动未来的一千万个应用的产生。
在本章,我们将深入分析跨地域应用需求情况,以及语言匹配情况,量化应用贸易平衡,并找出需求和供给中的机遇。
全球应用的供给和需求
目前,应用需求由美国推动,其次是中国。据Xyologic数据,在2012年4月,美国和中国分别有12亿和3.5亿次应用下载。为了量化各地区的应用需求,我们分析了参加2012年开发者经济调查的1500+开发者的答复。北美的应用需求最高,有41%的开发者表示北美是他们应用前3大下载地区(不考虑开发者所在籍)。欧洲为31%,接下是亚洲的25%。
地区需求的格局基本因素是各地区的智能手机渗透率和应用认知。如下图所示。
每个国家向前发展是沿着智能手机渗透率增长轨迹。随着人们越来越熟悉智能手机,以及使用手机进行越来越多日常任务,他们对越来越认知应用。当用户下载更多应用,更经常地使用应用,用户参与度会提高。因此各个国家应用需求将伴随三个因素而增长:提高智能手机渗透率水平,提高用户参与度,以及各国可寻址智能手机用户的市场总和(也就是各国总移动用户量,可寻址就是有个手机号)。
目前,就每用户下载量而言,引领应用市场的是美国和英国。接着是其他欧洲市场,如德国、荷兰,它们大部分出现在图的右上象限。该象限表示成熟去,市场具有最高的用户参与度以及智能手机渗透率。这些国家是应用经济的启航地,过去三年一直推动应用经济的发展。
左下角象限主要是金砖国家,用户参与度和智能手机惨透率低,但发展迅速。同时,市场可寻址用户规模(圆圈大小表示该国移动用户数量)比其他大部分国家要大得多。因此就整个国家的应用需求量,左下象限的国家在某些情况下会超过右上象限的国家。
左下是机遇象限:这些国家的应用需求的发展曲线处于开始轨迹,将最终到达成熟象限。在应用经济中,大部分新机遇将出现在这些国家市场走向成熟的路径上。机遇正比图中三大变量:可寻址市场(圆圈大小),智能手机渗透率(X轴)、和用户参与度水平(Y轴)。智能手机将在2013年达到十亿用户的里程碑,应用认知在全球范围内提高,由此,开发者在未来十年有巨大的机遇。
我们相信,当金砖国家市场进入成熟象限时,全世界应用经济规模将是非常巨大。接下来的一千万个应用将来自机遇象限和本地化应用需求,而非来自当前的领先市场。
应用语言和用户语言的不平衡
同时,这张图还清晰提看到智能手机渗透率本身并不能很好地预测应用需求:有相近渗透率的国家(如英国和西班牙)可能在每个用户应用需求量上有非常大的差异。这种不平衡背后有很多原因。
人口统计:文化和收入的差异导致人们在移动应用现象中表现不同。发达国家的人们对新技术响应更快,并有更多的可支配收入和时间来购买应用和参与应用。
基础设施:费用可承担的无线带宽是影响用户下载应用倾向的一个重要因素。尽管可负担的无线带宽在欧洲和美国普及,但在太多国家仍未实现。
应用认知:有数量很大的智能手机用户从未使用过手机上的应用。Android快速地降低了智能手机准入价格门槛,很多用户购买智能手机仅仅是因为价格和用来拨打电话的功能手机一样。换句话说,由于价格下降,智能手机“泄露”到低应用认识的用户人群。
阿根廷一位移动web专家说:“目前,拉美对开发者只有很少的机遇。例如,大部分阿根廷开发者编写英文应用,并向全球发布。”
尽管如此,本地化以及适配本地语言和文化是应用使用和应答需求的最大障碍之一。Felipe Andrade解释道:“进入亚洲市场的主要障碍在于文化差异和获取/创建合适内容”,Andrade是巴西软件公司i2移动的产品和服务主管。尽管某些应用类型,如游戏或者实用类可以面向全球观众,大部分应用要在地区推广,需要适配当地语言和文化背景。
应用使用哪种语言?
为揭示应用的地区相关性,我们根据开发者所在地区,研究调查的1500+开发者他们的应用所使用语言。我们发现,绝大部分开发者(85%)发布英文应用,21%发布西班牙应用,16%发布中文应用。需要注意,开发者发布的应用平均有2个语言版本。
比较全球范围说话语言和开发者应用语言,可清晰看到语言欠缺。下图揭示了说话语言和应用语言的不平衡:有85%开发者发布英文应用,而全球人口中只有8%(大约5亿)讲英语,世界人口有22%讲中文,只吸引16%的开发者。
自然,由发布该语言版本的开发者比例来代表该语言的应用提供量,反应了应用需求格局;北美和欧洲推动应用需求,那里英语在说话语言中有很高比例。通过英语,开发者可以达到两个目的:直接面对北美这类高需求市场,以及其他英语地方。全球估计有超过5亿的人讲英语,但也有些估计认为超过十亿。与此同时,英文应用可为非英语用户提供很好的替代方案。作为应用经济的创始成员,某些欧洲语言,如德文和意大利文,同样也有相对高的开发者比例。然而,处在机遇象限的国家语言,即发展中国家经济体,在开发者应用语言中比例很低。
我们从各地区语言提供情况来查看哪些开发者更喜欢英语亦或本土语言。下图根据开发者归属地区显示开发者应用平均语言版本数,比较英语和本地语言产品量。
欧洲本地语言提供有富余:欧洲的开发者发布1.33种本地语言应用(不包括英语),但89%也提供英语版本。平均而言,欧洲开发者发布2.45种语言应用(包括英语),在所有地区提供最多种应用语言。于此同时,没有任何一个欧洲语种比英语所占开发者比例高,所有的地方语言(如德语或意大利语)都比不上英语。这在其他地区也一样,唯一例外的是南美,西班牙语被84%的开发者使用,而英语只有48%。在亚洲,中文被53%开发者使用,而英语的比例为73%。
显然,几乎在所有的地区,英语都在开发者应用语言中占主导地位,使得本地语言供应不足,这不仅在全球范围体现,同样也在地区范围体现。本地语言供应不足是主要摩擦点,压抑主流语言非英语的地区的应用需求。据应用分析公司App Annie的数据,很多亚洲市场,本地语言主导下载排名榜前25名,如韩国(70%)和台湾(60%),说明本地化应用有很高的需求。于此同此,这些国家和地区在本土语言的开发者/人口比例上显得不足。
新兴应用经济体的可寻址市场比英语地区和欧洲大很多。下一个应用经济提升将来自这些地区,即来自机遇象限。为了获取机遇,全球的开发者必须把握供给不足的语言,加速提供本地语言产品。
相关链接:我的产业生态链和杂谈文章
1、通过aidl定义服务接口IService
2、通过aidl定义监听接口IListener
3、ISerice接口中增加接口添加监听
4、Service中实现ISerice接口
5、ServiceManager中获取ISerice接口,对ISerice包装调用。
6、ServiceManager中内嵌IListener实现,添加到ISerice,实现对IListener监听,内嵌IListener只是为了中间处理,最终还是为了返回给用户自定义的IListener
QT父子窗口事件传递与事件过滤器
处理监控系统的时候遇到问题,在MainWidget中创建多个子Widget的时候,原意是想鼠标点击先让MainWidget截获处理后再分派给子Widget去处理,但调试后发现如果子Widget重新实现了事件方法,就直接处理掉事件了,没有进到MainWidget的处理方法中去,如果子Widget没有accept或ignore该事件,则该事件就会被传递给其父亲,在子Widget存在accept或ignore事件的时候,想要经过一下MainWidget的处理方法,就得用到事件处理器,因此网上找了一下,发现QT的事件处理器可以处理。
QT将事件封装为QEvent实例以后,会呼叫QObject的event()方法,并且将QEvent实例传送给它,在某些情况下,希望在执行event()之前,先对一些事件进行处理或过滤,然后再决定是否呼叫event()方法,这时候可以使用事件过滤器。
可以重新定义一个继承自QObject(或其子类)的类的eventFilter()方法,
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if(event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Tab)
{
// 处理Tab键
return true;
}
}
return false;
}
eventFilter()的object参数表示事件发生的来源物件,eventFilter()若返回false,则安装该事件过滤器的对象的event()会继续执行,若返回true,则安装事件过滤器的对象后event()方法就不会被执行,由此进行事件的拦截处理。给本对象安装事件过滤器:
this->installEventFilter(this);
Qt事件的类型很多, 常见的qt的事件如下:
键盘事件: 按键按下和松开.
鼠标事件: 鼠标移动,鼠标按键的按下和松开.
拖放事件: 用鼠标进行拖放.
滚轮事件: 鼠标滚轮滚动.
绘屏事件: 重绘屏幕的某些部分.
定时事件: 定时器到时.
焦点事件: 键盘焦点移动.
进入和离开事件: 鼠标移入widget之内,或是移出.
移动事件: widget的位置改变.
大小改变事件: widget的大小改变.
显示和隐藏事件: widget显示和隐藏.
窗口事件: 窗口是否为当前窗口.
还有一些非常见的qt事件,比如socket事件,剪贴板事件,字体改变,布局改变等等.
Qt的事件和Qt中的signal不一样. 后者通常用来"使用"widget, 而前者用来"实现" widget. 比如一个按钮, 我们使用这个按钮的时候, 我们只关心他clicked()的signal, 至于这个按钮如何接收处理鼠标事件,再发射这个信号,我们是不用关心的. 但是如果我们要重载一个按钮的时候,我们就要面对event了. 比如我们可以改变它的行为,在鼠标按键按下的时候(mouse press event) 就触发clicked()的signal而不是通常在释放的( mouse release event)时候.
事件的产生
事件的两种来源:
一种是系统产生的;通常是window system把从系统得到的消息,比如鼠标按键,键盘按键等, 放入系统的消息队列中. Qt事件循环的时候读取这些事件,转化为QEvent,再依次处理.
一种是由Qt应用程序程序自身产生的.程序产生事件有两种方式, 一种是调用QApplication::postEvent(). 例如QWidget::update()函数,当需要重新绘制屏幕时,程序调用update()函数,new出来一个paintEvent,调用QApplication::postEvent(),将其放入Qt的消息队列中,等待依次被处理. 另一种方式是调用sendEvent()函数. 这时候事件不会放入队列, 而是直接被派发和处理, QWidget::repaint()函数用的就是这种方式.
事件的调度
两种调度方式,一种是同步的, 一种是异步.
Qt的事件循环是异步的,当调用QApplication::exec()时,就进入了事件循环. 该循环可以简化的描述为如下的代码:
while ( !app_exit_loop ) {
while( !postedEvents ) { processPostedEvents() }
while( !qwsEvnts ){ qwsProcessEvents(); }
while( !postedEvents ) { processPostedEvents() }
}
先处理Qt事件队列中的事件, 直至为空. 再处理系统消息队列中的消息, 直至为空, 在处理系统消息的时候会产生新的Qt事件, 需要对其再次进行处理.
调用QApplication::sendEvent的时候, 消息会立即被处理,是同步的. 实际上QApplication::sendEvent()是通过调用QApplication::notify(), 直接进入了事件的派发和处理环节.
事件的派发和处理
首先说明Qt中事件过滤器的概念. 事件过滤器是Qt中一个独特的事件处理机制, 功能强大而且使用起来灵活方便. 通过它, 可以让一个对象侦听拦截另外一个对象的事件. 事件过滤器是这样实现的: 在所有Qt对象的基类: QObject中有一个类型为QObjectList的成员变量,名字为eventFilters,当某个QObjec (qobjA)给另一个QObject (qobjB)安装了事件过滤器之后, qobjB会把qobjA的指针保存在eventFilters中. 在qobjB处理事件之前,会先去检查eventFilters列表, 如果非空, 就先调用列表中对象的eventFilter()函数. 一个对象可以给多个对象安装过滤器. 同样, 一个对象能同时被安装多个过滤器, 在事件到达之后, 这些过滤器以安装次序的反序被调用. 事件过滤器函数( eventFilter() ) 返回值是bool型, 如果返回true, 则表示该事件已经被处理完毕, Qt将直接返回, 进行下一事件的处理; 如果返回false, 事件将接着被送往剩下的事件过滤器或是目标对象进行处理.
Qt中,事件的派发是从QApplication::notify() 开始的, 因为QAppliction也是继承自QObject, 所以先检查QAppliation对象, 如果有事件过滤器安装在qApp上, 先调用这些事件过滤器. 接下来QApplication::notify() 会过滤或合并一些事件(比如失效widget的鼠标事件会被过滤掉, 而同一区域重复的绘图事件会被合并). 之后,事件被送到reciver::event() 处理.
同样, 在reciver::event()中, 先检查有无事件过滤器安装在reciever上. 若有, 则调用之. 接下来,根据QEvent的类型, 调用相应的特定事件处理函数. 一些常见的事件都有特定事件处理函数, 比如:mousePressEvent(), focusOutEvent(), resizeEvent(), paintEvent(), resizeEvent()等等. 在实际应用中, 经常需要重载这些特定事件处理函数在处理事件. 但对于那些不常见的事件, 是没有相对应的特定事件处理函数的. 如果要处理这些事件, 就需要使用别的办法, 比如重载event() 函数, 或是安装事件过滤器.
事件的转发
对于某些类别的事件, 如果在整个事件的派发过程结束后还没有被处理, 那么这个事件将会向上转发给它的父widget, 直到最顶层窗口. 如图所示, 事件最先发送给QCheckBox, 如果QCheckBox没有处理, 那么由QGroupBox接着处理, 如果QGroupBox没有处理, 再送到QDialog, 因为QDialog已经是最顶层widget, 所以如果QDialog不处理, QEvent将停止转发.
如何判断一个事件是否被处理了呢? Qt中和事件相关的函数通过两种方式相互通信.
QApplication::notify(), QObject::eventFilter(), QObject::event() 通过返回bool值来表示是否已处理.
“真”表示已经处理, “假”表示事件需要继续传递. 另一种是调用QEvent::ignore() 或 QEvent::accept() 对事件进行标识. 这种方式只用于event() 函数和特定事件处理函数之间的沟通. 而且只有用在某些类别事件上是有意义的, 这些事件就是上面提到的那些会被转发的事件, 包括: 鼠标, 滚轮, 按键等事件.
实际应用
1.重载特定事件处理函数
最常见的事件处理办法就是重载象mousePressEvent(), keyPressEvent(), paintEvent() 这样的特定事件处理函数. 以按键事件为例, 一个典型的处理函数如下:
void imageView::keyPressEvent(QKeyEvent * event)
{
switch (event->key()) {
case Key_Plus:
zoomIn();
break;
case Key_Minus:
zoomOut();
break;
case Key_Left:
// …
default:
QWidget::keyPressEvent(event);
}
}
2.重载event()函数
通过重载event()函数,我们可以在事件被特定的事件处理函数处理之前(象keyPressEvent())处理它. 比如, 当我们想改变tab键的默认动作时,一般要重载这个函数. 在处理一些不常见的事件(比如:LayoutDirectionChange)时,evnet()也很有用,因为这些函数没有相应的特定事件处理函数. 当我们重载event()函数时, 需要调用父类的event()函数来处理我们不需要处理或是不清楚如何处理的事件.
下面这个例子演示了如何重载event()函数, 改变Tab键的默认动作: (默认的是键盘焦点移动到下一个控件上. )
bool CodeEditor::event(QEvent * event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = (QKeyEvent *) event;
if (keyEvent->key() == Key_Tab) {
insertAtCurrentPosition('\t');
return true;
}
}
return QWidget::event(event);
}
3.在QT对象上安装事件过滤器
安装事件过滤器有两个步骤: (假设要用A来监视过滤B的事件)
首先调用B的installEventFilter( const QOject *obj ), 以A的指针作为参数. 这样所有发往B的事件都将先由A的eventFilter()处理.
然后, A要重载QObject::eventFilter()函数, 在eventFilter() 中书写对事件进行处理的代码.
用这种方法改写上面的例子: (假设我们将CodeEditor 放在MainWidget中)
MainWidget::MainWidget()
{
// …
CodeEditor * ce = new CodeEditor( this, “code editor”);
ce->installEventFilter( this );
// …
}
bool MainWidget::eventFilter( QOject * target , QEvent * event )
{
if( target == ce ){
if( event->type() == QEvent::KeyPress ) {
QKeyEvent *ke = (QKeyEvent *) event;
if( ke->key() == Key_Tab ){
ce->insertAtCurrentPosition('\t');
return true;
}
}
}
return false;
}
4.给QAppliction对象安装事件过滤器
一旦我们给qApp(每个程序中唯一的QApplication对象)装上过滤器,那么所有的事件在发往任何其他的过滤器时,都要先经过当前这个eventFilter(). 在debug的时候,这个办法就非常有用, 也常常被用来处理失效了的widget的鼠标事件,通常这些事件会被QApplication::notify()丢掉.
( 在QApplication::notify() 中, 是先调用qApp的过滤器, 再对事件进行分析, 以决定是否合并或丢弃)
5.继承QApplication类,并重载notify()函数
Qt是用QApplication::notify()函数来分发事件的.想要在任何事件过滤器查看任何事件之前先得到这些事件,重载这个函数是唯一的办法. 通常来说事件过滤器更好用一些, 因为不需要去继承QApplication类. 而且可以给QApplication对象安装任意个数的事件过滤器, 相比之下, notify()函数只有一个.
文章出处:
http://www.cnblogs.com/bingcaihuang/archive/2010/12/17/1909369.html