视频

[toc]

前言

一、视频上传

文件分片上传与分片下载.md

二、常见视频播放

1、视频地址/素材获取网站

mov_website1

2、m3u8文件认识

现在比较常见的视频流媒体,大部分都是 m3u8 格式的,而对于 m3u8 格式的视频而言,如果你下载过,你会发现它就是一个文本文件,大概也就只有几十 kb,从磁盘大小来看,应该也知道它并不是一个直接的视频文件。即如果我们想要下载对应的视频文件,直接下载 m3u8,当然是达不到目的的。

m3u8 是一种视频的播放格式,它是将一个完整的视频切割成多个 ts 后缀的视频,然后当我们的进度条被移动或者按时间顺序移动的时候,就会下载对应的片段来加载。

触发的方法在iOS中如下,其中 loadingRequest 每一小块数据的请求,请求地址如m3u8格式中的ts文件链接地址。

1
2
3
4
5
6
7
8
9
10
/*  连接视频播放和视频断点下载的桥梁
* 必须返回Yes,如果返回NO,则resourceLoader将会加载出现故障的数据
* 这里会出现很多个loadingRequest请求,需要为每一次请求作出处理
* 该接口会被调用多次,请求不同片段的视频数据,应当保存这些请求,在请求的数据全部响应完毕才销毁该请求
* @param resourceLoader 资源管理器
* @param loadingRequest 每一小块数据的请求
*/
- (BOOL)resourceLoader:(AVAssetResourceLoader*)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest*)loadingRequest{
// TODO:在这里面开始我们的网络下载请求,也就是得到AVAssetResourceLoadingRequest对象
}

实现代码摘自:KJPlayerDemo 其说明文档见: iOS 音频视频播放器实现边下载边播放缓存视频

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// 开始请求下载数据 
NS_INLINE void kStartDownloading(KJDownloader * downloader, AVAssetResourceLoadingRequest * request){
AVAssetResourceLoadingDataRequest *dataRequest = request.dataRequest;
NSInteger length = dataRequest.requestedLength;

NSInteger offset;
if (dataRequest.currentOffset != 0) {
offset = (NSInteger)dataRequest.currentOffset;
} else {
offset = (NSInteger)dataRequest.requestedOffset;
}
if (@available(iOS 9.0, *)) {
if (dataRequest.requestsAllDataToEndOfResource) {
[downloader kj_downloadTaskRange:NSMakeRange(offset, length) whole:YES];
return;
}
}
[downloader kj_downloadTaskRange:NSMakeRange(offset, length) whole:NO];
}

2.1、m3u8文件示例

m3u8文件示例1:ts片段是完整地址

1
2
3
4
5
6
7
8
9
10
11
#EXTM3U
#EXT-X-TARGETDURATION:19
#EXT-X-VERSION:2
#EXTINF:10,
http://122.225.31.106/6775ED76AE94E84581E2FA4D03/0300080B0053D05A49A22808DF700EC607AF80-D3EF-E08A-FB37-918809D8CF4C.mp4.ts?ts_start=0&ts_end=10&ts_seg_no=0
#EXTINF:10,
http://122.225.31.106/6775ED76AE94E84581E2FA4D03/0300080B0053D05A49A22808DF700EC607AF80-D3EF-E08A-FB37-918809D8CF4C.mp4.ts?ts_start=10&ts_end=20&ts_seg_no=1
…………..
#EXTINF:12,
http://122.225.31.85/657DD4BEC15488401753C7200C/0300080B0A53D05A49A22808DF700EC607AF80-D3EF-E08A-FB37-918809D8CF4C.mp4.ts?ts_start=320&ts_end=336&ts_seg_no=396
#EXT-X-ENDLIST

m3u8文件示例2:这里的片段,全部是基于域名的相对地址,

1
2
3
4
5
6
7
8
9
10
#EXTM3U
#EXT-X-VERSION:2
#EXT-X-MEDIA-SEQUENCE:102
#EXT-X-TARGETDURATION:12
#EXTINF:10,
57b3f432.ts
#EXTINF:12,
57b3f43c.ts
#EXTINF:9,
57b3f446.ts

