作为一款应用的开发者,我很关心应用的是否稳定,用户的使用习惯和浏览行为是什么;作为应用的运营者,最关心应用的激活量,用户使用频率,推广渠道等。这些都需要海量数据的支持。但是一个小团队独立开发一套数据统计平台,基本是不现实的。
我们都知道在Web网站上,每一个站长都会使用一些统计工具,来了解访客的一些基本信息。最初这些统计代码都是自己写的,到后来大家意识到,这是一个市场,于是有专门的公司来提供这种数据统计服务。
下面介绍的一款移动应用统计平台---友盟,平台有完善的文档,就不在赘述使用方法啦。好了,废话不多说,开始介绍,部分内容整理自网络。
创新工场孵化项目之一, 我目前正在使用的统计平台。目前友盟提供的SDK数量很多,包括标准的统计分析SDK、SNS分享SDK、用户反馈SDK,以及应用联盟SDK。开发者可以根据需要自行选择,我们根据需要选择统计分析SDK即可。平台方面,友盟目前支持iOS、Android及Windows Phone平台(目前Windows Phone平台仅提供统计分析SDK)。下面是功能简介
统计概况
统计概况中包含基本统计和版本分布,帮助开发者了解基本的数据情况,主要是用户增长情况和应用的启动次数等信息。
用户分析
而留存用户则是用于考察用户忠诚度的参数,简单来说就是新增用户经过一段时间后,仍有多少用户继续使用应用。这是一个相当重要的参数,一方面可以体现出应用的质量;另一方面,它也是考察运营动作、渠道推广、渠道质量的重要参数。
比如,开发者可以通过考察某一时间点的留存用户情况,对比自身应用推广获得的新增用户情况,用于评估活动推广效果、渠道用户质量等运营细节。在时间粒度方面,友盟的留存用户模块提供按周和按月查询两种方式。但是对于现在不少以周为周期迭代更新的应用来说,这个颗粒度不够细。毕竟大量的客户流失都发生在安装应用后的前几日内,看日留存率还是很有必要的。
渠道分析
自定义事件
友盟的自定义事件模块主要用于管理设置好的自定义事件,并针对每一个自定义事件提供数据统计功能。如果开发者需要了解到各事件之间的转化情况,就需要进入转化率分析模块,利用友盟提供的漏斗模型功能,基本使用流程上与Flurry很接近,设置好事件转化流程后,即可查看每个步骤之间的转化情况。
以上是友盟近期更新的一些关键功能,另外像是传统的终端及网络、错误分析等功能,友盟仍然提供给开发者。只不过,这些数据目前还不能像Flurry那样用于细分用户群,希望友盟能够尽快推出用户群细分功能,一方面能够给开发者提供更为全面的服务,另一方面也可以更充分的利用自身平台数据,使这些数据成为有机的整体。另外,友盟在最新的更新中,加入了类似Flurry的全局筛选功能,不过目前来看这一功能似乎存在一些问题,比如选择按版本查看后,再选择一个时间段,之前选择的版本设置会恢复到默认的“全部”,反之亦然。如果这不是一个Bug的话,那么只能是这个全局筛选功能并不能做到跨版本、跨时间段交叉筛选,只是比较像全局筛选的开关而已。总体来说,友盟在数据统计的功能设置上,还是比较全面的,但在数据分析层面,与Flurry还有较大差距,这方面还有很多空间可挖。作为国内目前知名度最高的移动应用数据统计分析平台,友盟做的相当不错。尤其是友盟也和Flurry一样,具有应用联盟平台,在这个平台上用户可以通过内置友盟的移动广告平台实现盈利,也可以通过友盟的交换网络实现应用推广,对开发者来说还是比较方便的。不过国内应用推广、广告领域可以说是鱼龙混杂,形势颇为复杂。友盟的服务虽然对开发者来说足够便捷,但最终的实现层面上的价值有多大,还需要进一步考察。
其他移动统计平台:
TalkingData:
CNZZ移动统计平台:
百度移动统计平台:
欢迎访问:望月听涛
简介:
上篇我们讲到了KVC,这篇我们学习KVO,全名为:Key Value Observing,直译为:基于键值的观察者。
那它有什么用呢?KVO主要用于视图交互方面,比如界面的某些数据变化了,界面的显示也跟着需要变化,那就要建立数据和界面的关联。
ObjC中提供的KVO就是解决这种问题的。以下用显示页面观察学生的课程名称变化的例子来说明KVO的使用。
学生类命名为:Student,页面类是:PageView.
上图来自苹果官网,图中的BankObject好比PageView,PersonObject好比Student,
PageView观察Student的变化。
1、添加Student学生类。.h
#import <Foundation/Foundation.h> @interface Student : NSObject { NSString *name; NSString *courseName; } -(void)changeCourseName:(NSString*) newCourseName; @end
类中有name,和课程名称courseName,添加一个可以改变课程名称的方法changeCourseName。一会用来做对比,看直接改变课程名称时会不会有回调。
实现文件.m
#import "Student.h" @implementation Student -(void)changeCourseName:(NSString*) newCourseName { courseName = newCourseName; } @end
实现类把方法实现了。
.h文件
#import <Foundation/Foundation.h> @class Student; @interface PageView : NSObject { Student *student; } -(id)init:(Student*)initStudent; @end
.m文件
#import "PageView.h" #import "Student.h" @implementation PageView -(id)init:(Student*)initStudent { if (self = [super init]) { student = initStudent; [student addObserver:self forKeyPath:@"courseName" options:NSKeyValueObservingOptionOld |NSKeyValueObservingOptionNew context:nil]; } return self; } - (void) dealloc{ [student removeObserver:self forKeyPath:@"courseName" context:nil]; [super dealloc]; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if ([keyPath isEqual:@"courseName"]) { NSLog(@"PageView课程被改变了"); NSLog(@"PageView新课程是:%@ 老课程是:%@", [change objectForKey:@"new"],[change objectForKey:@"old"]); } } @end
init初始化时,向student实例添加观察者,在释放的时候移除观察者。
3、实现观察在main函数中
#import "Student.h" #import "Course.h" #import "PageView.h" int main(int argc, const char * argv[]) { @autoreleasepool { Student *student = [[[Student alloc]init]autorelease]; [student changeCourseName:@"数学课"]; NSLog(@"初始值:%@", [student valueForKey:@"courseName"]); //创建页面实例 PageView *pageview = [[[PageView alloc]init:student]autorelease]; [student setValue:@"化学课" forKey:@"courseName"]; } return 0; }新建一个student的实例,设置他的课程是数学课,然后创建页面类的时候,用student初始化。这是页面类已经观察着学生的课程了。
再给课程设置新的值为化学课。这时候运行打印结果:
2012-07-24 16:29:21.561 objectiveC[2192:403] 初始值:数学课
2012-07-24 16:29:21.565 objectiveC[2192:403] PageView课程被改变了
2012-07-24 16:29:21.566 objectiveC[2192:403] PageView新课程是:化学课老课程是:数学课
可以看到Pageview类中的回调被调用,Pageview接收到学生课程数据更新的信息。 4、直接改变课程信息对比#import "Student.h" #import "Course.h" #import "PageView.h" int main(int argc, const char * argv[]) { @autoreleasepool { Student *student = [[[Student alloc]init]autorelease]; [student changeCourseName:@"数学课"]; NSLog(@"初始值:%@", [student valueForKey:@"courseName"]); //创建页面实例 PageView *pageview = [[[PageView alloc]init:student]autorelease]; [student setValue:@"化学课" forKey:@"courseName"]; [student changeCourseName:@"英语课"]; NSLog(@"直接改变的课程为:%@", [student valueForKey:@"courseName"]); } return 0; }直接调用changeCourseName方法改变课程,打印结果:
2012-07-24 16:32:06.230 objectiveC[2240:403] 初始值:数学课
2012-07-24 16:32:06.237 objectiveC[2240:403] PageView课程被改变了
2012-07-24 16:32:06.238 objectiveC[2240:403] PageView新课程是:化学课老课程是:数学课
2012-07-24 16:32:06.239 objectiveC[2240:403] 直接改变的课程为:英语课
可以看到,这时Pageview的回调没被调用到。说明只有通过键值编码(KVC)改变的值,才会回调观察者注册的方法。
这里是苹果官网的关于KVO的文档,英文好的朋友可以看看:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177-BCICJDHA
著作权声明:本文由http://blog.csdn.net/totogo2010/原创,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!
申请key:
package com.baidu.mapapi.demo; import android.app.Application; import android.util.Log; import android.widget.Toast; import com.baidu.mapapi.*; public class BMapApiDemoApp extends Application { static BMapApiDemoApp mDemoApp; //百度MapAPI的管理类 BMapManager mBMapMan = null; // 授权Key // TODO: 请输入您的Key, // 申请地址:http://dev.baidu.com/wiki/static/imap/key/ String mStrKey = "请输入您的Key"; boolean m_bKeyRight = true; // 授权Key正确,验证通过 // 常用事件监听,用来处理通常的网络错误,授权验证错误等 static class MyGeneralListener implements MKGeneralListener { @Override public void onGetNetworkState(int iError) { Log.d("MyGeneralListener", "onGetNetworkState error is "+ iError); Toast.makeText(BMapApiDemoApp.mDemoApp.getApplicationContext(), "您的网络出错啦!", Toast.LENGTH_LONG).show(); } @Override public void onGetPermissionState(int iError) { Log.d("MyGeneralListener", "onGetPermissionState error is "+ iError); if (iError == MKEvent.ERROR_PERMISSION_DENIED) { // 授权Key错误: Toast.makeText(BMapApiDemoApp.mDemoApp.getApplicationContext(), "请在BMapApiDemoApp.java文件输入正确的授权Key!", Toast.LENGTH_LONG).show(); BMapApiDemoApp.mDemoApp.m_bKeyRight = false; } } } @Override public void onCreate() { Log.v("BMapApiDemoApp", "onCreate"); mDemoApp = this; mBMapMan = new BMapManager(this); mBMapMan.init(this.mStrKey, new MyGeneralListener()); mBMapMan.getLocationManager().setNotifyInternal(10, 5); // if (mBMapMan != null) { // mBMapMan.destroy(); // mBMapMan = null; // } super.onCreate(); } @Override //建议在您app的退出之前调用mapadpi的destroy()函数,避免重复初始化带来的时间消耗 public void onTerminate() { // TODO Auto-generated method stub if (mBMapMan != null) { mBMapMan.destroy(); mBMapMan = null; } super.onTerminate(); } }
注册账号百度账号,申请key
资源下载地址:
http://download.csdn.net/detail/jingwen3699/4447494