作为一款应用的开发者,我很关心应用的是否稳定,用户的使用习惯和浏览行为是什么;作为应用的运营者,最关心应用的激活量,用户使用频率,推广渠道等。这些都需要海量数据的支持。但是一个小团队独立开发一套数据统计平台,基本是不现实的。
我们都知道在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