日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ios无痕埋点_iOS可视化埋点方案

發布時間:2024/9/19 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ios无痕埋点_iOS可视化埋点方案 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

隨著公司業務的發展,數據的重要性日益體現出來。 數據埋點的準確和全面性顯得尤為重要。

通過精準和詳細的數據,后面的分析才有意義。隨著業務的不斷變化,動態化埋點也越來越重要。

三大埋點方式

為了解決這些問題, 很多公司都提出自己的解決方案, 各中解決方案中,大體分為以下三種:

1、代碼埋點

由開發人員在觸發事件的具體方法里,植入多行代碼把需要上傳的參數上報至服務端。

2、可視化埋點

根據標識來識別每一個事件, 針對指定的事件進行取參埋點。而事件的標識與參數信息都寫在配置表中,通過動態下發配置表來實現埋點統計。

3、無埋點

無埋點,也稱為“無痕埋點”,無埋點還有另一種叫法:全埋點。

前端的任意一個事件都被綁定一個標識,所有的事件都別記錄下來。通過定期上傳記錄文件,配合文件解析,解析出來我們想要的數據, 并生成可視化報告供專業人員分析 ,因此稱為“無埋點”統計。

無埋點方案目前已經有神策埋點實現,另外考慮到“無埋點”的方案成本較高,并且后期解析也比較復雜,加上view_path的不確定性,所以本文重點介紹“可視化埋點”的簡單實現方式。

可視化埋點

可視化埋點并非完全拋棄了代碼埋點,而是在代碼埋點的上層封裝的一套邏輯來代替手工埋點。理論上可視化的埋點也應該封裝在埋點的SDK層,但是由于歷史原因,智聯的埋點SDK只封裝了緩存數據和上報數據這一層,所以我們可以在客戶端層面,增加一層可視化埋點SDK。

大體架構如下:

WX20181212-111410@2x.png

從業務架構上來看,可視化埋點主要對頁面Out、In(PV)、按鈕等事件點擊(Action)、列表的滑動、點擊等(List)、手勢動作(Gesture)進行埋點,就能覆蓋90%以上統計事件。

要解決的問題

代碼埋點可以解決所有的自定義埋點,深入程度也是最高的,但是他有天然劣勢,就是每出現一個新的頁面,新的需求,都需要開發人員植入多行代碼把需要上傳的參數上報至服務端,開發成本高,效率低下,經常出現業務開發需求一星期,埋點埋兩星期的情況。

埋點需要解決的問題有:

1、重復埋點問題

如何才能動態埋點,不需要每次需求都要特意去埋一次點,特別是那些頁面的進出、停留時長等的埋點,重復的埋點徒增開發時間。

2、pageid(pagecode)不同而且無規律問題

雖然可視化埋點可以利用Hook原理,來解決這個問題,但由于統計的要求,每個頁面都自帶不同的pageid或者pagecode,這樣一來無法利用父類的方式去一次性埋點,因為即使通過繼承的方式,也無法做到每個子類都有不同的pageid。即使利用Hook原理,去Hook每一個頁面的Appear和DisAppear方法,也無法對這些不同的頁面注入不同的Pagecode,這樣唯一標識又構成了瓶頸。

3、動態埋點問題

即使進行了代碼埋點,每個版本都進行埋點,但是卻無法對已經上線的版本進行埋點,假如上線后有些埋點忘記埋了,就只能等到下個版本才能進行埋點的添加,是否有辦法做到動態的下發配置來對線上版本增加埋點,而不需要發版呢?

4、頁面OI先后順序問題

代碼埋點的方式,如果想知道C頁面是從A頁面進來的,還是從B頁面還是先A再B最后在進入C的,就得對每個頁面進行一個傳值,而且這樣做還有一個弊端就是可能不知道用戶這個C頁面可能是這樣的:A->B->D->B->A->B->C,普通的代碼埋點只能知道是B到C,卻不了解進入C之前其實有很多前進后退頁面的操作,這樣對數據分析可能就會有偏差,那是否有辦法做到自動記錄頁面進出的方案呢?

解決方案