2.2、m3u8文件字段说明

#EXTINF:10表示的是这段TS的时长是10秒。

57b3f432.ts这里表示的是每一个TS的文件名,有的M3U8这里直接是一个完成的http链接。

其他参考文章:

iOS解析M3U8文件及TS文件下载与合并

iOS 拼接m3u8文件 苹果m3u8怎么转换成mp4

1
self.playerUrl = @"http://cctv2.vtime.cntv.wscdns.com:8000/live/no/204_/seg0/index.m3u8?begintime=1469509516000";

附:边下边播

1、解码m3u8:拿到一个M3U8链接后可以解析出M3U8索引的具体内容,包括每一个TS的下载链接、时长等;

下载:拿到每一个TS文件的链接就可以逐个下载了,下载后存储为xxx.ts到手机里;
打包:将下载的TS数据按照播放顺序打包,供客户端播放;
播放:数据打包完成,就可以播放了。

Android——实现m3u8视频缓存

iOS流媒体开发之三:HLS直播(M3U8)回看和下载功能的实现

iOS 视频边下边播(缓存,预加载)

点播:

服务器将直播内容使用FFMPEG转码成MP4和3GP等点播源,生成播放连接返回给客户端播放就可以了。

2、播放器选择之路

音视频播放对于现在的互联网应用来说,已经是不可或缺的功能之一。作为一个 App 开发者,开发一个音视频播放功能,说难不难,说简单也不简单,我们常常会面临几个抉择:

  1. 使用原生视频组件(如:MediaPlayer)
  2. 使用原生硬解码/FFmpeg软解,定制视频播放组件
  3. 使用完全开源的第三方组件(如:ijkplayer)
  4. 使用商业第三方组件(如:腾讯云播放器,阿里云播放器)

2.1、腾讯云播放器

flutter_tencentplayer_plus

三、直播礼物播放

直播礼物类型详解

VAP动效实现方案

SVGA VS VAP

SVGA 是矢量动画方案,对简单动画支持更好。

VAP是视频方法,对复杂动画支持更好。相比SVGA,VAP具有更高的压缩率和硬件解码的优势,使得动画播放更加流畅。VAP的素材大小较小,仅为1.5M,远小于Apng和Webp格式,且采用硬件解码,解码速度更快。

VAP在文件大小和内存占用方面优于SVGA,这可以极大的节约文件包的空间并加快用户的读取速度

四、视频预加载

上中下三个player组成类似轮播的效果。播放当前视频时候,预加载下一个视频的数据。

五、视频帧

上下两个区域。页面下部分区域为视频的帧图,页面上部分为视频滑动到指定位置显示的画面,以此来形成滑动页面下部分滚动条时候,能实时看到视频帧的效果,实际最终帧为点击确认后才截取。

业务流程图:视频帧获取.graffle

六、视频播放暗掉

核心:

1
2
3
4
5
// 1.打开常亮
[[UIApplication sharedApplication] setIdleTimerDisabled:YES] ;

// 2.关闭长亮
[[UIApplication sharedApplication] setIdleTimerDisabled:NO] ;

视频解码

音频降噪

枚举值名称 枚举值 枚举类型描述
V2TIM_ELEM_TYPE_NONE 0 没有元素
V2TIM_ELEM_TYPE_TEXT 1 文本消息 V2TimTextElem
V2TIM_ELEM_TYPE_CUSTOM 2 自定义消息 V2TimCustomElem
V2TIM_ELEM_TYPE_IMAGE 3 图片消息 V2TimImageElem
V2TIM_ELEM_TYPE_SOUND 4 语音消息 V2TimSoundElem
V2TIM_ELEM_TYPE_VIDEO 5 视频消息 V2TimVideoElem
V2TIM_ELEM_TYPE_FILE 6 文件消息 V2TimFileElem
V2TIM_ELEM_TYPE_LOCATION 7 地理位置消息 V2TimLocationElem
V2TIM_ELEM_TYPE_FACE 8 表情消息 V2TimFaceElem
V2TIM_ELEM_TYPE_GROUP_TIPS 9 群 Tips 消息(存消息列表) V2TimGroupTipsElem
V2TIM_ELEM_TYPE_MERGER 10 合并消息 V2TimMergerElem

