others-AppsFlyer第三方统计

others-AppsFlyer第三方统计


前篇


SDK 接入

  1. build.gradle 引入库

    1
    2
    3
    4
    5
    dependencies {
    implementation 'com.appsflyer:af-android-sdk:5.4.0'
    implementation 'com.google.android.gms:play-services-ads-identifier:17.0.0'
    implementation 'com.android.installreferrer:installreferrer:2.1'
    }
  2. 权限 及 广播 的 清单配置 AndroidManifest.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <manifest>
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!-- Optional : -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application>
    <!-- appsflyer -->
    <receiver
    android:name="com.appsflyer.SingleInstallBroadcastReceiver"
    android:exported="true">
    <intent-filter>
    <action android:name="com.android.vending.INSTALL_REFERRER" />
    </intent-filter>
    </receiver>
    </application>
    </manifest>
  3. 混淆配置 proguard-user.txt

    1
    2
    3
    4
    ######### appsflyer #########
    -keep public class com.android.installreferrer.** { *; }
    -keep class com.appsflyer.** { *; }
    -dontwarn com.android.installreferrer
  4. java 代码

    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    package com.yang.androidaar.other;

    import android.content.Context;
    import android.util.Log;

    import com.appsflyer.AppsFlyerConversionListener;
    import com.appsflyer.AppsFlyerLib;
    import com.yang.androidaar.Define;
    import com.yang.androidaar.JsonTool;
    import com.yang.androidaar.MyCode.ECode;
    import com.yang.androidaar.Tools;
    import com.yang.androidaar.model.JsonBean.CEventInfo;

    import java.util.HashMap;
    import java.util.Map;

    public class AppsflyerHelper {

    static AppsflyerHelper instance = null;
    public static AppsflyerHelper getIns() {
    if (instance == null) {
    instance = new AppsflyerHelper();
    }
    return instance;
    }

    private static final String TAG = "--- AppsflyerHelper";
    private String mAfJson = null;
    private boolean mIsCall = false;

    public void init(Context context, boolean isDebug, Define.JsonRunnable task) {
    try {
    // af 回调
    innerInit(context, isDebug, (code, afJson) -> {
    if (code != ECode.Ok) {
    return;
    }
    mAfJson = afJson;
    });

    } catch (Exception e) {
    e.printStackTrace();
    callTask(task);
    }
    }

    private void innerInit(Context context, boolean isDebug, Define.CodeRunnable task) {
    String devKey = Tools.GetStringVaule(context, "af_dev_key"); // 获取 af 后台的秘钥
    AppsFlyerConversionListener conversionListener = new AppsFlyerConversionListener() {

    @Override
    public void onConversionDataSuccess(Map<String, Object> conversionData) {
    // 转化回调
    task.run(ECode.Ok, JsonTool.toJson(conversionData));
    }

    @Override
    public void onConversionDataFail(String errorMessage) {
    task.run(ECode.LoginError, errorMessage);
    }

    @Override
    public void onAppOpenAttribution(Map<String, String> attributionData) {
    // 深度连接的回调: https://support.appsflyer.com/hc/zh-cn/articles/213766183-%E5%BC%80%E5%8F%91%E8%80%85Unity-Plugin-V4%E5%AF%B9%E6%8E%A5%E6%8C%87%E5%8D%97#%E6%A0%B8%E5%BF%83api
    Log.d(TAG, "onAppOpenAttribution: ");
    }

    @Override
    public void onAttributionFailure(String errorMessage) {
    Log.d(TAG, "onAttributionFailure: ");
    }
    };

    // af 初始化, 在 onCreate 的时候就调用
    AppsFlyerLib.getInstance().init(devKey, conversionListener, context);
    AppsFlyerLib.getInstance().startTracking(context);
    AppsFlyerLib.getInstance().setDebugLog(isDebug);
    }

    public void logEvent(Context context, String jsonMsg, Define.CodeRunnable task) {
    CEventInfo ei = JsonTool.toObject(jsonMsg, CEventInfo.class);
    logEvent(context, ei, task);
    }

    public void logEvent(Context context, CEventInfo ei, Define.CodeRunnable task) {
    if (ei == null || ei.name.length() == 0) {
    return;
    }
    AppsFlyerLib.getInstance().trackEvent(context, ei.name, ei.params);
    }

    public String getInfo() {
    Map<String, Object> args = new HashMap<>();
    args.put("AfJson", mAfJson);
    return JsonTool.toJson(args);
    }

    private void callTask(Define.JsonRunnable task) {
    if (mIsCall || task == null) {
    return;
    }

    mIsCall = true;
    task.run(mAfJson);
    }
    }

非自然量测试

  1. sdk 对接测试 中, 点击 非自然量测试

    image-20240206103503341

  2. 选择 加白 设备 (没有加白的设备要先加白), 然后选择 other

    image-20240206103707302

  3. 二维码生成后, 扫描二维码跳转到目的连接即可

  4. 卸载再安装 apk, 直接启动, 就能获取到 非自然流量 的归因数据

    然后 af 面板上就会自动显示 非自然量 测试成功

    image-20240206104127895


