web-dev-qa-db-ja.com

SDK内での新しいAndroid広告主IDの使用

Android広告SDKはAndroidの新しい広告主IDを使用することは非常に理にかなっています。

http://developer.Android.com/google/play-services/id.html のように、GoogleサービスSDKを使用しないとIDを取得できないようです。

Google play services sdkを使用するには、google-play-services_libプロジェクトを参照する必要があるため、いくつかの問題が発生します。

  1. 多くのSDKはjarです。つまり、それらはgoogle-play-services_libをそのまま使用できません(リソースを含めることができないため)。
  2. 広告主IDのみが必要な場合は、google-play-services_libをプロジェクトに追加する必要があります。これは、ほぼ1 MBの重みです。

リソースを使用せずに広告主IDのみを取得する方法はありますか?

23
dors

同じ問題が発生しましたが、advertiserIdだけが必要な場合は、インテントを使用して直接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();
41
Adrian

エイドリアンの解決策は優れており、私はそれを自分で使用しています。

しかし、今日、Google Play開発者サービスがデバイスにインストールされていないときにバグがあることを発見しました。アクティビティ/サービスが停止すると、ServiceConnectionのリークに関するメッセージが表示されます。これは実際にはContext.bindServiceのバグです。サービスへのバインドが失敗すると(この場合はGoogle Play開発者サービスがインストールされていないため)、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.unbindServicefalseを返しても、Context.bindServiceが呼び出されます。

7
Avi

注:プロジェクトに含めるGooglePlayServicesライブラリの部分を選択できるようになったため、私の答えはGradleでは古くなっています

最近、私が取り組んでいるプロジェクトが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]

  • JarJarリンクコマンドとルールの詳細については、こちらをご覧ください。 https://code.google.com/p/jarjar/wiki/CommandLineDocs

  • 例を挙げれば、私が書かなければならないコマンドは次のようになりました(ファイル名に応じてコマンドを変更してください)。

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に減少します

どうやって使うのですか:

  • 通常どおり、SDKからプロジェクトにGoogle Playサービスをインポートします。必ずワークスペースにコピーしてください。 libsフォルダーで、google-play-services.jarを以前に生成したjarに置き換えます。

  • そこにいる場合は、リソースも削除して、さらに0.5 MBを解放できます。 values/common_strings.xmlおよびvalues/version.xmlを必ず保持してください。

  • グーグルプレイサービスのためのマニフェストメタデータを追加することを忘れないでください。

これにより、プロジェクトのサイズを2.5 MBを超えて縮小し、Googleの広告主IDにアクセスしながら、65kのdexクラスとメソッドの制限を守ることができました。

それもあなたを助けることを願っています。

6
Andrei Lupsa

MoPubと他のいくつかの大きなプレーヤーは、GPSをSDKに含めていません。 MoPubのヘルプページから:

moPub SDKはGoogle Play開発者サービスを必要としません。インストールされている場合は、新しいGoogle広告IDが自動的に使用されます。 Google Play開発者サービスをインストールしない場合、引き続き古いAndroid IDが渡されます。アプリが拒否されるのを防ぐため、すべてのパブリッシャーは8月1日までにアプリでGPSを使用する必要があります。 Google Playストア

詳細については、このリンクを確認してください:

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

お役に立てれば。

1
ankit