End

页面加载体验优化

[toc]

页面加载体验优化

一、页面初始加载优化

1、数据携带/数据参照

通过前一个页面的已获得数据,对所进入的新页面中的数据进行预填充。

eg1:商品列表 —> 商品详情:使用数据携带

eg2:愿望星count个数:通过count的0与非0,知晓所进入的页面初始状态更有可能是哪种状态

1、如果所进入的页面没有缓存数据,则携带的数据在进入的时候直接使用,后台接口返回实时数据后,再更新

2、如果所进入的页面有缓存数据,则携带的数据只能给缓存数据,而不能是后台接口返回的实时数据

2、管理的数据的缓存

2.1、普通数据的缓存

通过缓存框架,将数据缓存起来(key需携带uid),下次界面展示时候,优先从缓存中获取。

附:缓存框架详见:flutter_cache_kit使用文档

2.2、管理数据的缓存

建立Service层,管理用户所有数据的变动。

好处:与普通数据的缓存相比,能够在将来增加数据变动时候,通过本地通知系统,告知相关页面更新相应数据,而不用等到重新下载后才能显示已知道会更新的数据。

使用要点:

修改的时候,同步数据;下次界面需要数据,优先从用户管理服务中获取初始数据

eg1:用户愿望单收藏、商品收藏、品牌收藏、足迹数据

eg2:用户会员中心数据

3、默认数据

如果没有从前一页携带数据过来,则使用与产品约定的默认数据来加载。

1
2
3
if (携带数据) return 携带的.orderCount; 
if (管理数据) return servier.orderCount;
if (默认数据) return default_orderCount;

4、预览页:美团/饿了么的骨架屏灰底效果

对携带的数据与默认数据中相同的对象,使用携带的数据替换掉,一起整合成初始默认数据(携带的数据优先级高)。

5、添加请求加载圈

6、网络接口的缓存数据

页面处理优化及处理过程中的内容(图片)加载优化如下图所示:

图片来源可查看 《app启动与页面加载.graffle》中的【二、页面加载】版面

image-20240725001944956

从上图中,可以看出弱网情况下,我们可以针对不同的网络:做不同超时,重试设置,以及取不同质量的图片数据。

二、图片优化

图片优化,请查看我的另一篇文章:图片框架.md

图片高磁盘占用,请查看我的另一篇文章:《高磁盘占用的排查与优化.md

三、网络优化

api数据缓存,请查看我的另一篇文章:《网络框架.md

弱网优化,请查看我的另一篇文章:《弱网优化空间探索.md

四、按钮点击区域优化

eg:返回键的点击

防抖(立即执行):

五、样式

loading加载动画按需添加

返回按钮统一

toast提示统一

底部弹窗统一

六、底层异常处理

1、图片的404错误

错误示例:

https://img.alicdn.com/bao/uploaded/i1/1799996758/TB2YYnmoFXXXXatXFXXXXXXXXXX_!!1799996758.jpg

错误提示:

flutter: CacheManager: Failed to download file from https://img.alicdn.com/bao/uploaded/i3/791232018/TB2_zAjsVXXXXaNXXXXXXXXXXXX_!!791232018.jpg with error:

image-20220119162152615

修改方案:

Flutter cached_network_image图片缓存异常/加载失败优化

七、代码优化

屏幕适配写法

iOS 等比例UI适配方案

八、其他预加载优化

1、列表预加载

1.1、轮播图列表预加载

预加载下一张(不是全部)

2、视频预加载

3、全局WebView

End

Flutter代码开发规范

[toc]

Flutter代码开发规范

一、目录

目录结构请查看:项目目录结构规范

二、文件+类命名

1、类文件命名

查看dart源码发现,源文件名都是小写英文加上下划线组成,如app_bar.dart;

使用小写加下划线来命名库和源文件

1
2
3
4
5
// good 
app_bar.dart

// bad
AppBar.dart

2、资源文件命名

命名:功能名称 _ 控件类型 _ 自定义参数*(标准或者大字版).png

1
2
标准:share_btn_weixin.png
选中:share_btn_weixin_selected.png

