2013-11-20 38 views
21

Ma to sens, że pakiety SDK reklam na Androida będą korzystać z nowego identyfikatora reklamodawcy Androida.Używanie nowego identyfikatora reklamodawcy Android w SDK

Wygląda na to, że identyfikator można uzyskać tylko za pomocą usługi SDK usług Google, o czym wspomniano: http://developer.android.com/google/play-services/id.html.

Korzystanie z SDK usług Google Play, wymaga przedstawieniu projektu google play-services_lib, co powoduje liczne problemy:

  1. Wiele SDK są słoiki, co oznacza, że ​​nie można korzystać z google play-services_lib tak jak (ponieważ nie mogą zawierać zasobów).
  2. Jeśli chcę tylko identyfikator reklamodawcy, muszę dodać google-play-services_lib do mojego projektu, który waży prawie 1 MB.

Czy istnieje sposób na uzyskanie identyfikatora reklamodawcy bez korzystania z zasobów?

Odpowiedz

34

Natknęłam się na ten sam problem, jeśli potrzebujesz tylko reklamodawcy, abyś mógł bezpośrednio korzystać z usługi Google Play za pomocą intencji. Przykład niestandardowej klasy:

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

Upewnij się, że nie dzwonisz to od głównego wątku UI. Na przykład użyć czegoś takiego:

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(); 
+0

Jest to niebezpieczne w użyciu. Czy coś takiego jest udokumentowane w dowolnym miejscu? – Javanator

+0

niebezpieczne dlaczego? https://developer.android.com/google/play-services/id.html – sirvon

+0

Wystąpił następujący błąd na com.google.android.gms.ads.identifier.AdvertisingIdClient.getAdvertisingIdInfo: ** java.lang.NoSuchFieldError : android.content.pm.PackageInfo.signatures ** Jak mogę tego uniknąć? – mdavid

-1

jedyną obsługiwaną metodą dostępu Reklama ID to poprzez bezpośrednie powiązanie z SDK Play i dostępu Reklama identyfikator za pośrednictwem tych interfejsów API. Google nie zaleca ani nie wspiera żadnego rozwiązania, które pozwala uniknąć bezpośredniego dostępu do interfejsów API usług Play, ponieważ powoduje on przełamanie funkcji użytkownika (takich jak obsługa błędów w przypadkach, gdy aplikacja Usługi Google Play na urządzeniu jest nieaktualna), a jego zachowanie będzie nieprzewidywalne w przyszłości. Publikacje usług.

Urządzenie Google Play Developer Program Policies wymaga dostępu do interfejsów API Google Play tylko w autoryzowany sposób.

+0

Dzięki Eric, to dobrze wiedzieć. Jednak twoja odpowiedź nie zapewnia rozwiązania 2 problemów, które mają większość z nas. Daj nam znać, czy jest lepsze podejście. – Adrian

1

MoPub i kilku innych dużych graczy nie uwzględnia GPS w swoich pakietach SDK. Od strony pomocy MoPub za:

the MoPub SDK does not require Google Play Services. If you have it installed, we will automatically use the new Google Advertising ID. If you do NOT install Google Play Services, we will continue to pass the old Android ID. Note that all publishers need to use GPS in their app by August 1 to prevent their apps from being rejected by the Google Play Store

Sprawdź ten link na dużo bardziej szczegółowo:

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

nadzieję, że to pomaga.

6

UWAGA: Moja odpowiedź jest przestarzały dla Gradle od teraz można wybrać, które elementy biblioteki GooglePlayServices chcesz umieścić w swoim projekcie

wpadłem na ten sam problem ostatnio gdy projekt Pracowałem na osiągnięciu Limit de minim.

Oto jak rozwiązać go:

  • idź do https://code.google.com/p/jarjar/downloads/list i pobrać najnowsze Jar Jar Links w formacie .jar. Umieść plik w folderze roboczym. W tym przykładzie użyję pulpitu.

  • Przejdź do [ścieżka SDK systemu Android] \ dodatki \ google \ google_play_services \ libproject \ google-play-services_lib \ libs i skopiuj google-play-services.jar do tego samego folderu roboczego.

  • W tym samym folderze utwórz plik tekstowy o nazwie rules.txt (nazwa nie ma znaczenia).

  • Wewnątrz rules.txt wkleić tekst (bez cudzysłowów):

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

  • Jeśli chcesz innych klas, które chcesz zachować, możesz je dodać tutaj.

  • Otwórz plik wiersza polecenia i zmień ścieżkę do folderu roboczego. W systemie Windows użyj polecenia [cd].

  • Wpisz następujące polecenie:

java -jar [jarjar archive] process [rulesFile] [inJar] [outJar]

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

  • wykonać polecenie.

Co robi:

  • Komenda wygeneruje nowe archiwum Java (* .jar) w folderze roboczym, który będzie zawierał tylko klasę potrzebne, aby uzyskać identyfikator reklamodawcy i jego zależności. Tak, google play-services.jar pójdzie w dół z 2,2 MB do ~ 50kb

jak go używać:

  • importu usługi Google Play z SDK do projektu, jak zwykle, upewnij się, że skopiowałeś go do swojego obszaru roboczego. W folderze libs zamień google-play-services.jar na wcześniej wygenerowany jar.

  • Jeśli tam jesteś, możesz również usunąć zasoby, aby zwolnić kolejne 0,5 MB. Zachowaj wartości/common_strings.xml i values ​​/ version.xml.

  • Nie zapomnij dodać manifestu metadanych dla usług Google Play.

To pomogło mi w celu zmniejszenia wielkości projektu z ponad 2,5 mb i pozostać pod 65k Dex klas i metod ograniczania będąc w stanie uzyskać dostęp do identyfikatora reklamodawcy Google.

Mam nadzieję, że to ci pomoże.

3

Rozwiązanie Adriana jest doskonałe, a ja używam go sam.

Jednak dzisiaj odkryłem, że ma on błąd, gdy Usługi Google Play nie są zainstalowane na urządzeniu. Otrzymasz wiadomość o wycieku ServiceConnection, gdy Twoja aktywność/usługa zostanie zatrzymana. W rzeczywistości jest to błąd w Context.bindService: gdy powiązanie z usługą nie powiedzie się (w tym przypadku, ponieważ Usługi Google Play nie są zainstalowane), Context.bindService zwraca wartość false, ale nie usuwa odniesienia do ServiceConnection i oczekuje, że zadzwonisz pod numer Context.unbindService nawet chociaż usługa nie istnieje!

Rozwiązaniem jest zmiana kodu getAdvertisingIdInfo tak:

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

ten sposób Context.unbindService zostanie wywołana nawet jeśli Context.bindService powraca false.