Google Play Android应用 如何接入Appsflyer SDK

1,188 阅读2分钟

接入Appsflyer SDK主要目的是做一些渠道归因统计,也可以做一些事件上报之类的。

1. 仓库依赖

repositories { 
    mavenCentral() 
}

2. 引用SDK

这里为什么引入installreferrer呢,主要是用于Android-GooglePlay安装来源追踪PlayInstallReferrer, 使用商店引荐来源网址可提高归因准确性。

dependencies {
    implementation 'com.android.installreferrer:installreferrer:2.2'
    implementation 'com.appsflyer:af-android-sdk:6.9.0'
}

3. 声明权限

2022 年初,谷歌宣布改变谷歌播放服务的行为并获取安卓广告 ID。根据公告,针对Android 13(API 33)及更高版本的应用程序必须在其文件中声明Google Play服务正常权限,才能访问设备的广告ID。

<uses-permission android:name="android.permission.INTERNET" /> 
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />

4. receiver声明

<application>
    <receiver
        android:name="com.appsflyer.SingleInstallBroadcastReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="com.android.vending.INSTALL_REFERRER" />
        </intent-filter>
    </receiver>
</application>

5. 初始化SDK

这里只处理了Google投放和Facebook投放

installChannel可用于af归因的渠道值

<AF_DEV_KEY> af后台返回的Dev Key

<CUSTOMER_ID> 代表CUID 可看业务需要定义,比如手机号或者设备唯一id之类的

class AFApplication : Application() {
    override fun onCreate() {
        super.onCreate();

        val appsFlyerConversionListener: AppsFlyerConversionListener = object : AppsFlyerConversionListener {
            override fun onConversionDataSuccess(conversionData: Map<String, Any>) {
                val af_status = if (conversionData["af_status"] == null) "null" else conversionData["af_status"].toString()

                // 判断是否是非自然安装
                if (af_status == "Non-organic") {
                    if (conversionData["is_first_launch"] != null && conversionData["is_first_launch"].toString() == "true") {
                        val media_source = if (conversionData["media_source"] == null) "null" else conversionData["media_source"].toString()

                        var campaign = if (conversionData["campaign"] == null) "null" else conversionData["campaign"].toString()

                        if (TextUtils.isEmpty(campaign) || "None" == campaign) {
                            campaign = "null"
                        }

                        if ("googleadwords_int" == media_source) {
                            val af_channel = if (conversionData["af_channel"] == null) "null" else conversionData["af_channel"].toString()

                            val network = if (conversionData["network"] == null) "null" else conversionData["network"].toString()

                            // 正常来说该渠道值 一般会记录到后台使用,方便分析
                            val installChannel = String.format("%s#%s/%s/%s", media_source, campaign, af_channel, network)
                        }
                        else if ("Facebook Ads" == media_source || "restricted" == media_source) {
                            val adgroup = if (conversionData["adgroup"] == null) "null" else conversionData["adgroup"].toString()

                            val ad_id = if (conversionData["ad_id"] == null) "null" else conversionData["ad_id"].toString()

                            // 正常来说该渠道值 一般会记录到后台使用,方便分析
                            val installChannel = String.format("%s#%s/%s/%s", media_source, campaign, adgroup, ad_id)
                        } else {
                            // 正常来说该渠道值 一般会记录到后台使用,方便分析
                            val installChannel = String.format("%s#%s", media_source, campaign)
                        }
                    }
                } else {
                    // 此时该渠道值为默认一个渠道值比如GooglePlay
                    val installChannel = "GooglePlay"
                }


                val android_id = if (conversionData["android_id"] == null) "null" else conversionData["android_id"].toString()

                val advertising_id = if (conversionData["advertising_id"] == null) "null" else conversionData["advertising_id"].toString()

                val install_time = if (conversionData["install_time"] == null) "null" else conversionData["install_time"].toString()

                val is_first_launch = if (conversionData["is_first_launch"] == null) false else conversionData["is_first_launch"] as Boolean
                
            }

            override fun onConversionDataFail(errorMessage: String) {}

            override fun onAppOpenAttribution(conversionData: Map<String, String>) {}

            override fun onAttributionFailure(errorMessage: String) {}
        }

        AppsFlyerLib.getInstance().init("<AF_DEV_KEY>",appsFlyerConversionListener,this.applicationContext)
        AppsFlyerLib.getInstance().waitForCustomerUserId(true)
        AppsFlyerLib.getInstance().start(this)
        AppsFlyerLib.getInstance().setCustomerIdAndLogSession("<CUSTOMER_ID>", this)
        AppsFlyerLib.getInstance().setCollectAndroidID(true)
        AppsFlyerLib.getInstance().setCollectIMEI(true)
        AppsFlyerLib.getInstance().setCollectOaid(true)
        AppsFlyerLib.getInstance().setDebugLog(BuildConfig.DEBUG)
        
    }

}

6. 事件上报

EventName 代表事件的名称

eventValues 看业务需要传递相关的数据即可

Map<String, Object> eventValues = new HashMap<String, Object>(); 
eventValues.put(AFInAppEventParameterName.PRICE, 1234.56); 
eventValues.put(AFInAppEventParameterName.CONTENT_ID,"1234567"); 
AppsFlyerLib.getInstance().logEvent(getApplicationContext(), "<EventName>" , eventValues);

AppsFlyerLib.getInstance().logEvent(getApplicationContext(), "<EventName>", eventValue, new AppsFlyerRequestListener() 
{ 
    @Override public void onSuccess() 
    { 
        Log.d(LOG_TAG, "Event sent successfully"); 
    } 

    @Override public void onError(int i, @NonNull String s) 
    { 
        Log.d(LOG_TAG, "Event failed to be sent:\n" + "Error code: " + i + "\n" + "Error description: " + s); 
    } 
});

7. 混淆

-keep class com.appsflyer.** { *; }
-dontwarn com.appsflyer.**
-dontwarn com.android.installreferrer