唯一標識的組成方式主要是又 target + action 來確定, 即任何一個事件都存在一個target與action。 在此引入AOP編程,AOP(Aspect-Oriented-Programming)即面向切面編程的思想,基于 Runtime 的 Method Swizzling能力,來 hook 相應的方法,從而在hook方法中進行統一的埋點處理。例如所有的按鈕被點擊時,都會觸發UIApplication的sendAction方法,我們hook這個方法,即可攔截所有按鈕的點擊事件。

WX20181212-143303@2x.png

但是剛才問題2提到,只是單純的Hook,無法解決PageCode、ActionCode如果埋入的問題,即:“事件唯一標識符“如何埋入的問題。所以在這里,我們要利用一份配置表來管理這個“事件唯一標識符“。

這里主要分為兩個部分 :

事件的鎖定

事件的鎖定主要是靠 “事件唯一標識符”來鎖定,而事件的唯一標識是由我們寫入配置表中的。這里分為兩種,本地配置表和線上下載的配置表。

埋點數據的上報。

埋點數據的數據又分為兩種類型: 固定數據與可變的業務數據, 而固定數據我們可以直接寫到配置表中,通過唯一標識來獲取。而對于業務數據,我是這么理解的:數據是有持有者的,例如我們Controller的一個屬性值,又或者數據再Model的某一個層級。這么的話我們就可以通過KVC的的方式來遞歸獲取該屬性的值來取到業務數據。

整體解決方案

由于業務中的事件場景是多樣的,以iOS為例,在此我以UIControl(Button、Switch、TextField等都屬于Control), UITablview(CollectionView與TableView基本相同,Android里對應的則是ListView),UITapGesture,UIViewController的PV統計為例,介紹一下具體思路。

UIViewController PV統計

頁面的統計較為簡單,利用Method Swizzing hook 系統的viewDidLoad, 直接通過頁面名稱即可鎖定頁面的展示代碼如下:

+ (void)load

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

SEL originalAppearSelector = @selector(viewWillAppear:);

SEL swizzingAppearSelector = @selector(analysis_viewWillAppear:);

[ZPMMethodSwizzingTool swizzingForClass:[self class] originalSel:originalAppearSelector swizzingSel:swizzingAppearSelector];

SEL originalDisappearSelector = @selector(viewWillDisappear:);

SEL swizzingDisappearSelector = @selector(analysis_viewWillDisappear:);

[ZPMMethodSwizzingTool swizzingForClass:[self class] originalSel:originalDisappearSelector swizzingSel:swizzingDisappearSelector];

SEL originalDidLoadSelector = @selector(viewDidLoad);

SEL swizzingDidLoadSelector = @selector(analysis_viewDidLoad);

[ZPMMethodSwizzingTool swizzingForClass:[self class] originalSel:originalDidLoadSelector swizzingSel:swizzingDidLoadSelector];

});

}

Hook系統ViewDidLoad的方法大致如下:

- (void)analysis_viewDidLoad

{

[self analysis_viewDidLoad];

self.view.backgroundColor = [UIColor whiteColor];

//從配置表中取參數的過程 1 固定參數 2 業務參數(此處參數被target持有)

NSString *identifier = [NSString stringWithFormat:@"%@", [self class]];

// NSLog(@"identifier:%@",identifier);

NSDictionary *dic = [[[ZPMDataContainer sharedInstance].data objectForKey:@"PAGEPV"] objectForKey:identifier];

if (dic) {

NSString *pageid = dic[@"userDefined"][@"pageid"];

NSString *pagename = dic[@"userDefined"][@"pagename"];

NSDictionary *pagePara = dic[@"pagePara"];

__block NSMutableDictionary *uploadDic = [NSMutableDictionary dictionaryWithCapacity:0];

[pagePara enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {

id value = [ZPMCapturePropertyTool captureVarforInstance:self withPara:obj];

if (value && key) {

[uploadDic setObject:value forKey:key];

}

}];

NSLog(@"\n 事件唯一標識為:%@ \n pageid === %@,\n pagename === %@,\n pagepara === %@ \n", [self class], pageid, pagename, uploadDic);

}

}

UIControl 點擊統計

主要通過hook sendAction:to:forEvent: 來實現, 其唯一標識符我們用 targetname/selector/tag來標記,具體代碼如下:

+ (void)load

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

