title: others-Adjust第三方统计
categories: Others
tags: [others, Adjust, 统计]
date: 2020-09-21 14:13:14
comments: false
mathjax: true
toc: true
others-Adjust第三方统计
前篇
- 官网 - https://www.adjust.com/
- Android 集成文档: https://docs.adjust.com/zh/sdk/android/
- 接入文档: https://github.com/adjust/android_sdk
接入
-
build.gradle 引入库
dependencies { implementation 'com.adjust.sdk:adjust-android:4.24.1' implementation 'com.google.android.gms:play-services-ads-identifier:17.0.0' implementation 'com.android.installreferrer:installreferrer:2.1' }
-
权限 及 广播 的 清单配置 AndroidManifest.xml
<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> <!-- adjust --> <receiver android:name="com.adjust.sdk.AdjustReferrerReceiver" android:permission="android.permission.INSTALL_PACKAGES" android:exported="true" > <intent-filter> <action android:name="com.android.vending.INSTALL_REFERRER" /> </intent-filter> </receiver> </application> </manifest>
-
混淆配置 proguard-user.txt
######### adjust ######### -keep class com.adjust.sdk.** { *; } -keep class com.google.android.gms.common.ConnectionResult { int SUCCESS; } -keep class com.google.android.gms.ads.identifier.AdvertisingIdClient { com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context); } -keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info { java.lang.String getId(); boolean isLimitAdTrackingEnabled(); } -keep public class com.android.installreferrer.** { *; }
-
java 代码
package com.ecological.auctioneer.other; import android.app.Activity; import android.app.Application; import android.content.Context; import android.os.Bundle; import android.util.Log; import com.adjust.sdk.Adjust; import com.adjust.sdk.AdjustConfig; import com.adjust.sdk.AdjustEvent; import com.adjust.sdk.LogLevel; import com.ecological.auctioneer.Define; import com.ecological.auctioneer.JsonTool; import com.ecological.auctioneer.LogUtil; import com.ecological.auctioneer.MyCode.ECode; import com.ecological.auctioneer.SystemInfoUtil; import com.ecological.auctioneer.Tools; import com.ecological.auctioneer.model.JsonBean.CEventInfo; import com.ecological.auctioneer.model.JsonBean.CTransfer; import java.util.ArrayList; import java.util.List; public class AdjustHelper { static AdjustHelper instance = null; public static AdjustHelper getIns() { if (instance == null) { instance = new AdjustHelper(); } return instance; } private static final String TAG = "--- AdjustHelper"; private static final String ResKey_Token = "adjust_token"; private static final String ResKey_Secret = "adjust_secret"; private boolean mIsCall = false; private CTransfer mTransfer = new CTransfer(); // 保存数据 // // ---------------- 测试字段 // private String mAttribution = "attr: "; // private String mEventTrackingSucceeded = "ets: "; // private String mEventTrackingFailed = "etf: "; // private String mSessionTrackingSucceeded = "sts: "; // private String mSessionTrackingFailed = "stf: "; public void init(Activity activity, boolean isDebug, Define.TransferRunnable task) { if (isDebug) { Log.d(TAG, "AdjustHelper debug mode"); } String token = Tools.GetStringVaule(activity, ResKey_Token); if (!Tools.isEmpty(token)) { // ad 回调 initAdjust(activity, token, isDebug, (code, adJson) -> { if (code != ECode.Ok) { return; } mTransfer.AdJson = adJson; LogUtil.TD(TAG, "--- initAdjust ok, AdJson: %s", mTransfer.AdJson); callTask(task); }); // referer 回调 initReferer(activity, (code, msg) -> { if (code != ECode.Ok) { return; } mTransfer.GgJson = Tools.parseUrl2Json(msg); LogUtil.TD(TAG, "--- initReferer ok 111, GgJson: %s", mTransfer.GgJson); // callTask(task); 不能 callTask, 要等 onConversionDataSuccess 回调 }); } else { initReferer(activity, (code, msg) -> { if (code != ECode.Ok) { return; } mTransfer.GgJson = Tools.parseUrl2Json(msg); LogUtil.TD(TAG, "--- initReferer ok 222, GgJson: %s", mTransfer.GgJson); callTask(task); }); } } private void initAdjust(Activity activity, String token, boolean isDebug, Define.CodeRunnable task) { String secret = Tools.GetStringVaule(activity, ResKey_Secret); List<Long> infoLst = parseSecret(secret); String environment = AdjustConfig.ENVIRONMENT_PRODUCTION; if (isDebug) { environment = AdjustConfig.ENVIRONMENT_SANDBOX; } AdjustConfig config = new AdjustConfig(activity, token, environment); config.setAppSecret(infoLst.get(0), infoLst.get(1), infoLst.get(2), infoLst.get(3), infoLst.get(4)); config.setLogLevel(isDebug ? LogLevel.VERBOSE : LogLevel.ERROR); // disable warning logs config.setSendInBackground(true); // -------- 注册各种回调 // Set attribution delegate. config.setOnAttributionChangedListener(attribution -> { LogUtil.TD(TAG, "Attribution: " + attribution.toString()); task.run(ECode.Ok, JsonTool.toJson(attribution)); }); // Set event success tracking delegate. config.setOnEventTrackingSucceededListener(eventSuccessResponseData -> { LogUtil.TD(TAG, "Event success data: " + eventSuccessResponseData.toString()); }); // Set event failure tracking delegate. config.setOnEventTrackingFailedListener(eventFailureResponseData -> { LogUtil.TD(TAG, "Event failure data: " + eventFailureResponseData.toString()); }); // Set session success tracking delegate. config.setOnSessionTrackingSucceededListener(sessionSuccessResponseData -> { LogUtil.TD(TAG, "Session success data: " + sessionSuccessResponseData.toString()); }); // Set session failure tracking delegate. config.setOnSessionTrackingFailedListener(sessionFailureResponseData -> { LogUtil.TD(TAG, "Session failure data: " + sessionFailureResponseData.toString()); }); // Evaluate deferred deep link to be launched. config.setOnDeeplinkResponseListener(deeplink -> { LogUtil.TD(TAG, "Deep link URL: " + deeplink); return true; }); Adjust.onCreate(config); activity.getApplication().registerActivityLifecycleCallbacks(new AdjustLifecycleCallbacks()); } private void initReferer(Context context, Define.CodeRunnable task) { GoogleReferrerHelper.getIns().start(context, task); } public static List<Long> parseSecret(String secret) { List<Long> numLst = new ArrayList<>(); try { String[] idArr = secret.split(","); for (String idStr : idArr) { long num = Long.parseLong(idStr.trim()); numLst.add((num)); } } catch (Exception e) { e.printStackTrace(); } LogUtil.TA(TAG, numLst.size() == 5, "--- parseSecret error, size: %d", numLst.size()); return numLst; } private static final class AdjustLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { } @Override public void onActivityStarted(Activity activity) { } @Override public void onActivityResumed(Activity activity) { Adjust.onResume(); } @Override public void onActivityPaused(Activity activity) { Adjust.onPause(); } @Override public void onActivityStopped(Activity activity) { } @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { } @Override public void onActivityDestroyed(Activity activity) { } } public void logEvent(String jsonMsg, Define.CodeRunnable task) { if (SystemInfoUtil.ThirdSdk != Define.EThirdSdk.Adjust) { return; } CEventInfo ei = JsonTool.toObject(jsonMsg, CEventInfo.class); logEvent(ei, task); } public void logEvent(CEventInfo ei, Define.CodeRunnable task) { if (ei == null || ei.name.length() == 0 || SystemInfoUtil.ThirdSdk != Define.EThirdSdk.Adjust) { return; } LogUtil.TD(TAG, "--- event: %s", JsonTool.beauty(ei)); AdjustEvent adjustEvent = new AdjustEvent(ei.name); if (ei.valueToSum > 0.001f) { String curr = "INR"; if (ei.params.size() > 0 && ei.params.containsKey("currency")) { curr = (String) ei.params.get("currency"); } adjustEvent.setRevenue(0.01, curr); } Adjust.trackEvent(adjustEvent); } public String getInfo() { return JsonTool.toJson(mTransfer); } // public void showMsg(Context ctx) { // CTips tps = new CTips(); // tps.msg = String.format("%s\n%s\n%s\n%s\n%s" // , mAttribution, mEventTrackingSucceeded, mEventTrackingFailed, mSessionTrackingSucceeded, mSessionTrackingFailed); // Tools.tips02(ctx, tps, null); // } private void callTask(Define.TransferRunnable task) { if (mIsCall || task == null) { return; } mIsCall = true; task.run(mTransfer); } }
自定义短链配置 - 跟踪链接
Google Play 应用的短链推广
-
后台配置, 把需要透传的参数设置到 推广结构 中.
-
adjust 回调中 可以获取到 归因数据 Addata
{\"Addata\":\"{\\\"adgroup\\\":\\\"bbb\\\",\\\"adid\\\":\\\"a5ad874ca32aa310c636adaf54396ab6\\\",\\\"campaign\\\":\\\"aaa\\\",\\\"creative\\\":\\\"ccc\\\",\\\"network\\\":\\\"ad3\\\",\\\"trackerName\\\":\\\"ad3::aaa::bbb::ccc\\\",\\\"trackerToken\\\":\\\"3lcxp3p\\\"}\",\"Appid\":3,\"Deviceid\":\"1183eca72cbe5513eaf1e4f1232a8ee8\",\"Ggdata\":\"{\\\"utm_content\\\":\\\"bbb\\\",\\\"adjust_reftag\\\":\\\"cvmLwqpUXZUfr\\\",\\\"utm_term\\\":\\\"ccc\\\",\\\"utm_source\\\":\\\"ad3\\\",\\\"pid\\\":\\\"1109\\\",\\\"utm_campaign\\\":\\\"aaa\\\"}\
Facebook Ads 投放
需要 Facebook 对应的 app id 接收 服务条款, 才能在 af 的 onConversionDataSuccess 回调中, 接收到 campaign 相关数据
链接: https://www.facebook.com/ads/manage/advanced_mobile_measurement/app_based_tos?appid=123123 (123123 是 app id)
-
合伙伙伴设置 中, 添加 facebook, 填入 fb app id.
-
然后就可以在 adjust 的回调用获取到 fb 广告系列的名字
config.setOnAttributionChangedListener(attribution -> { // adjust 归因回调 LogUtil.TD(TAG, "Attribution: " + attribution.toString()); task.run(ECode.Ok, JsonTool.toJson(attribution)); }); // 获取到的数据 \"Addata\":\"{\\\"adgroup\\\":\\\"0918 (23845782953730115)\\\",\\\"adid\\\":\\\"eee\\\",\\\"campaign\\\":\\\"ln_1094_rum9.19\\\",\\\"creative\\\":\\\"1 (23845782953750115)\\\",\\\"network\\\":\\\"Facebook Installs\\\",\\\"trackerName\\\":\\\"Facebook Installs::ln_1094_rum9.18 (23845782953570115)::0918 (23845782953730115)::1 (23845782953750115)\\\",\\\"trackerToken\\\":\\\"ofkv4pp\\\"}\"
-
campaign 对应的就是 fb 广告系列名字
-
-
查看归因
-
所有设置 -> 测试控制台, 输入 谷歌广告 id (gaid), 可以查看到该设备的 归因数据.
api 上报事件
官方文档: https://help.adjust.com/en/article/server-to-server-events
先确定是否归因正确 查看归因 , 再查看事件的归因是否正确.
-
生成 s2s 秘钥 secret. 所有设置 -> S2S 安全 -> 创建识别码, 如:
b78a67xxxxxx
-
激活 s2s. 所有设置 -> S2S 安全 -> 激活 S2S 认证
-
上报参数中, 不用平台的使用 id 字段不同
- Android: gps_adid, Google 广告 id, 通过
Adjust.getGoogleAdId
回调中获取到 - ios: idfa
- Android: gps_adid, Google 广告 id, 通过
py 测试代码
-
测试用例
# adjust def test_adEvent(self): from urllib.parse import urlencode, quote appToken = "y0sxxx" eventToken = "4vexxx" secret = "b78a67xxxxxx" aduid = "ed6ec341-277c-43ce-b48d-xxx" params = { "revenue": 100, # 收益 "currency": "INR", # 币种 "environment": "production", # 生成 or 测试环境 } data = { "s2s": 1, "app_token": appToken, "event_token": eventToken, "gps_adid": aduid, # 不同环境配置的值不一样, Android: gps_adid, 谷歌广告id "partner_params": quote(utils.beautyJson(params, indent=None)), # 要 encode } url = "https://s2s.adjust.com/event?{}".format(urlencode(data, doseq=False)) # 不要 encode headers = { "Authorization": "Bearer {}".format(secret), "Content-Type": "application/json", } print("--- url: {}".format(url)) code, rspDct = utils.httpPost(url, headers=headers) print("--- code: {}".format(code)) print("--- rsp: {}".format(utils.beautyJson(rspDct)))
-
上报成功, 返回的 json 有
"status": "OK"
才代表成功了.如果是
code: 200
但是 json 是空的, 则是上报失败.--- url: https://s2s.adjust.com/event?s2s=1&app_token=y0sxxx&event_token=4vexxx&gps_adid=ed6ec341-277c-43ce-b48d-xxx&partner_params=%257B%2522revenue%2522%253A%2520100%252C%2520%2522currency%2522%253A%2520%2522INR%2522%252C%2520%2522environment%2522%253A%2520%2522production%2522%257D --- code: 200 --- rsp: { "status": "OK" }
-
复用 Facebook 的 app id
Google 账号被封, 新的 Google 包可以复用 Facebook 的 app id, 然后 fb 的广告账号就不用重新开户了, sdk 能正常获取到 fb 的广告数据, 也就可以正常解析为 动态渠道.