Usando la nueva identificación del anunciante de Android dentro de un SDK

Resuelto dors asked hace 54 años • 5 respuestas

Tiene mucho sentido que los SDK de anuncios de Android utilicen la nueva identificación de anunciante de Android.

Parece que solo puede obtener la identificación utilizando el SDK de servicios de Google, como se menciona aquí: http://developer.android.com/google/play-services/id.html .

El uso del SDK de servicios de Google Play requiere hacer referencia al proyecto google-play-services_lib, lo que causa varios problemas:

  1. Muchos SDK son archivos jar, lo que significa que no pueden usar google-play-services_lib tal cual (porque no pueden incluir recursos).
  2. Si solo quiero el ID del anunciante, necesito agregar google-play-services_lib a mi proyecto, que pesa casi 1 MB.

¿Existe alguna manera de obtener únicamente la identificación del anunciante, sin utilizar recursos?

dors avatar Jan 01 '70 08:01 dors
Aceptado

Me encontré con el mismo problema: si solo necesita el ID del anunciante, puede interactuar con el servicio Google Play directamente mediante una intención. Ejemplo de clase personalizada:

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;
    }
}
}

Asegúrese de no llamar a esto desde el hilo principal de la interfaz de usuario. Por ejemplo, use algo como:

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();
Adrian avatar Feb 05 '2014 03:02 Adrian

La solución de Adrian es excelente y yo misma la uso.

Sin embargo, hoy descubrí que tiene un error cuando los servicios de Google Play no están instalados en el dispositivo. Recibirá un mensaje sobre una fuga ServiceConnectioncuando se detenga su actividad/servicio. En realidad, esto es un error en Context.bindService: cuando falla la vinculación al servicio (en este caso porque los servicios de Google Play no están instalados), Context.bindServicedevuelve falso, pero no borra la referencia al ServiceConnectiony espera que usted llame Context.unbindServiceaunque el servicio no ¡No existe!

La solución es cambiar el código de getAdvertisingIdInfoesta manera:

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");
}

De esa manera Context.unbindServicese llamará incluso si Context.bindServiceregresa false.

Avi avatar Apr 27 '2015 14:04 Avi

NOTA: Mi respuesta está desactualizada para Gradle ya que ahora puedes elegir qué partes de la biblioteca de GooglePlayServices deseas incluir en tu proyecto.

Me encontré con el mismo problema últimamente cuando el proyecto en el que estaba trabajando alcanzó el límite de 65k dex.

Así es como lo resolví:

  • Vaya a https://code.google.com/p/jarjar/downloads/list y descargue los enlaces Jar jar más recientes en formato .jar. Coloque el archivo en una carpeta de trabajo. Para este ejemplo usaré el escritorio.

  • Vaya a [Ruta del SDK de Android]\extras\google\google_play_services\libproject\google-play-services_lib\libs y copie google-play-services.jar en la misma carpeta de trabajo.

  • En la misma carpeta, cree un archivo de texto llamado reglas.txt (el nombre realmente no importa).

  • Dentro del Rules.txt pega el texto (sin las comillas):

"mantener com.google.android.gms.ads.identifier.AdvertisingIdClient"

  • Si quieres otras clases que quieras conservar, puedes agregarlas aquí.

  • Abra un archivo de símbolo del sistema y cambie la ruta a su carpeta de trabajo. En Windows utilice el comando [cd].

  • Escribe el siguiente comando:

java -jar [archivo jarjar] proceso [archivoreglas] [inJar] [outJar]

  • Puede encontrar más detalles sobre los comandos y reglas de JarJar Links aquí: https://code.google.com/p/jarjar/wiki/CommandLineDocs

  • Sólo para dar un ejemplo, el comando que tuve que escribir se veía así (cambie el suyo según los nombres de sus archivos):

java -jar jarjar-1.4.jar reglas de proceso.txt google-play-services.jar google-play-services-lite.jar

  • Ejecute el comando.

QUE HACE:

  • El comando generará un nuevo archivo java (*.jar) en la carpeta de trabajo que contendrá solo la clase necesaria para obtener su identificación de anunciante y sus dependencias. Entonces, google-play-services.jar bajará de 2,2 Mb a ~50 kb.

CÓMO USARLO:

  • Importe los servicios de Google Play desde el SDK a su proyecto como de costumbre, asegúrese de copiarlo en su espacio de trabajo. En la carpeta libs, reemplace google-play-services.jar con el jar que generó anteriormente.

  • Si estás allí, también puedes eliminar los recursos para liberar otros 0,5 MB. Asegúrese de conservar los valores/common_strings.xml y los valores/versión.xml.

  • No olvide agregar metadatos de manifiesto para los servicios de Google Play.

Esto me ayudó a reducir el tamaño del proyecto en más de 2,5 MB y a mantenerme por debajo del límite de 65.000 clases y métodos dex mientras podía acceder a la identificación del anunciante de Google.

Espero que te ayude a ti también.

 avatar Jul 03 '2014 08:07

MoPub y algunos otros grandes actores no incluyen GPS en sus SDK. Desde la página de ayuda de MoPub:

El SDK de MoPub no requiere los servicios de Google Play. Si lo tienes instalado, utilizaremos automáticamente el nuevo ID de publicidad de Google. Si NO instala los Servicios de Google Play, continuaremos pasando la ID de Android anterior. Tenga en cuenta que todos los editores deben usar GPS en su aplicación antes del 1 de agosto para evitar que Google Play Store rechace sus aplicaciones.

Consulte este enlace para obtener muchos más detalles:

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

Espero que esto ayude.

ankit avatar Jul 01 '2014 07:07 ankit