SEL originalSelector = @selector(sendAction:to:forEvent:);

SEL swizzingSelector = @selector(analysis_sendAction:to:forEvent:);

[ZPMMethodSwizzingTool swizzingForClass:[self class] originalSel:originalSelector swizzingSel:swizzingSelector];

});

}

- (void)analysis_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event

{

[self analysis_sendAction:action to:target forEvent:event];

NSString *identifier = [NSString stringWithFormat:@"%@/%@", [target class], NSStringFromSelector(action)];

NSDictionary *dic = [[[ZPMDataContainer sharedInstance].data objectForKey:@"ACTION"] objectForKey:identifier];

if (dic) {

NSString *eventid = dic[@"userDefined"][@"eventid"];

NSString *targetname = dic[@"userDefined"][@"target"];

NSString *pageid = dic[@"userDefined"][@"pageid"];

NSString *pagename = dic[@"userDefined"][@"pagename"];

NSDictionary *pagePara = dic[@"pagePara"];

__block NSMutableDictionary *uploadDic = [NSMutableDictionary dictionaryWithCapacity:0];

[pagePara enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {

id value = [ZPMCapturePropertyTool captureVarforInstance:target withPara:obj];

if (value && key) {

[uploadDic setObject:value forKey:key];

}

}];

NSLog(@"\n event id === %@,\n target === %@, \n pageid === %@,\n pagename === %@,\n pagepara === %@ \n", eventid, targetname, pageid, pagename, uploadDic);

}

}

TableView (CollectionView) 的點擊統計

tablview的唯一標識, 我們使用 delegate.class/tableview.class/tableview.tag的組合來唯一鎖定。主要是通過hook setDelegate 方法, 在設置代理的時候再去交互 didSelect(點擊)、scrollViewDidScroll(滑動列表)方法來實現。具體代碼先不上了。

gesture方式添加的的點擊統計

gesture的事件,是通過 hook initWithTarget:action:方法來實現的, 事件的唯一標識依然是target.class/actionname來鎖定的。

從上面的代碼可以看出,這個pageid和eventid都不是寫死的,而且從一個字典里面獲取值,如dic[@"userDefined"][@"pageid"],那么這個value從哪獲取呢,這里就要用到配置表了。

配置表結構

配置表是一個json數據。 針對不同的場景 (UIControl , 頁面PV, Tabeview, Gesture)都做了區分, 用不同的key區別。 對于 "固定參數" , 我們之間寫到配置表中,而對于業務參數, 我們之間寫清楚參數在業務內的名字, 以及上傳時的 keyName, 參數的持有者。 通過Runtime + KVC來取值。 配置表可以是這個樣子:(僅供參考)

{

"ACTION": {

"ThirdViewController/jumpSecond:": {

"userDefined": {

"eventid": "201803074|93",

"target": "",

"pageid": "234",

"pagename": "button點擊,跳轉至下一個頁面"

},

"pagePara": {

"testKey9": {

"propertyName": "testPara",

"propertyPath":"",

"containIn": "0"

}

}

},

"SecondViewController/back": {

"userDefined": {

"eventid": "201803074|965",

"target": "second",

"pageid": "235",

"pagename": "button點擊,返回"

},

"pagePara": {

"testKey9": {

"propertyName": "testPara",

"propertyPath":"",

"containIn": "0"

}

}

}

},

"PAGEPV": {

"HomeViewController": {

"userDefined": {

"pageid": "3156",

"pagename": "首頁展示了"

},

"pagePara": {

"testKey10": {

"propertyName": "testPara",

"propertyPath":"",

"containIn": "0"

}

}

},

"SecondViewController": {

"userDefined": {

"pageid": "4687",

"pagename": "SecondView頁面展示"

},

"pagePara": {

"testKey0": {

"propertyName": "age",

"propertyPath":"",

"containIn": "0"

},

"testKey1": {

"propertyName": "content",

"propertyPath":"",

"containIn": "0"

},

"testKey2": {

"propertyName": "propertyDic",

"propertyPath":"",

"containIn": "0"

},

"testKey3": {

"propertyName": "items",

"propertyPath":"",

"containIn": "0"

}

}

}

},

"TABLEVIEW": {

"ViewController/UITableView/0":{

"userDefined": {

"eventid": "201803074|93",

"target": "",

"pageid": "238",

"pagename": "tableview 被點擊"

},

"pagePara": {

"user_grade": {

"propertyName": "grade",

"propertyPath":"",

"containIn": "1"

}

}

}

},

"GESTURE": {

"ViewController/gesture1clicked:":{

"userDefined": {

"eventid": "201803074|93",

"target": "",

"pageid": "手勢1對應的id",

"pagename": "手勢1對應的page name"

},

"pagePara": {

"testKey1": {

"propertyName": "testPara",

"propertyPath":"",

"containIn": "0"

}

}

},

"ViewController/gesture2clicked:":{

"userDefined": {

"eventid": "201803074|93",

"target": "",

"pageid": "手勢2對應的id",

"pagename": "手勢2對應的page name"

},

"pagePara": {

"testKey2": {

"propertyName": "testPara",

"propertyPath":"",

"containIn": "0"

}

}

},

"SecondViewController/gesture3clicked:":{

"userDefined": {

"eventid": "201803074|98",

"target": "",

"pageid": "gesture3clicked",

"pagename": "手勢3對應的page name"

},

"pagePara": {

"user_age": {

}

}

}

}

}

