【问题标题】:Using the new Android Advertiser id inside an SDK在 SDK 中使用新的 Android 广告商 ID
【发布时间】:2026-02-09 13:20:02
【问题描述】:

Android 广告 SDK 将使用 Android 的新广告客户 ID,这很有意义。

您似乎只能通过使用 google services sdk 获取 id,如下所述: http://developer.android.com/google/play-services/id.html.

使用google play services sdk,需要引用google-play-services_lib项目,这会导致几个问题:

  1. 很多 SDK 都是 jar,这意味着它们不能按原样使用 google-play-services_lib(因为它们不能包含资源)。
  2. 如果我只需要广告客户 ID,我需要将 google-play-services_lib 添加到我的项目中,它的大小接近 1 MB。

有没有办法只获取广告客户 ID,而不使用资源?

【问题讨论】:

    标签: android uniqueidentifier google-play-services ads


    【解决方案1】:

    我遇到了同样的问题,如果您只需要 adserId,您可以使用 Intent 直接与 Google Play 服务进行交互。自定义类示例:

    import java.io.IOException;
    import java.util.concurrent.LinkedBlockingQueue;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.content.pm.PackageManager;
    import android.os.IBinder;
    import android.os.IInterface;
    import android.os.Looper;
    import android.os.Parcel;
    import android.os.RemoteException;
    
    public final class AdvertisingIdClient {
    
    public static final class AdInfo {
        private final String advertisingId;
        private final boolean limitAdTrackingEnabled;
    
        AdInfo(String advertisingId, boolean limitAdTrackingEnabled) {
            this.advertisingId = advertisingId;
            this.limitAdTrackingEnabled = limitAdTrackingEnabled;
        }
    
        public String getId() {
            return this.advertisingId;
        }
    
        public boolean isLimitAdTrackingEnabled() {
            return this.limitAdTrackingEnabled;
        }
    }
    
    public static AdInfo getAdvertisingIdInfo(Context context) throws Exception {
        if(Looper.myLooper() == Looper.getMainLooper()) throw new IllegalStateException("Cannot be called from the main thread");
    
        try { PackageManager pm = context.getPackageManager(); pm.getPackageInfo("com.android.vending", 0); }  
        catch (Exception e) { throw e; }
    
        AdvertisingConnection connection = new AdvertisingConnection();
        Intent intent = new Intent("com.google.android.gms.ads.identifier.service.START");
        intent.setPackage("com.google.android.gms");
        if(context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
            try {
                AdvertisingInterface adInterface = new AdvertisingInterface(connection.getBinder());
                AdInfo adInfo = new AdInfo(adInterface.getId(), adInterface.isLimitAdTrackingEnabled(true));
                return adInfo;
            } catch (Exception exception) {
                throw exception;
            } finally {
                context.unbindService(connection);
            }
        }       
        throw new IOException("Google Play connection failed");     
    }
    
    private static final class AdvertisingConnection implements ServiceConnection {
        boolean retrieved = false;
        private final LinkedBlockingQueue<IBinder> queue = new LinkedBlockingQueue<IBinder>(1);
    
        public void onServiceConnected(ComponentName name, IBinder service) {
            try { this.queue.put(service); }
            catch (InterruptedException localInterruptedException){}
        }
    
        public void onServiceDisconnected(ComponentName name){}
    
        public IBinder getBinder() throws InterruptedException {
            if (this.retrieved) throw new IllegalStateException();
            this.retrieved = true;
            return (IBinder)this.queue.take();
        }
    }
    
    private static final class AdvertisingInterface implements IInterface {
        private IBinder binder;
    
        public AdvertisingInterface(IBinder pBinder) {
            binder = pBinder;
        }
    
        public IBinder asBinder() {
            return binder;
        }
    
        public String getId() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            String id;
            try {
                data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService");
                binder.transact(1, data, reply, 0);
                reply.readException();
                id = reply.readString();
            } finally {
                reply.recycle();
                data.recycle();
            }
            return id;
        }
    
        public boolean isLimitAdTrackingEnabled(boolean paramBoolean) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            boolean limitAdTracking;
            try {
                data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService");
                data.writeInt(paramBoolean ? 1 : 0);
                binder.transact(2, data, reply, 0);
                reply.readException();
                limitAdTracking = 0 != reply.readInt();
            } finally {
                reply.recycle();
                data.recycle();
            }
            return limitAdTracking;
        }
    }
    }
    

    确保您没有从主 UI 线程调用它。 例如,使用类似:

    new Thread(new Runnable() {        
        public void run() {
            try {
                AdInfo adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
                advertisingId = adInfo.getId();
                optOutEnabled = adInfo.isLimitAdTrackingEnabled();
            } catch (Exception e) {
                e.printStackTrace();                            
            }                       
        }
    }).start();
    

    【讨论】:

    • 我觉得这很危险。任何地方都记录了这样的事情吗?
    • 我在 com.google.android.gms.ads.identifier.AdvertisingIdClient.getAdvertisingIdInfo 收到以下错误:java.lang.NoSuchFieldError:android.content.pm.PackageInfo.signatures 我怎样才能避免呢?
    【解决方案2】:

    唯一受支持的访问广告 ID 的方法是直接链接到 Play Services SDK 并通过这些 API 访问广告 ID。 Google 不推荐或支持任何避免直接访问 Play 服务 API 的解决方法,因为它会破坏面向用户的功能(例如设备上的 Play 服务应用程序过时时的错误处理),并且其行为将在未来的 Play 中不可预测服务发布。

    Google Play Developer Program Policies 要求您仅以授权方式访问 Google Play API。

    【讨论】:

    • 谢谢埃里克,很高兴知道这一点。但是,您的回复并没有为@dors 和我们大多数人遇到的两个问题提供解决方案。如果有更好的方法,请告诉我们。
    • Facebook 在其 sdk 中使用它:github.com/facebook/facebook-android-sdk/blob/…
    【解决方案3】:

    MoPub 和其他一些大玩家没有将 GPS 包含在他们的 SDK 中。来自 MoPub 的帮助页面:

    MoPub SDK 不需要 Google Play 服务。如果您安装了它,我们将自动使用新的 Google Advertising ID。如果您不安装 Google Play 服务,我们将继续传递旧的 Android ID。请注意,所有发布商都需要在 8 月 1 日前在其应用中使用 GPS,以防止其应用被 Google Play 商店拒绝

    查看此链接了解更多详情:

    http://help.mopub.com/customer/portal/articles/1523610-google-advertising-id-faqs

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      注意:我对 Gradle 的回答已经过时,因为现在您可以选择要包含在项目中的 GooglePlayServices 库的哪些部分

      当我正在处理的项目达到 65k dex 限制时,我最近遇到了同样的问题。

      我是这样解决的:

      • 转到https://code.google.com/p/jarjar/downloads/list 并以.jar 格式下载最新的Jar jar 链接。将文件放在工作文件夹中。在本例中,我将使用桌面。

      • 转到 [Android SDK 路径]\extras\google\google_play_services\libproject\google-play-services_lib\libs 并将 google-play-services.jar 复制到同一工作文件夹。

        李>
      • 在同一个文件夹中创建一个名为 rules.txt 的文本文件(名称并不重要)。

      • 在 rules.txt 中粘贴文本(不带引号):

      “保留 com.google.android.gms.ads.identifier.AdvertisingIdClient”

      • 如果您想保留其他课程,可以在此处添加。

      • 打开命令提示符文件并更改工作文件夹的路径。在 Windows 上使用 [cd] 命令。

      • 编写以下命令:

      java -jar [jarjar 归档] 进程 [rulesFile] [inJar] [outJar]

      java -jar jarjar-1.4.jar 进程 rules.txt google-play-services.jar google-play-services-lite.jar

      • 执行命令。

      它的作用:

      • 该命令将在工作文件夹中生成一个新的 java 存档 (*.jar),其中仅包含获取广告商 ID 及其依赖项所需的类。因此,google-play-services.jar 将从 2.2 Mb 下降到 ~50kb

      如何使用它:

      • 照常将 google play 服务从 sdk 导入您的项目,确保将其复制到您的工作区。在 libs 文件夹中,将 google-play-services.jar 替换为您之前生成的 jar。

      • 如果您在那里,您也可以删除资源以释放另外 0.5 mb。确保保留 values/common_strings.xml 和 values/version.xml。

      • 不要忘记为 google play 服务添加清单元数据。

      这帮助我减少了超过 2.5 mb 的项目大小,并保持在 65k dex 类和方法的限制之下,同时能够访问 Google 广告商 ID。

      希望对你也有帮助。

      【讨论】:

        【解决方案5】:

        Adrian 的解决方案非常棒,我自己也用。

        但是,今天我发现它在设备上未安装 Google Play 服务时存在错误。当您的活动/服务停止时,您将收到有关泄漏ServiceConnection 的消息。这实际上是Context.bindService中的一个bug:当绑定到服务失败时(在这种情况下是因为没有安装Google Play Services),Context.bindService返回false,但是它并没有清除对ServiceConnection的引用,并且即使该服务不存在,也希望您致电Context.unbindService

        解决方法是像这样更改getAdvertisingIdInfo的代码:

        public static AdInfo getAdvertisingIdInfo(Context context) throws Exception {
            if(Looper.myLooper() == Looper.getMainLooper())
                throw new IllegalStateException("Cannot be called from the main thread");
        
            try {
                PackageManager pm = context.getPackageManager();
                pm.getPackageInfo("com.android.vending", 0);
            } catch(Exception e) {
                throw e;
            }
        
            AdvertisingConnection connection = new AdvertisingConnection();
            Intent intent = new Intent("com.google.android.gms.ads.identifier.service.START");
            intent.setPackage("com.google.android.gms");
            try {
                if(context.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
                    AdvertisingInterface adInterface = new AdvertisingInterface(connection.getBinder());
                    AdInfo adInfo = new AdInfo(adInterface.getId(), adInterface.isLimitAdTrackingEnabled(true));
                    return adInfo;
                }
            } catch(Exception exception) {
                throw exception;
            } finally {
                context.unbindService(connection);
            }
            throw new IOException("Google Play connection failed");
        }
        

        即使Context.bindService 返回false,也会调用Context.unbindService

        【讨论】:

          最近更新 更多