通过前面的两篇文章,我想大家入门应该是没什么问题的,下面将如实地讲解植入到代码中的方法与捷径。
从所周知,通常我们一个ViewController都是继承自UIViewController。这个相信大家都很清楚。便GA的SDK在跟踪页面的时候要求所有VIEWCONTROLLER继承于GAITrackedViewController,其实它与是继承自UIViewContoller。
好现在第一个问题摆在面前的是,我的项目起码有100多个ViewController,每一个都修改为继承GAITrackedViewController,这是一个量的问题。
如果这个时候你就动手改了的话,哪后面的工作就有得你改了。
eg:
@interface goodsViewController : UIViewController
{
...
}
@end
修改后为:
@interface goodsViewController : GAITrackedViewContoller
{
...
}
@end
好,为了以后更好扩展,不建议各个ViewController直接继承GAITrackedViewController,此时我们同样可以由GAITrackedViewController 来派生一个子基类,然后在子基类中引入"GAITrackedViewController.h"即可,然后我们所有的ViewContoller继承些基类就省事多了。往后有些什么需要改动的全部的地方,想想是否可以使用基类来完成。
修改后如下:
#import "GAITrackedViewContoller.h"
@interface BaseViewController: GAITrackedViewContoller
{
...
}
@end
具体的ViewController
#import "BaseViewController.h"
@interface goodsViewController : BaseViewController
{
...
}
@end
好,按照上面把你的所有ViewController继承到了自己的基类,如果都改好后,哪么你的第一步植入GA SDK算完成了一半吧。
下面进行GA的SDK初始化,在官网中初始化动作,常常放在appdelegate中的
didFinishLaunchingWithOptions
方法中来实进行。如:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Optional: automatically send uncaught exceptions to Google Analytics. [GAI sharedInstance].trackUncaughtExceptions = YES; // Optional: set Google Analytics dispatch interval to e.g. 20 seconds. [GAI sharedInstance].dispatchInterval = 20; // Optional: set debug to YES for extra debugging information. [GAI sharedInstance].debug = YES; // Create tracker instance. id<GAITracker> tracker = [[GAI sharedInstance] trackerWithTrackingId:@"UA-YOUR-TRACKING-ID"]; }OK,有了这个初始化之后,就可以进行页面跟踪了。就这么简单,没什么含量嘛。呵呵,还没有的,如果你认为这样就OK,哪就大错特错了,因为GA还有一个跟踪对象,这个对象在每个ViewController中是不会被初始化的,因此必段在viewDidLoad中进行添加一句self.tracker = [[GAI shareInstance] defaultTracker];这句是必要的,但不一定放在这里初始化,不过放在这里是常常的动作。哪么这里又来问题了,前面刚修改完100多个ViewController的继承关系,这里又要在每个Controller中进行赋值,因此,是否可以在构造或父类中进行初始化呢,答安是可以的,但这里就要权衡一下选哪种方式了,如果你的代码都已写好了,并且在某些地方也做了调用,这里如果改为构造方式进行,哪就可审查代码,这也是耗时的工作,在这里建议大家使用在父类创建的构造中进行初始化。
为满足已有项目中改动最小的原则,于是我把GA SDK 的配置值单独作为一个单例类。假设为 GASDKSingle ,然后在基类和应该用程序代理类中引用这个单例。即在appDelegate.m中
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[单例类 initSDKValueOfConfig]; //在此方法中实现必要的配置数据即可。
....
}
在基类中 使用这程方法的缺陷是如果使用NIB进行构造的话就不会调用到父类的此方法上。
@implement BaseViewController
- (id)init
{
self = [supper init];
if (self)
{
self.tracker = 单例配置类(GASDKSingle ).tracker; //哈哈,注意到了没有,在这里就配置好了。以后就不用在各个类中赋值了。
}
return self;
}
@end
以上是讲解过程,下面是我的一个具体单例类:
CGAConfig.h
//
// CGAConfig.h
// DemoTest
//
// Created by on 13-4-23.
// Copyright (c) 2013年 __fengsh998__. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "GAI.h"
#import "GAITracker.h"
@interface CGAConfig : NSObject
@property (nonatomic,assign) id<GAITracker> tracker;//保存全局只有一个防止retain
@property (nonatomic,assign) NSTimeInterval dispatchInterval;
@property (nonatomic,assign) BOOL isDebug;
@property (nonatomic,assign) double SampleRate;
@property (nonatomic,assign) BOOL OptOut;
@property (nonatomic,assign) BOOL trackUncaughtExceptions;
@property (nonatomic,assign) BOOL Anonymize;
@property (nonatomic,assign) BOOL UseHttps;
+(CGAConfig*)ShareInstrance;
-(void)destroy;
@end
CGAConfig.m
//
// CGAConfig.m
// DemoTest
//
// Created by on 13-4-23.
// Copyright (c) 2013年 __fengsh998__. All rights reserved.
//
// Google Analytics config
//
//
#import "CGAConfig.h"
static CGAConfig * gacfg = nil;
@implementation CGAConfig
@synthesize tracker = _tracker;
@synthesize dispatchInterval = _dispatchInterval;
@synthesize isDebug = _isDebug;
@synthesize SampleRate = _SampleRate;
@synthesize OptOut = _OptOut;
@synthesize trackUncaughtExceptions = _trackUncaughtExceptions;
@synthesize Anonymize = _Anonymize;
@synthesize UseHttps = _UseHttps;
/*
-(id)init
{
[NSException raise:@"singlton not call init."
format:@"Abort, retry, fail?"];
return nil;
}
*/
-(void)InitDefaulteValue
{
//add google analytics
[GAI sharedInstance].trackUncaughtExceptions = YES;
//设置每隔多少时间向GA发送一次数据
[GAI sharedInstance].dispatchInterval = 20;
//设置打开SDK的调试信息开关
[GAI sharedInstance].debug = YES;
self.tracker = [[GAI sharedInstance] trackerWithTrackingId:@"UA-40231989-1"];
//[self.tracker setSampleRate:50.0];//设置采样的速度
//设置是否停止向GA发送数据
//[[GAI sharedInstance] setOptOut:YES];
//设置捕获tracker产生的异常,当为FALSE时不捕捉
if ([GAI sharedInstance].trackUncaughtExceptions) {
[[GAI sharedInstance]setTrackUncaughtExceptions:YES];
}
//黑认为false,设置是否Ni名访问GA
//[self.tracker setAnonymize:YES];
//设置是否使用https访问GA default = YES
//[self.tracker setUseHttps:YES];
//手动跟踪某个view
}
-(void)dealloc
{
//防止自动释放时忘记nil
[_tracker close];
gacfg = nil;
[super dealloc];
}
-(void)destroy
{
if (gacfg) {
[gacfg release];
}
}
+(id) allocWithZone:(NSZone *)zone
{
@synchronized(self)
{
if (!gacfg)
{
gacfg = [super allocWithZone:zone];
return gacfg;
}
}
return nil;
}
+(CGAConfig*)ShareInstrance
{
@synchronized(self)
{
if (!gacfg)
{
gacfg = [[self alloc] init];
[gacfg InitDefaulteValue];
}
}
return gacfg;
}
-(void)setDispatchInterval:(NSTimeInterval)dispatchInterval
{
[GAI sharedInstance].dispatchInterval = dispatchInterval;
}
-(NSTimeInterval)setDispatchInterval
{
return [GAI sharedInstance].dispatchInterval;
}
-(void)setIsDebug:(BOOL)isDebug
{
[GAI sharedInstance].debug = isDebug;
}
-(BOOL)getIsDebug
{
return [GAI sharedInstance].debug;
}
-(void)setSampleRate:(double)SampleRate
{
[self.tracker setSampleRate:SampleRate];
}
-(double)getSampleRate
{
return self.tracker.sampleRate;
}
-(void)setOptOut:(BOOL)OptOut
{
[[GAI sharedInstance] setOptOut:OptOut];
}
-(BOOL)getOptOut
{
return [GAI sharedInstance].optOut;
}
-(void)setTrackUncaughtExceptions:(BOOL)trackUncaughtExceptions
{
[[GAI sharedInstance] setTrackUncaughtExceptions:trackUncaughtExceptions];
}
-(BOOL)getTrackUncaughtExceptions
{
return [GAI sharedInstance].trackUncaughtExceptions;
}
-(void)setAnonymize:(BOOL)Anonymize
{
[self.tracker setAnonymize:Anonymize];
}
-(BOOL)getAnonymize
{
return self.tracker.anonymize;
}
-(void)setUseHttps:(BOOL)UseHttps
{
[self.tracker setUseHttps:UseHttps];
}
-(BOOL)getUseHttps
{
return self.tracker.useHttps;
}
@end
在此也贴一下我的父类的DEMO实现。
头文件
#import <UIKit/UIKit.h>
#import "GAITrackedViewController.h"
@interface CPrjBaseViewController : GAITrackedViewController
@end
实现文件
#import <UIKit/UIKit.h>
#import "GAITrackedViewController.h"
@interface CPrjBaseViewController : GAITrackedViewController
@end
#import "CPrjBaseViewController.h"
#import "CGAConfig.h"
@implementation CPrjBaseViewController
-(id)init
{
self = [super init];
if (self) {
//add code here.
self.tracker = [CGAConfig ShareInstrance].tracker;
}
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
//add code here.
self.tracker = [CGAConfig ShareInstrance].tracker;
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
/*
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView
{
}
*/
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
[super viewDidLoad];
}
*/
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
//self.trackedViewName = @"";
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end
以上OK之后,整个项目的植入准备工作已完成,就可以进行相关的跟踪了。
另:附两个研究要点,一监听所有TOUCH事件,进行捕捉,捕捉后统一区分进行发送,这样就减少了每个事件的地方写一次发送事件的代码,当然这个也是具体项目来使用的。拦截代码如下:
- (void)sendEventHooked:(UIEvent *)event {
//在这里做你想做的事情吧
NSLog(@"截获事件:%@", [event description]);
UIView *obj = [[[event allTouches]anyObject] view];
if ([obj isKindOfClass:[UIButton class]])
{
NSLog(@"OK ,button %@",[(UIButton *)obj titleLabel].text); //在这里可以做好多事情的。哈哈。
}
//执行原来的消息传递流程
[self performSelector:@selector(sendEventOriginal:)withObject:event];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//拦截事件消息
Method sendEvent =class_getInstanceMethod([UIWindow class],@selector(sendEvent:));
Method sendEventMySelf =class_getInstanceMethod([self class], @selector(sendEventHooked:));
IMP sendEventImp =method_getImplementation(sendEvent);
class_addMethod([UIWindow class],@selector(sendEventOriginal:), sendEventImp,method_getTypeEncoding(sendEvent));
IMP sendEventMySelfImp =method_getImplementation(sendEventMySelf);
class_replaceMethod([UIWindow class],@selector(sendEvent:), sendEventMySelfImp,method_getTypeEncoding(sendEvent));
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[[CViewController alloc] initWithNibName:@"CViewController_iPhone" bundle:nil] autorelease];
} else {
self.viewController = [[[CViewController alloc] initWithNibName:@"CViewController_iPad" bundle:nil] autorelease];
}
//ak
// self.viewController.tracker = self.tracker;
self.Navigater = [[UINavigationController alloc]init];
[self.Navigater pushViewController:self.viewController animated:NO];
self.window.rootViewController = self.Navigater;//self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
第二种就是使用全局宏进行配置,利用调试开关控制跟踪输出。
同时整个SDK的授根协议为GNU GPL v2 因此对商用的用户需要特别注意。
最好,完工。