json最外層有四個Key, 分別為 ACTION PAGEPV TABLEVIEW GESTURE, 分別對應 UIControl的點擊,頁面PV,tableview cell點擊, Gesture 單擊事件的參數。 每個key對應的value為json格式,Json中的keys, 即為唯一標識符。

標識符下的json有兩個key :

userDefine指的固定數據,即直接取值進行上報。

而pagePara為業務參數。pagePara對應的value也是一個json, json的keys,即上報的keys,value內的json包含三個參數:

propertyName為屬性名字,

containIn 參數只有0 ,1 兩種情況,用來區分類似Tableview的Cell里面按鈕的持有對象,看是要統計cell的點擊事件,還是cell里面的控件的點擊事件。

propertyPath是屬性路徑,有些不同的層級有相同的屬性名字,比如self.age 和 self.person.age,如果把propertyPath的值設為 person/age,取值的時候就會按照指定路徑進行取值。

從配置表來看,所有的hook事件都不再是一個寫死的id,而是從配置表里面拿的數據,解決了問題2提到的,由于不同的pageid導致無法Hook的瓶頸。這樣的配置表有2個好處,一是可以自由配置想統計的頁面,二是可以動態下發,只要配置正確,即使不發版也可以拿到線上版本的數據。

效果如下:

2018-12-12 15:04:18.373103+0800 ZPMStatisticsDemo[1435:199292]

事件唯一標識為:SecondViewController

pageid === 4687,

pagename === SecondView頁面展示,

pagepara === {

testKey0 = 30;

testKey1 = "Hello World";

testKey2 = {

key = 1;

};

testKey3 = (

a,

b,

2

);

}

有了這個配置表,頁面的In、Out,就可以通過Hook頁面的ViewAppear和ViewDisAppear來攔截埋點了,減少了大量重復埋點的時間。但是我們還有一個問題,頁面進出先后順序問題。

頁面進出順序

試想一個場景,我在JD頁點擊投遞簡歷按鈕,可視化埋點雖然記錄了點擊事件,我們做分析的時候,確無法得知用戶點擊這個投遞按鈕是通過什么方式進來的,是從搜索結果頁進入,還是首頁推薦,還是推送進入的。如果要知道這樣的進出先后,就需要進行頁面的傳遞。如果有一個方案,自動記錄頁面的進出堆棧順序,那么這個問題就迎刃而解了。

解決方案:

通過Hook viewWillAppear:方法來實現,具體代碼如下:

- (void)analysis_viewWillAppear:(BOOL)animated