三、类

1、class命名

一般情况下,类名是源文件名的大驼峰写法,如源文件app_bar.dart,类名AppBar;源文件bottom_app_bar,类名BottomAppBar;

1
2
3
class AppBar {

}

2、import

2.1、import顺序

为了使你的文件前言保持整洁,我们有规定的命令,指示应该出现在其中。每个“部分”应该用空行分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
// ①dart库
import 'dart:async';
import 'dart:html';

// ②三方库
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

// ③项目文件
import 'package:my_package/util.dart';

// ④相对文件
export 'src/error.dart';

2.2、import as

使用小写加下划线来命名导入前缀

1
2
3
4
5
6
7
8
9
// good
import 'dart:math' as math;
import 'package:angular_components/angular_components'
as angular_components;

// bad
import 'dart:math' as Math;
import 'package:angular_components/angular_components'
as angularComponents;

3、构造函数

3.1、构造函数定义

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
class Point {
num x, y;

// good
Point2(this.x, this.y);

// bad
Point2(num x, num y) {
this.x = x;
this.y = y;
}
}

3.2、构造函数设计技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

class WishPublishPage extends BJHBasePage {
final bool isUpdateBusiness;
final WishDetailModel wishDetailModel;
final bool reEditPublish;
final Function backCallBack;// 从哪个页面跳转过来的 用于区分游戏中心的跳转

WishPublishPage({
Key key,
this.isUpdateBusiness = false,
this.reEditPublish = false,
this.wishDetailModel,
this.backCallBack,
}) : super(key: key);

WishPublishPage.fromOtherUser({
Key key,
this.reEditPublish = false,
this.isUpdateBusiness = false,
this.wishDetailModel,
List<WishGood> selectGoodsList,
this.backCallBack,
}) : super(key: key) {
wishDetailModel.wishGoods = selectGoodsList; // 暂时只平移代码,不修改(原本可能有问题)
}

@override
_WishPublishPageState createState() => _WishPublishPageState();
}

四、其他命名

1、常量、变量命名

使用小驼峰法命名

1.1、常量