自定义短链配置

这个的短板是: 如果点击链接先跳转到浏览器的 gp 商店, 在点击打开商店进入商店里面下载, 就会丢失归因数据. 这个 adjust 做的就比较好, 不会丢失归因参数.

Google Play 应用的短链推广

  1. 后台配置

  2. onConversionDataSuccess 中可以获取到对应的参数


Facebook Ads 投放

官方说明获取归因数据必须 接收 服务条款 - https://support.appsflyer.com/hc/zh-cn/articles/207032096-Deferred-Deeplinking-Getting-the-Conversion-Data#%E7%AE%80%E4%BB%8B

需要 Facebook 对应的 app id 接收 服务条款, 才能在 af 的 onConversionDataSuccess 回调中, 接收到 campaign 相关数据

链接: https://www.facebook.com/ads/manage/advanced_mobile_measurement/app_based_tos?appid=123123 (123123 是 app id)


api 上报事件

官方文档: 适用于移动设备的服务器到服务器事件API

py 测试代码

  • 测试用例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # appsflyer
    def test_afEvent(self):
    pkgName = "com.aaa.bbb" # 安卓包名
    secret = "axYvBxxxxxxxxxxxxx" # dev key
    afuid = "1602570356839-6524xxxxxxxxxxxxxx" # af uid

    url = "https://api2.appsflyer.com/inappevent/{}".format(pkgName)
    headers = {
    "authentication": secret,
    "Content-Type": "application/json",
    }

    data = {
    "appsflyer_id": afuid, # af uid
    "eventName": "test_yx01_af_purchase", # 事件
    "eventCurrency": "INR", # 币种
    "eventValue": utils.beautyJson({
    "af_revenue": "100", # 收益
    }, indent=None),
    }

    code, rspDct = utils.httpPost(url, dct=data, headers=headers)
    print("--- code: {}".format(code))
    print("--- rsp: {}".format(utils.beautyJson(rspDct)))

第三方数据服务条款


实时回传

在 af 后台配置回传 url 及 需要回传的字段, 在 Integration -> API Access

  • 回传的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        {
    "af_c_id": "127021111111",
    "appsflyer_id": "1622000030319-803339034811111111",
    "campaign": "Daji1_1250_040801",
    "event_time": "2021-05-26 03:33:53.998",
    "af_channel": "ACI_Search",
    "app_id": "com.aaa.bbb",
    "event_name": "install",
    "advertising_id": "1eac9954-cfa4-4a45-a4c0-aaaaaa"
    }



    附:

    - 比较有用的几个信息, 勾选 *激活* 行了

    ```json
    "{\"af_c_id\":\"13536068592\",\"app_name\":\"Rummy Winner Online - 13 Cards\",\"appsflyer_id\":\"1624014963149-589936205098832861\",\"campaign\":\"Daji_1254_0617\",\"event_time\":\"2021-06-18 11:16:10.676\",\"af_channel\":\"ACI_Display\",\"app_id\":\"com.quencheddwfkcv.politicianslijohqe\",\"event_name\":\"install\",\"advertising_id\":\"79bd45f4-66c1-45e6-a617-a030dfb5e46a\"}"


iOS 接入


生成 api/s2s token


Pull Api

Organic Installs

  • https://dev.appsflyer.com/hc/reference/get_app-id-organic-installs-report-v5

  • 示例:

    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
        curl --request GET \
    --url 'https://hq1.appsflyer.com/api/raw-data/export/app/com.aaa.bbb/organic_installs_report/v5?from=2024-05-08&to=2024-05-10&additional_fields=blocked_reason_rule,store_reinstall,impressions,contributor3_match_type,custom_dimension,conversion_type,gp_click_time,match_type,mediation_network,oaid,deeplink_url,blocked_reason,blocked_sub_reason,gp_broadcast_referrer,gp_install_begin,campaign_type,custom_data,rejected_reason,device_download_time,keyword_match_type,contributor1_match_type,contributor2_match_type,device_model,monetization_network,segment,is_lat,gp_referrer,blocked_reason_value,device_category,app_type,rejected_reason_value,ad_unit,keyword_id,placement,network_account_id,install_app_store,amazon_aid,att,engagement_type,contributor1_engagement_type,contributor2_engagement_type,contributor3_engagement_type' \
    --header 'accept: text/csv' \
    --header 'authorization: Bearer aaa'



    ---

    ### 踩坑

    #### 链路验证失败

    - 报错: `Chain validation failed`
    - 原因是因为手机系统时间不正确, 比如把系统时间调到了未来 几天/几个月 之后, 就会出现这样的问题, 解决办法就是把系统时间调回正确的时间

    - 参考: https://blog.csdn.net/m0_37609239/article/details/123893566



    ---

    #### 置到后台返回来回重复调用

    - 原因是 AppsFlyerConversionListener 里的回调会回调回来

    - 解决办法, 回调后取消回调即可

    ```json
    AppsFlyerLib.getInstance().unregisterConversionListener();