{

[self analysis_viewWillAppear:animated];

NSString *identifier = [NSString stringWithFormat:@"%@", [self class]];

NSDictionary *dic = [[[ZPMDataContainer sharedInstance].data objectForKey:@"PAGEPV"] objectForKey:identifier];

NSString *pageInfo = [NSString stringWithFormat:@"%@, %@",[self getCurrentTimes], [self class]];

if (dic) {

NSString *pageid = dic[@"userDefined"][@"pageid"];

pageInfo = [pageInfo stringByAppendingFormat:@" ,%@",pageid];

}

[[ZPM_IO_Queue sharedInstance].queueArray addObject:pageInfo];

// 把頁面出現順序保存起來

if ([ZPM_IO_Queue sharedInstance].queueArray.count > 10) { // 這里只存10個頁面隊列

[[ZPM_IO_Queue sharedInstance].queueArray removeObjectAtIndex:0];

}

NSLog(@"queueArray:%@",[ZPM_IO_Queue sharedInstance].queueArray);

}

打印了一下日志:

2018-12-12 15:04:36.813738+0800 ZPMStatisticsDemo[1435:199292] queueArray:(

"2018-12-12 15:04:12, UINavigationController",

"2018-12-12 15:04:12, HomeViewController ,3156",

"2018-12-12 15:04:18, SecondViewController ,4687",

"2018-12-12 15:04:32, ThirdViewController",

"2018-12-12 15:04:36, SecondViewController ,4687"

)

從日志里可以看出,進出隊列包含時間、類名、和pageid,(UINavigationController代表的是這些頁面都是Navigation類型的,而ThirdViewController沒有pageid是因為配置表里沒有配)。這樣每個點擊事件的來源就一目了然了。

動態獲取自定義上報事件

試想這樣一個場景,即使解決了動態Pageid的問題,如果統計需求,要在不同頁面拿不同的參數,比如簡歷頁我要獲取簡歷id、簡歷編號、個人信息,JD頁我又要頁面數據、各個點擊事件。每個頁面要拿的參數都是不一樣的,那么豈不是即使解決了pageid不同的瓶頸,最后還是落得要手動代碼埋點的下場,因為每個頁面的上報的參數都是不一樣的。那這種情況下該如何解決呢?

一般來說有2種解決方案

第一種是代碼埋點,對于高度自定義的上報就是得用代碼來埋點,因為即使是像神策這樣的全埋點策略,也無法做到所有地方的精確埋點。

這里主要是介紹第二種方案,取參埋點法。簡單介紹一下什么是取參埋點,取參埋點其實就是利用Runtime,獲取一個類所有的property屬性,即成員變量,比如搜索結果頁的列表數據,是存放在一個叫listData的數組里的,那么通過Hook機制,動態拿到listData,就可以拿到里面的數據進行上傳操作。

這樣只需要通過配置表,添加自己想獲取的屬性數據,就能上報這樣的數據(前提是這個頁面有這樣的成員變量,局部變量的數據只能手動埋點了)。

取參埋點的部分代碼:

+ (BOOL)getVariableWithClass:(Class) myClass varName:(NSString *)name

{

unsigned int outCount, i;

Ivar *ivars = class_copyIvarList(myClass, &outCount);

for (i = 0; i < outCount; i++) {

Ivar property = ivars[i];

NSString *keyName = [NSString stringWithCString:ivar_getName(property) encoding:NSUTF8StringEncoding];

keyName = [keyName stringByReplacingOccurrencesOfString:@"_" withString:@""];

if ([keyName isEqualToString:name]) {

return YES;

}

}

return NO;

}

+ (id)captureVarforInstance:(id)instance varName:(NSString *)varName

{

unsigned int count;

objc_property_t *properties = class_copyPropertyList([instance class], &count);

// 檢測是否存在這個屬性

BOOL exit = [ZPMCapturePropertyTool getVariableWithClass:[instance class] varName:varName];

id value = nil;

if (exit) {

value = [instance valueForKey:varName];

}

if (!value) {

NSMutableArray *varNameArray = [NSMutableArray arrayWithCapacity:0];

for (int i = 0; i < count; i++) {

objc_property_t property = properties[i];

NSString *propertyAttributes = [NSString stringWithUTF8String:property_getAttributes(property)];

NSArray *splitPropertyAttributes = [propertyAttributes componentsSeparatedByString:@"\""];

if (splitPropertyAttributes.count < 2) {

continue;

}

NSString *className = [splitPropertyAttributes objectAtIndex:1];

Class cls = NSClassFromString(className);

NSBundle *bundle2 = [NSBundle bundleForClass:cls];

if (bundle2 == [NSBundle mainBundle]) {

// NSLog(@"自定義的類----- %@", className);

const char *name = property_getName(property);

NSString *varname = [[NSString alloc] initWithCString:name encoding:NSUTF8StringEncoding];

[varNameArray addObject:varname];

} else {

// NSLog(@"系統的類");

}

}

for (NSString *name in varNameArray) {

id newValue = [instance valueForKey:name];

if (newValue) {

value = [newValue valueForKey:varName];

if (value) {

return value;

}else{

value = [[self class] captureVarforInstance:newValue varName:varName];

}

}

}

}

return value;

}