1
2
3
const num pi = 3.14;
const int defaultTimeout = 1000;
final RegExp urlScheme = RegExp('^([a-z]+)

1.2、变量

1
2
3
List<String> bookList;
Map<String, dynamic> bookMap;
bool isShowDetail; // 使用is开头+具体的业务场景命名

非特殊需要,禁止使用dynamic。(无法发挥空安全优势)

1
2
3
4
5
6
7
8
9
10
// 错误代码
dynamic string1 = sp.getString();
if ( string1.length == 2 )

// bugly报错
string1 出现 null

// 正确修改
String? string1 = sp.getString();
if ( string1 != null && string1!.length == 2 )

1.3、枚举命名

1
2
3
4
5
/* 枚举的用途 */
enum Direction {
top, //类型的用途 xxxx
left,//类型的用途 xxxx
}

五、基础接口使用规范

判空(不使用length)

1
2
3
4
5
6
7
// good: isEmpty/isNotEmpty
if (lunchBox.isEmpty) return 'so hungry...';
if (words.isNotEmpty) return words.join(' ');

// bad:length
if (lunchBox.length == 0) return 'so hungry...';
if (!words.isEmpty) return words.join(' ');

End

代码编写技巧

[toc]

代码编写技巧

1
2
3
4
5
self.selectedBackgroundView = ({
UIView *view = [UIView new];
view.backgroundColor = [UIColor colorWithRed:244/255.0 green:244/255.0 blue:244/255.0 alpha:1.0]; //#f4f4f4
view;
});

枚举转 int AvatarStatus

协变和逆变 ?= 策略

在Dart1.x的版本种是既支持协变又支持逆变,但是在Dart2.x版本开始仅支持协变。所以后面我们就不再讨论Dart的逆变。

1
2
3
4
5
6
7
8
9
class PrintMsg<T> {

T _msg;

set msg(T msg) { this._msg = msg; }

void printMsg() { print(_msg); }

}

T是一个抽象类型,因此无法直接使用T(a: "hello")来创建实例。我们需要稍微修改一下代码,以便在test()方法中正确创建T的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
abstract class A {
String? a;
A({this.a});
}

class A1 extends A {
A1({String? a}) : super(a: a);
}

class A2 extends A {
A2({String? a}) : super(a: a);
}

class B<T extends A> {
// 生成泛型T的实例ben。即达到B<A1>().test(); 输出A1,而B<A2>().test(); 输出A2
T createInstance() {
if (T == A1) {
return A1(a: "hello") as T;
} else if (T == A2) {
return A2(a: "world") as T;
} else {
throw Exception("Unsupported type");
}
}

void test() {
T bean = createInstance();
print(bean.runtimeType); // 输出实例的运行时类型
}
}

void main() {
B<A1>().test(); // 输出: A1
B<A2>().test(); // 输出: A2
}

埋点方案-页面和视图的显示和离开

[toc]

埋点方案-页面和视图的进入和离开

一、页面进入、离开的监控方案

视图创建基类 LifeCyclePage,使用 route 方法,并提供以下方法

1
2
3
4
5
// 当前页面显示了,底层必须先执行 super.viewDidAppear
void viewDidAppear(AppearBecause appearBecause)

// 当前页面消失了,底层必须先执行 super.viewDidDisappear
void viewDidDisappear(DisAppearBecause disAppearBecause);

二、视图进入、离开的监控方案

场景:运行需要知道页面下的tab、页面下的tab的《子tab/子视图》的浏览时间。

方案1:点击–view_show/hide–不推荐

使用点击 view_show/hide 方案,实现步骤如下:

点击tab1:记tab1 view_show 事件,并记下当前显示的tab,备后续切换的时候为本tab添加 view_hide 事件。

点击tab2:记tab1 view_hide 事件,tab2 view_show 事件,并记下当前显示的tab,备后续切换的时候为本tab添加 view_hide 事件。

缺点:tab点击时候,记下当前显示的tab,备后续切换的时候为本tab添加 view_hide 事件。

方案2:视图是否显示监控–visibility–推荐方案

视图创建基类 LifeCycleView,使用 visibility 方案,并提供以下方法

1
2
3
4
5
// 当前页面显示了,底层必须先执行 super.viewDidAppear
void viewDidAppear(AppearBecause appearBecause)

// 当前页面消失了,底层必须先执行 super.viewDidDisappear
void viewDidDisappear(DisAppearBecause disAppearBecause);

2.1、原理:创建 VisibilityDetector 视图类,并设置 onVisibilityChanged 回调方法;

1
2
3
4
5
6
7
const VisibilityDetector({
required Key key,
required Widget child,
required this.onVisibilityChanged,
}) : assert(key != null),
assert(child != null),
super(key: key, child: child);

优点:不用每个视图(如tab页)去处理。

总结

页面和视图的进入和离开方案,总结如下:

序号 类型 方案 优点与不足
1 页面 LifeCyclePage + route 能比view增加具体显示和离开的原因
2 页面内的视图 LifeCycleView + visibility 只能知道显示、还是离开

End

埋点规范

[toc]

埋点规范

参考文档:

一、埋点入参

1、【固定的】公共入参

1.1、【固定的】的【基本】公共入参7个

建议和《基础规范-请求规范》 中的request公共入参保持一致

属性名 说明 备注
app_id App的唯一标识 com.ciyouzen.beyond
app_name App的应用名称 XXX测试版
app_version App的应用版本 1.16.30
app_buildNumber App的应用编译号 16301610
channel_name 渠道名称 《请求规范》 中的request公共入参的渠道】
platform/lib SDK 类型 Android/iOS
account_id 用户唯一标识 用户没有登录时,客户端自动生成唯一标识

1.2、【固定的】的【设备及系统信息】公共入参

属性名 说明 备注
systemVersion/lib_version 操作系统版本 16.1.1
manufacturer 设备制造商
brand 设备品牌 iPhone
model 设备型号 iPhone
os 操作系统 iPhone13,3
os_version 操作系统版本
screen_width 屏幕宽度 390.0
screen_height 屏幕高度 844.0
screen_devicePixelRatio 设备像素比率 3.0
carrier SIM 卡的运营商名称
device_id 设备ID Android 端主要取 Android ID ,
iOS 端先尝试获取 IDFA,如果获取不到,则取 IDFV
geo_coordinate_system 坐标系:iOS 端默认为 WGS84, Android 端需要手动传入 bd09ll

1.3、【固定的】的【其他】公共入参

属性名 说明 备注
lib_method 埋点方式 固定值为code

2、【可变的】的Body入参

2.1、单记录Body:【可变的】的【基本】入参

属性名 说明 备注 是否可全局按需时时获取
network_type 网络类型
mobile / wifi / bluetooth / vpn / other
mobile
timezone App 或系统的时区
Duration timezoneOffset = DateTime.now().timeZoneOffset;
Asia/Shanghai
timezone_offset 时区偏移量(以分为单位)
int timezoneOffsetMinutes = timezoneOffset.inMinutes;
480
latitude GPS信息-纬度
longitude GPS信息-经度
request_time 单条记录生成时间,精确到毫秒 批量时,多条应是同一个时间

2.2、单记录Body:【可变的】的【其他】入参

属性名 说明 备注
param 服务端下发 如算法标识等
event_name 事件名称 如“AppInstall”
event_attr 事件属性对象集合 event_name=”AppInstall”,event_attr={“accountId”:”1234”,”city”:”厦门”}

1、event_attr 希望的其他参数

属性名 说明 备注 是否可全局按需时时获取
cur_page 当前页面,用于标明 position/scene “goods_detail_page” 可全局记录旧页面
cur_view 当前视图,用于标明 position/scene “sku_choose_window” 必须请求单独设置
referrer_title 前一个页面标题

二、埋点事件

1、App事件

事件描述 事件名
冷启动 appStart
关闭应用 appEnd
返回前台/热启动 appResumed
进入后台 appPaused

1.1、打开/冷启动 appStart

参数 说明 示例
—- —-

1.2、关闭应用 appEnd

参数 说明 示例
—- —-

1.3、返回前台/热启动 appResumed

参数 说明 示例
—- —-

1.4、进入后台 appPaused

参数 说明 示例
duration 页面停留的时长(毫秒)

2、页面停留事件

事件描述 事件名
显示页面 viewDidAppear
离开页面 viewDidDisappear
1
2
3
4
5
enum AppearBecause {
newCreate, // 新显示
pop, // 从其他界面pop回来的
resume, // 从后台进入前台
}
1
2
3
4
5
enum DisAppearBecause {
goNew, // 去新的页面
pop, // 退出当前界面
pause, // 从前台进入后台
}

2.1、原生页面停留

2.1.1、进入页面 viewDidAppear
参数 示例 其他
page 页面类名 Goods_detail_page
cause 进入的方式 newCreate / pop
2.1.2、离开页面 viewDidDisappear
参数 说明 示例
page 页面类名 Goods_detail_page
cause 离开的方式 goNew / pop
duration 页面停留的时长(毫秒)
2.1.3、页面/视图进入、离开的监控方案

页面/视图进入、离开的监控方案:ShowMonitorMethod,详见《埋点方案-页面和视图的进入和离开

2.2、游戏页面停留

2.2.1、进入页面 viewDidAppear
参数 示例 其他
page 页面类名 WebViewPage
cause 进入的方式 newCreate / pop
url 网页地址
2.2.2、离开页面 viewDidDisappear
参数 说明 示例
page 页面类名 WebViewPage
cause 离开的方式 newCreate / pop
duration 页面停留的时长
url 网页地址

3、页面加载事件

事件描述 事件名
页面加载开始 firstStart
页面首帧绘制结束 firstAppFrameEnd
页面首屏绘制结束
(取到数据即算,不论是后台还是网络)
firstUserScreenEnd
页面首屏绘制结束
(以相关请求的最后一个为结束为结束)
firstNetworkScreenEnd

3.1、页面加载开始 firstStart

参数 示例 其他
page 页面类名 GoodsDetailPage

3.2、页面首屏绘制结束 firstNetworkScreenEnd

参数 说明 示例
page 页面类名 GoodsDetailPage
requestCount 请求的网络个数 3
duration 页面加载的时长(毫秒)

4、点击与曝光事件(非页面)

描述 点击事件名 曝光事件名 属性示例
商品 click_goods exposure_goods {“id”: id, “sku_id”: sku_id}
用户 click_user exposure_user {
“userId”: userId,
“userType”: avatar/name/other
}
订单 click_order exposure_order {“id”: id}
卡片 click_card exposure_card {
“cardId”: cardId,
“cardType”: user/goods/banner,
“cardOwnerId”: cardOwnerId
}
tab(一个页面多tab) click_tab
(使用该tab的视图曝光)
{“tab_name”: tab_name}
照片切换 click_photo_change 无(不需要) {“count”: 3, “old_index”: 0, “cur_index”: 2}
照片浏览 click_photo_browse 无(不需要) {count”: 3, “index”: 0}

4.1、卡片参数

参数 说明 示例
cardPosition/cardScene 卡片位置/场景
cardType 卡片类型(banner、user、goods)
cardId 卡片id
cardOwnerId 卡片持有者id(可空)

四、全埋点

参考文章:

1、点击自动埋点 autoTrackClick

参数 示例 其他
path 按钮路径
content 按钮上的文本,如果存在
screentName 当前页面名,同于page,却值不同

四、埋点页面名

1、一级页

页面描述 页面类名 页面埋点名
主页
作品首页 WishHomePage WishHomePage
商城首页 MallHomePage MallHomePage
发布页面
消息首页
我的首页

1.1、一级页中的标签页

页面描述 页面类名 页面埋点名
作品–推荐
作品–同城 WishDiscoverNearby WishDiscoverNearby
作品–热单 WishDiscoverHot WishDiscoverHot
作品–关注 WishDiscoverFriend WishDiscoverFriend

2、二级页

2.1、作品相关

页面描述 页面类名 页面埋点名
作品–推荐–推荐用户
作品–同城–
作品–热单–获豆榜 RankBeanWidget RankBeanWidget
作品–热单–愿望榜 RankWishWidget RankWishWidget
作品–关注–关注/粉丝/互关
作品详情页

2.2、商品相关

页面描述 页面类名 页面埋点名
商品详情页 GoodsTaDetailPage GoodsTaDetailPage

2.3、发布相关

页面描述 页面类名 页面埋点名
添加商品页

2.4、消息相关

页面描述 页面类名 页面埋点名
消息–收到的打赏 MsgCostPage MsgCostPage
消息–实现的愿望 MsgWishPage MsgWishPage
消息–新增的关注 MsgFocusPage MsgFocusPage
消息–互动消息–收到的评论 MsgCommentPage MsgCommentPage
消息–互动消息–发表的评论 InteractiveMessageListPage InteractiveMessageListPage
消息–互动消息–赞过的评论 InteractiveMessageListPage InteractiveMessageListPage
消息–系统消息
消息–聊天页面

2.5、我的相关

页面描述 页面类名 页面埋点名
我的–关注 MyFriendsPage MyFriendsPage
我的–粉丝 MyRelationPage MyRelationPage
我的–愿望豆 StarHistoryPage StarHistoryPage
我的–我的农场
我的–我的订单 MyOrderMainPage MyOrderMainPage
我的–我的足迹 RecordPage RecordPage
我的–我的收藏 CollectPage CollectPage

3、其他

页面描述 页面类名 页面埋点名
意见反馈 FeedbackPage FeedbackPage
平台客服 APPIMChat APPIMChat

End

瘦身-③app安装包

[toc]

一、查看包大小

序号 类型 描述 其他
1 下载大小 此值为通过无线下载的压缩 App 大小。
2 安装大小 此值为此 App 将在用户设备上占用的磁盘空间大小。

1、查看iOS包大小

1、进入官网 https://appstoreconnect.apple.com/apps

2、选择指定app –> TestFilght –> 指定版本 –> 指定构建号

3、查看内容如下

app_package_size_lookup_ios

二、如何瘦身

iOS APP瘦身优化

Flutter 如何缩减接近 50% 的 Flutter 包体积

三、项目示例

assets_generator

Python3安装xlsxwriter