總結

以上討論的方案主要是解決了提出的4個問題,盡量可以減少代碼的侵入性,以及以后的維護成本。同時可以動態更新埋點數據,而不需要通過發版的方式解決。但是以上方案也只是涵蓋了大部分場景, 并非所有場景都適用,具體大家可以根據業務情況來決定使用范圍。如果有更好的方案獲取提議,歡迎來騷擾。

總結

以上是生活随笔為你收集整理的ios无痕埋点_iOS可视化埋点方案的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲在线资源 | 亚洲免费中文 | 国产一区91精品张津瑜 | 香港三日本8a三级少妇三级99 | 超碰天堂| 成人欧美一区二区三区黑人一 | 国产精品一区二区黑人巨大 | 女同亚洲精品一区二区三 | 日本伦理片在线看 | 67194av| 日本在线激情 | 天堂在线中文网 | 毛片内射 | 国产一区91 | 日韩不卡高清 | 国产精品久久久久久无人区 | 男人用嘴添女人下身免费视频 | www.欧美在线 | 日本中文一区 | 黑人性高潮 | 桃色av| 妖精视频在线观看 | 日本人性爱视频 | 免费不卡的av | 成人综合一区二区 | 欧美裸体视频 | 奇米影视777在线观看 | 一级日韩片 | 国产在线一区不卡 | 交专区videossex另类 | xxxxx69| exo妈妈mv在线播放免费 | 免费黄色一区二区 | 全肉的吸乳文 | www.香蕉.com| 天堂成人国产精品一区 | 超碰夜夜 | 素人fc2av清纯18岁 | 天天操天天射天天 | 午夜特片网 | 亚洲av无码国产精品久久久久 | 夜夜夜夜爽 | 亚洲永久无码精品 | 明日花绮罗高潮无打码 | 日韩中文字幕免费 | 欧美成人69 | 在线观看亚洲成人 | 成人在线免费看视频 | 寡妇激情做爰呻吟 | 亚洲精品va | 国产 中文 字幕 日韩 在线 | 夜夜操夜夜干 | 亚洲精品喷潮一区二区三区 | 91视频综合网 | 视频一区二区三区精品 | 传媒一区二区 | 抱着老师的嫩臀猛然挺进视频 | 亚洲bb | 欧美激情在线观看视频 | 五月色婷 | 精品久久久久久久久中文字幕 | 人妻在线一区 | 国产乡下妇女做爰毛片 | 丝袜美女啪啪 | 精品视频在线观看 | 人妻互换一二三区激情视频 | 日韩操操 | 免费看黄色片的网站 | 亚洲精品一卡 | 亚洲精品视频大全 | 人妻丰满熟妇av无码久久洗澡 | 在线成人播放 | 在线观看日本一区二区 | 人妻少妇一区 | 国产成人精品综合久久久久99 | 91久久精品国产91久久性色tv | 午夜在线视频免费 | 美女的奶胸大爽爽大片 | np视频| 青青草综合 | 色中文字幕 | 久草福利资源在线观看 | 一区二区三区视频免费看 | 成年人网站在线免费观看 | 久久99精品久久久久久琪琪 | 欧美成人三级在线观看 | 久久久久久久久久久久国产精品 | 日韩av男人的天堂 | chinese hd xxxx tube麻豆tv | 色屁屁影院www国产高清麻豆 | 久久精品99国产精 | 中文字幕在线播放视频 | 亚洲免费精品 | 亚洲视频999 | 99热精品在线 | 欧美18—19性高清hd4k | 日韩高清在线一区 | 色网站免费在线观看 | 96精品视频 |