İleri düzey konular

Bu bölümler referans amaçlıdır ve bunları okumanız gerekmez. yukarıdan aşağıya doğru.

Çerçeve API'lerini kullanın:

Bu API'ler, daha tutarlı bir API yüzeyi (ör. UserHandle nesnelerinden kaçının) ancak şimdilik bunları doğrudan çağırabilirsiniz.

Uygulama oldukça basittir. Etkileşim kurabiliyorsanız kabul edin. Değilse ancak istekte bulunabilir ve ardından kullanıcı isteminizi/banner/ipucunuzu/vb. Kullanıcı Ayarlar'a gitmeyi, istek amacını oluşturmayı ve Kullanıcıyı ilgili alana göndermek için Context#startActivity. Yayını özelliğini kullanın veya kullanıcı geldiğinde tekrar kontrol edin. geri dönüyor.

Bunu test etmek için iş profilinizde TestDPC'yi açmanız ve tıklayın ve paket adınızı bağlı uygulamalar izin verilenler listesine eklemeyi seçin. Bu yönetici "izin verilenler listesi"ni taklit eder en iyi şekilde yararlanabilirsiniz.

Sözlük

Bu bölümde, profiller arası geliştirmeyle ilgili anahtar terimler açıklanmaktadır.

Profiller Arası Yapılandırma

Profiller Arası Yapılandırma, ilgili Profiller Arası Sağlayıcıyı birlikte gruplandırır Sınıflar ve profiller arası özellikler için genel yapılandırma sağlar. Genellikle her kod tabanı için bir @CrossProfileConfiguration ek açıklaması olur ancak bazı karmaşık uygulamalarda birden fazla söz konusu olabilir.

Profil Konnektörü

Bağlayıcı, profiller arasındaki bağlantıları yönetir. Genellikle her çapraz profil belirli bir Bağlayıcı'yı işaret eder. Tek bir profildeki her tür profil türü yapılandırmasının aynı Bağlayıcı'yı kullanması gerekir.

Profiller Arası Sağlayıcı Sınıfı

Profiller Arası Sağlayıcı Sınıfı, ilgili Profiller Arası Türlerini gruplandırır.

Mediator

Bir arabulucu, üst düzey ve alt düzey kod arasında yer alır ve çağrıları doğru profilleri ve sonuçları birleştirmenize olanak tanır. URL'nizde tek bir kodla oluşturabilirsiniz. Bu, yerleşik bir kavram değil de mimari bir kavramdır. SDK'yı kullanabilirsiniz.

Profiller Arası Türü

Profiller arası türü, ek açıklamalı yöntemler içeren bir sınıf veya arayüzdür @CrossProfile Bu türdeki kodun profile duyarlı olması gerekmez ve yerel verilere göre hareket etmesi idealdir.

Profil Türleri

Profil Türü
Şu anki adıÇalıştırdığımız etkin profil.
Diğer(varsa) Yürütmediğimiz profil.
KişiselKullanıcı 0, cihazdaki profil kapalıdır.
İşGenellikle 10. kullanıcıdır ancak daha yüksek olabilir. Etkinleştirilebilir ve iş uygulamalarını ve verilerini içerir.
Birincilİsteğe bağlı olarak uygulama tarafından tanımlanır. Hangi profilde iki profilin birleştirilmiş görünümünü gösterir.
İkincilBirincil tanımlanmışsa, "ikincil", birincil değildir.
TedarikçiBirincil profilin tedarikçileri her iki profil de ikincil profilin tedarikçileri ise yalnızca ikincil profilin kendisidir.

Profil Tanımlayıcı

Bir profil türünü (kişisel veya iş) temsil eden sınıf. Bunlar, birden çok profil üzerinde çalışan ve daha fazla profil oluşturmak için kullanılabilecek yöntemler tarafından otomatik olarak kullanmanız gerekir. Bunlar, kolaylık sağlaması için int olarak serileştirilebilir depolama alanına sahip olursunuz.

Bu kılavuzda, verimli ve etkili bir şekilde inşa etmek için önerilen yapılar profiller arası işlevlere sahip olursunuz.

CrossProfileConnector öğesini tek bir tona dönüştürün

İşletmenizin yaşam döngüsü boyunca yalnızca tek bir örnek kullanılmalıdır. uygulamazsanız paralel bağlantılar oluşturursunuz. Bu işlem, ya da RACI matrisi kullanarak bir bağımlılık yerleştirme sistemi klasik Singleton desen, yeni veya mevcut bir sınıfta.

Oluşturulan Profil örneğini yöntemde oluşturmak yerine, çağrı yaparken sınıfınıza ekleyin veya aktarın

Bu, otomatik olarak oluşturulan FakeProfile örneğini daha sonra yapacaksınız.

Arabulucu kalıbını göz önünde bulundurun

Bu yaygın kalıp, mevcut API'lerinizden birini oluşturmaktır (ör. getEvents()) olduğunu tespit ettik. Bu durumda, mevcut API'niz bir "aracı" olmak oluşturulan yeni çağrıyı içeren yöntem veya sınıf Profiller arası kod.

Böylece her arayanı, profiller arası bir çağrı yapmasını istemeye zorlamazsınız. API'nizin bir parçası haline gelir.

Uygulama sınıflarınızı sağlayıcıda göstermek zorunda kalmamak için bir arayüz yöntemine @CrossProfile olarak ek açıklama ekleyip eklemeyeceğinizi değerlendirin.

Bu, bağımlılık yerleştirme çerçeveleriyle sorunsuz bir şekilde çalışır.

Profiller arası aramadan veri alıyorsanız verilerin hangi profilden geldiğini gösteren bir alan ekleyip eklemeyeceğinizi düşünün.

Bunu kullanıcı arayüzü katmanında bilmek isteyeceğiniz için bu iyi bir uygulama olabilir (ör. işle ilgili öğelere rozet simgesi ekleyebilirsiniz). Herhangi bir veri varsa tanımlayıcılar (ör. paket adları) olmadan benzersiz olmaz.

Profiller Arası

Bu bölümde, kendi Profiller Arası etkileşimlerinizi nasıl oluşturacağınız açıklanmaktadır.

Birincil Profiller

Bu belgedeki örneklerde yer alan çoğu çağrı, iş profili, kişisel profil ve her ikisi de dahil olmak üzere hangi profillerde çalıştırılacağını seçmek.

Pratikte yalnızca tek bir profilde birleştirilmiş deneyim sunan uygulamalarda Bu kararın, üzerinde çalıştığınız profile bağlı olmasını istiyorsanız bu durumu göz önünde bulunduran benzer pratik yöntemlerdir. kod tabanı if-else profili koşullarıyla dolup taşıyor.

Bağlayıcı örneğinizi oluştururken, her bir etiket için hangi profil türünün "birincil" öğeniz (ör. 'İŞ'). Bu işlem, takip etmek için:

profileCalendarDatabase.primary().getEvents();

profileCalendarDatabase.secondary().getEvents();

// Runs on all profiles if running on the primary, or just
// on the current profile if running on the secondary.
profileCalendarDatabase.suppliers().getEvents();

Profiller Arası Türler

@CrossProfile ek açıklamalı bir yöntem içeren sınıflar ve arayüzler Bunlar, Çapraz Profil Türleri olarak adlandırılır.

Çapraz Profil Türleri'nin uygulanması profilden bağımsız olmalıdır. bakıyorlar. Diğer yöntemlere çağrı yapmalarına ve tek bir profil üzerinde çalışıyormuş gibi çalışması gerekir. Kendisi yalnızca kendi profilinde durum erişimine sahip olur.

Örnek bir çapraz profil türü:

public class Calculator {
  @CrossProfile
  public int add(int a, int b) {
    return a + b;
  }
}

Sınıf ek açıklaması

En güçlü API'yi sağlamak üzere her çapraz reklam için bağlayıcıyı profil türü gibi:

@CrossProfile(connector=MyProfileConnector.class)
public class Calculator {
  @CrossProfile
  public int add(int a, int b) {
    return a + b;
  }
}

Bu seçenek isteğe bağlıdır ancak oluşturulan API'nin türler için daha spesifik olacağı anlamına gelir. ve derleme zamanı denetiminde daha katıdır.

Arayüzler

Bir arayüzdeki yöntemleri @CrossProfile olarak belirterek şunu belirtirsiniz: Bu yöntemin uygulanması, kolay erişilmesi gereken sağlayabilir.

Uygulamalar arası herhangi bir uygulamayı Arama Ağı'ndaki Profil Sağlayıcı'yı bu uygulamaya profiller arası erişilebilir olmalıdır. Herhangi bir teknik uygulama sınıflarına ilişkin notlar ekleyin.

Profiller Arası Sağlayıcılar

Her Profiller Arası Türü bir yöntem tarafından sağlanmalıdır @CrossProfileProvider şeklinde ek açıklama eklendi. Bu yöntemler, profiller arası çağrı yapıldığından, tek tek tıklamalı veya seçmeniz gerekir.

Marka

Sağlayıcının, hiçbir bağımsız değişken ya da bir tek Context bağımsız değişkeni.

Sağlayıcı Yöntemleri

Sağlayıcı yöntemleri herhangi bir bağımsız değişken veya tek bir Context bağımsız değişkeni almamalıdır.

Bağımlılık Yerleştirme

Proje yönetimi için Dagger gibi bir bağımlılık yerleştirme çerçevesi varsa, bu çerçevenin çapraz potansiyelinizi nasıl ortaya çıkaracağını gibi profil türleri belirleyebilir ve ardından bu türleri, sağlayıcı sınıfıdır. Daha sonra @CrossProfileProvider yöntemleri bunları döndürebilir enjekte edilen örneklere göz atın.

Profil Konnektörü

Her Profiller Arası Yapılandırmanın, diğer profille bağlantıyı yönetmekten sorumlu olur.

Varsayılan Profil Bağlayıcısı

Kod tabanında yalnızca bir Profiller Arası Yapılandırma varsa aşağıdakileri yapabilirsiniz: Kendi Profil Bağlayıcınızı oluşturmaktan kaçının ve com.google.android.enterprise.connectedapps.CrossProfileConnector Bu, herhangi bir değer belirtilmezse varsayılan olarak kullanılır.

Çapraz Profil Bağlayıcı'yı oluştururken, oluşturun:

  • Planlanmış Yürütücü Hizmeti

    SDK tarafından oluşturulan ileti dizileri üzerinde kontrol sahibi olmak istiyorsanız #setScheduledExecutorService(),

  • Cilt

    Profil bağlamayla ilgili belirli ihtiyaçlarınız varsa #setBinder kullanın. Bu muhtemelen yalnızca Cihaz Politikası Denetleyicileri tarafından kullanılır.

Özel Profil Bağlayıcı

Bazı konfigürasyonları ayarlayabilmek için özel bir profil bağlayıcısına ihtiyacınız vardır (CustomProfileConnector kullanılıyor) ve birden fazla fotoğrafa ihtiyacınız varsa tek bir kod tabanında birleştirmenize olanak tanır (örneğin, birden çok işleminiz varsa, işlem başına bir bağlayıcı öneririz).

Oluşturulan ProfileConnector aşağıdaki gibi görünmelidir:

@GeneratedProfileConnector
public interface MyProfileConnector extends ProfileConnector {
  public static MyProfileConnector create(Context context) {
    // Configuration can be specified on the builder
    return GeneratedMyProfileConnector.builder(context).build();
  }
}
  • serviceClassName

    Oluşturulan hizmetin adını değiştirmek için (buna AndroidManifest.xml) serviceClassName= kullanın.

  • primaryProfile

    Birincil profili belirtmek için primaryProfile simgesini kullanın.

  • availabilityRestrictions

    Kısıtlamaları değiştirmek için bağlantılara ve profil kullanılabilirliğine yer verdiğinden, availabilityRestrictions.

Cihaz Politikası Denetleyicileri

Uygulamanız bir Cihaz Politikası Denetleyicisi ise DpcProfileBinder, DeviceAdminReceiver referans alıyor.

Kendi profil bağlayıcınızı uyguluyorsanız:

@GeneratedProfileConnector
public interface DpcProfileConnector extends ProfileConnector {
  public static DpcProfileConnector get(Context context) {
    return GeneratedDpcProfileConnector.builder(context).setBinder(new
DpcProfileBinder(new ComponentName("com.google.testdpc",
"AdminReceiver"))).build();
  }
}

veya varsayılan CrossProfileConnector ayarını kullanarak:

CrossProfileConnector connector =
CrossProfileConnector.builder(context).setBinder(new DpcProfileBinder(new
ComponentName("com.google.testdpc", "AdminReceiver"))).build();

Profiller Arası Yapılandırma

@CrossProfileConfiguration ek açıklaması, tüm çapraz bağlantıları birbirine bağlamak için kullanılır. profil türlerinin bağlayıcından yararlanmasını sağlayın. Alıcı: Bunu yaptığımızda, bir sınıfa @CrossProfileConfiguration ile not ekleriz. Bu not, her sağlayıcı için şu şekildedir:

@CrossProfileConfiguration(providers = {TestProvider.class})
public abstract class TestApplication {
}

Bu işlem, tüm profiller arası Kullanıcıların aynı profil bağlayıcısına sahip veya hiçbir bağlayıcı belirtilmedi.

  • serviceSuperclass

    Oluşturulan hizmet varsayılan olarak android.app.Service üst sınıfını kullanır. Farklı bir sınıfa (kendisi alt sınıf olması gerekir) ihtiyacınız varsa of android.app.Service) kullanın, ardından şunu belirtin: serviceSuperclass=.

  • serviceClass

    Belirtilirse hiçbir hizmet oluşturulmaz. Bu serviceClassName, kullandığınız profil bağlayıcısında. Özel hizmeti, oluşturulan _Dispatcher sınıfını kullanarak çağrıları şu şekilde dağıtmalıdır: Örneğin:

public final class TestProfileConnector_Service extends Service {
  private Stub binder = new Stub() {
    private final TestProfileConnector_Service_Dispatcher dispatcher = new
TestProfileConnector_Service_Dispatcher();

    @Override
    public void prepareCall(long callId, int blockId, int numBytes, byte[] params)
{
      dispatcher.prepareCall(callId, blockId, numBytes, params);
    }

    @Override
    public byte[] call(long callId, int blockId, long crossProfileTypeIdentifier,
int methodIdentifier, byte[] params,
    ICrossProfileCallback callback) {
      return dispatcher.call(callId, blockId, crossProfileTypeIdentifier,
methodIdentifier, params, callback);
    }

    @Override
    public byte[] fetchResponse(long callId, int blockId) {
      return dispatcher.fetchResponse(callId, blockId);
  };

  @Override
  public Binder onBind(Intent intent) {
    return binder;
  }
}

Bu seçenek, farklı bir işlem öncesinde veya sonrasında başka işlemler yapabilirsiniz.

  • Bağlayıcı

    Varsayılan CrossProfileConnector dışında bir bağlayıcı kullanıyorsanız connector= kullanarak belirtmeniz gerekir.

Görünürlük

Uygulamanızın profiller arası etkileşimde bulunan her parçası Profil Bağlayıcınız'a dokunun.

@CrossProfileConfiguration ek açıklamalı sınıfınızın her bir sağlayıcı olduğunu belirler.

Eşzamanlı Çağrılar

Connected Apps SDK'sı, aşağıdaki durumlarda eşzamanlı (engelleme) çağrıları destekler: kaçınılmazdır. Ancak, kullanmanın bazı dezavantajları vardır. (örneğin, çağrıların uzun süre engellenmesi gibi) engellenebilir, mümkün olduğunda eşzamanlı aramalardan kaçınmanız önerilir. Kullanım için eşzamansız çağrılar için Eşzamansız aramaları için de kullanılabilir .

Bağlantı Tutucular

Eşzamanlı çağrılar kullanıyorsanız bağlantı sahibinin, profiller arası aramalar yapmadan önce kaydetmesi atlanır. Daha fazla bilgi için Bağlantı Tutucuları başlıklı makaleyi inceleyin.

Bağlantı sahibi eklemek için ProfileConnector#addConnectionHolder(Object) numaralı telefonu arayın herhangi bir nesneyle (muhtemelen bunu yapan nesne profil arası çağrı). Bu işlem, bu nesnenin bağlantı kurmayı deneyecek. Bu değer, önce çağrılmalıdır eşzamanlı çağrı yapıldığından emin olun. Bu, engellenmeyen bir çağrıdır. Dolayısıyla, bağlantıyı yeniden oluşturduğunuz zamana kadar (ya da mümkün olmayabilir) çağrınız. Bu durumda genel hata işleme davranışı geçerli olur.

Bir çağrıyı aradığınızda uygun profiller arası izinlere sahip değilseniz ProfileConnector#addConnectionHolder(Object) veya şu kullanıcılar için profil yok: bağlanmazsa hiçbir hata verilmez, ancak bağlı geri çağırma çağrıldı. İzin daha sonra verilirse veya diğer profil kullanılabilir olduğunda bağlantı kurulur ve geri arama çağrılır.

Alternatif olarak, ProfileConnector#connect(Object) bir engelleme yöntemidir. öğesini bir bağlantı tutucu olarak ekler ve bir bağlantı kurar veya UnavailableProfileException atın. Bu yöntem şuradan çağrılamaz: kullanıcı arayüzü iş parçacığı.

ProfileConnector#connect(Object) ve benzeri aramalar ProfileConnector#connect, otomatik kapanan nesneleri döndürür. Bu işlem, otomatik olarak Kapattıktan sonra bağlantı tutucuyu çıkarın. Bu, aşağıdaki gibi kullanıma olanak tanır:

try (ProfileConnectionHolder p = connector.connect()) {
  // Use the connection
}

Eşzamanlı aramaları yapmayı tamamladığınızda, ProfileConnector#removeConnectionHolder(Object) Tüm bağlantı sahipleri, kaldırılırsa bağlantı kapanacaktır.

Bağlantı

Bir bağlantı dinleyicisi, bağlantı durumu oluşturulduğunda bilgilendirilmek için kullanılabilir ve bir değişiklik olup olmadığını belirlemek için connector.utils().isConnected bağlantısı olduğundan emin olun. Örneğin:

// Only use this if using synchronous calls instead of Futures.
crossProfileConnector.connect(this);
crossProfileConnector.registerConnectionListener(() -> {
  if (crossProfileConnector.utils().isConnected()) {
    // Make cross-profile calls.
  }
});

Eşzamansız Çağrılar

Profilde açığa çıkan her yöntem engelleme olarak belirtilmelidir (eşzamanlı) veya engellemeyen (eşzamansız) olmalıdır. Bir eşzamansız veri türü (ör. ListenableFuture) veya geri çağırmayı kabul ediyor parametresinin engellemesi yok olarak işaretlenir. Diğer tüm yöntemler engelleme olarak işaretlenir.

Eşzamansız çağrıların kullanılması önerilir. Eşzamanlı çağrıları kullanmanız gerekiyorsa Eşzamanlı Aramalar.

Geri çağırma işlevleri

Engellemeyen çağrının en temel türü, geçersiz bir yöntem olan geçersiz kılma yöntemidir. yardımcı olur. Bu arayüzlerin SDK ile çalışması için arayüzün @CrossProfileCallback şeklinde ek açıklama eklendi. Örneğin:

@CrossProfileCallback
public interface InstallationCompleteListener {
  void installationComplete(int state);
}

Bu arayüz, daha sonra ek açıklamalı @CrossProfile öğesinde parametre olarak kullanılabilir yöntemini kullanın ve her zamanki gibi çağrılır. Örneğin:

@CrossProfile
public void install(String filename, InstallationCompleteListener callback) {
  // Do something on a separate thread and then:
  callback.installationComplete(1);
}

// In the mediator
profileInstaller.work().install(filename, (status) -> {
  // Deal with callback
}, (exception) -> {
  // Deal with possibility of profile unavailability
});

Bu arayüz sıfır ya da bir alan tek bir yöntem içeriyorsa parametrelerinden sonra aynı anda birden fazla profile yapılan çağrılarda da kullanılabilir.

Geri çağırma kullanılarak istenilen sayıda değer iletilebilir, ancak bağlantı yalnızca ilk değer için açık tutulur. Aşağıdakilerle ilgili bilgi için Bağlantı Tutucuları konusuna bakın: bağlantıyı açık tutarak daha fazla değer alabilirsiniz.

Geri çağırma ile eşzamanlı yöntemler

SDK ile geri çağırma kullanmanın alışılmadık bir özelliği, teknik olarak geri çağırma kullanan eşzamanlı bir yöntem yaz:

public void install(InstallationCompleteListener callback) {
  callback.installationComplete(1);
}

Bu durumda, yöntem aslında geri çağırmaya rağmen eşzamanlıdır. Bu kodu doğru şekilde yürütülür:

System.out.println("This prints first");
installer.install(() -> {
        System.out.println("This prints second");
});
System.out.println("This prints third");

Ancak bu, SDK kullanılarak çağrıldığında aynı şekilde çalışmaz. Her biri 100'den az gösterim alan yükleme yönteminin "Bu baskılar"dan önce çağrılacağını üçüncü" yazdırılır. SDK tarafından eşzamansız olarak işaretlenen bir yöntemin tüm kullanımları, yöntemin ne zaman çağrılacağı konusunda varsayımlarda bulunmayın.

Basit Geri Aramalar

"Basit geri çağırmalar" daha kısıtlayıcı bir geri çağırma biçimidir. ek özelliklerden yararlanabilirsiniz. Basit arayüzler Sıfır veya bir parametre alabilen tek bir yöntem içermelidir.

Bir geri çağırma arayüzünün kalması gerektiğini belirterek @CrossProfileCallback ek açıklamasındaki simple=true.

Basit geri çağırmalar .both(), .suppliers(), ve diğerleri.

Bağlantı Tutucular

Eşzamansız bir çağrı yaparken (geri çağırma ya da vadeli işlemler kullanarak) çağrı yapılırken bağlantı tutucu eklenir ve veya bir değer geçirildiğinden emin olun.

Geri çağırma kullanılarak birden fazla sonucun aktarılmasını bekliyorsanız geri çağırmayı manuel olarak bir bağlantı sahibi olarak ekleyin:

MyCallback b = //...
connector.addConnectionHolder(b);

  profileMyClass.other().registerListener(b);

  // Now the connection will be held open indefinitely, once finished:
  connector.removeConnectionHolder(b);

Bu, "kaynakları deneyin" bloğuyla da kullanılabilir:

MyCallback b = //...
try (ProfileConnectionHolder p = connector.addConnectionHolder(b)) {
  profileMyClass.other().registerListener(b);

  // Other things running while we expect results
}

Geri arama üzerinden veya ileride sesli arama yaparsak bağlantı açık kalır. devam ettirilir. Bir sonucun iletilmeyeceğini belirlersek geri çağırma işlevini veya future işlevini bağlantı tutucusu olarak kaldırırız:

connector.removeConnectionHolder(myCallback);
connector.removeConnectionHolder(future);

Daha fazla bilgi için Bağlantı Tutucular başlıklı makaleyi inceleyin.

Vadeli İşlemler

Vadeli anlaşmalar, SDK tarafından yerel olarak da desteklenir. Yerel olarak desteklenen tek Gelecekteki tür ListenableFuture, ancak özel gelecek emin olmanız gerekir. Gelecekleri kullanmak için desteklenen bir Future türünü, profiller arası bir yöntemin dönüş türü olarak tanımlamanız ve ardından normal şekilde kullanmanız yeterlidir.

Bunda da aynı "olağan dışı özellik" var kullanabilirsiniz. Burada, eş zamanlı bir yöntem değeri döndüren bir değer (ör. immediateFuture kullanılması) farklı şekilde davranır mevcut profilde çalıştırıldığında ve başka bir profilde yapıldığını varsayalım. yöntemindeki herhangi bir varsayımda bulunmamalıdır ve çağrılır.

Sohbetler

Profiller arası bir gelecek veya ana makinede geri arama bir sonucu için engelleme yapmayın thread. Bunu yaparsanız, bazı durumlarda kodunuz bir koruyabilmelidir. Bunun nedeni, diğer profille olan bağlantının da ana iş parçacığında oluşturulur. Bu işlem, beklemede olması durumunda hiçbir zaman gerçekleşmez sağlarsınız.

Kullanılabilirlik

Müsaitlik durumu dinleyicisi, müsaitlik durumu gerçekleştiğinde bilgi almak için kullanılabilir ve başka bir öğe olup olmadığını belirlemek için connector.utils().isAvailable kullanılabilir profili kullanılabilir. Örneğin:

crossProfileConnector.registerAvailabilityListener(() -> {
  if (crossProfileConnector.utils().isAvailable()) {
    // Show cross-profile content
  } else {
    // Hide cross-profile content
  }
});

Bağlantı Tutucular

Bağlantı sahipleri, profiller arası bağlantının kurulmasına ve sürdürülmesine olan ilgi.

Varsayılan olarak, eşzamansız çağrılar yaparken bir bağlantı sahibi eklenir çağrı başladığında ve herhangi bir sonuç veya hata oluştuğunda kaldırılır.

Daha fazla kontrol sağlamak için Bağlantı Tutucular manuel olarak da eklenebilir ve kaldırılabilir fark etmiyorsunuz. Bağlantı sahipleri, connector.addConnectionHolder ve şunlar kullanılarak kaldırıldı: connector.removeConnectionHolder.

En az bir bağlantı sahibi eklendiğinde SDK, bağlantıyı korumanız gerekir. Sıfır bağlantı sahibi eklendiğinde, bağlantı kapatılabilir.

Eklediğiniz tüm bağlantı sahiplerine bir referans vermeli ve bunu kaldırmalısınız artık alakalı olmadığında.

Eşzamanlı aramalar

Eşzamanlı çağrılar yapmadan önce bir bağlantı tutucu eklenmelidir. Bu ancak bunu yapabilmeniz için o nesneyi izlemelisiniz, artık eşzamanlı çağrılar yapmanıza gerek kalmadığında kaldırılır.

Eşzamansız çağrılar

Eşzamansız arama yaparken bağlantı sahipleri otomatik olarak yönetilir . Bunun ötesinde hayatta kalmak için bağlantıya ihtiyacınız varsa (örneğin, çok sayıda tek bir geri arama kullanarak yanıt veriyorsa) geri çağırmanın kendisini bağlantı tutucusunu silin ve başka veri almaya gerek kalmadığında dışı verilerdir.

Hata İşleme

Varsayılan olarak, diğer profil seçili değilken diğer profile yapılan aramalar bir UnavailableProfileException değerinin atılmasına (veya veya eşzamansız bir çağrı için hata geri çağırması olabilir.

Geliştiriciler bunu önlemek için #both() veya #suppliers() kullanabilir ve herhangi bir sayıdaki girişle ilgilenmek için aşağıdaki kodu kullanabilirsiniz (bu sayı, diğer profil kullanılamıyor veya varsa 2).

İstisnalar

Mevcut profile yapılan bir çağrıdan sonra ortaya çıkan işaretli olmayan istisnalar normal bir şekilde yayılır. Bu, her bir anahtar kelime için arayın (#current(), #personal, #both vb.).

Diğer profile yapılan çağrıdan sonra ortaya çıkan işaretlenmemiş istisnalar gibi orijinal istisnayla bir ProfileRuntimeException atılır. pek çok yolu vardır. Bu, çağrıyı yapmak için kullanılan yöntemden (#other(), #personal, #both vb.).

ifAvailable

UnavailableProfileException yakalanıp onunla başa çıkmaya alternatif olarak isterseniz varsayılan değer sağlamak için .ifAvailable() yöntemini kullanabilirsiniz bu değer, UnavailableProfileException hatası vermek yerine döndürülür.

Örneğin:

profileNotesDatabase.other().ifAvailable().getNumberOfNotes(/* defaultValue= */ 0);

Test

Kodunuzu test edilebilir hale getirmek için profilinizin örneklerini eklemeniz gerekir bağlayıcısı olan herhangi bir kod (profilin kullanılabilirliğini kontrol etmek, manuel olarak bağlanın vb.). Ayrıca, verilerinizin bilgi sahibi olmaları gerekir.

Testlerde kullanılabilecek sahte bağlayıcılarınızı ve türlerini sunuyoruz.

İlk olarak test bağımlılıklarını ekleyin:

  testAnnotationProcessor
'com.google.android.enterprise.connectedapps:connectedapps-processor:1.1.2'
  testCompileOnly
'com.google.android.enterprise.connectedapps:connectedapps-testing-annotations:1.1.2'
  testImplementation
'com.google.android.enterprise.connectedapps:connectedapps-testing:1.1.2'

Ardından, test sınıfınıza @CrossProfileTest kodunu ekleyerek Test edilecek @CrossProfileConfiguration ek açıklamalı sınıf:

@CrossProfileTest(configuration = MyApplication.class)
@RunWith(RobolectricTestRunner.class)
public class NotesMediatorTest {

}

Bu, yapılandırma.

Testinizde bu sahte örnekleri oluşturun:

private final FakeCrossProfileConnector connector = new
FakeCrossProfileConnector();
private final NotesManager personalNotesManager = new NotesManager(); //
real/mock/fake
private final NotesManager workNotesManager = new NotesManager(); // real/mock/fake

private final FakeProfileNotesManager profileNotesManager =
  FakeProfileNotesManager.builder()
    .personal(personalNotesManager)
    .work(workNotesManager)
    .connector(connector)
    .build();

Profil durumunu ayarlama:

connector.setRunningOnProfile(PERSONAL);
connector.createWorkProfile();
connector.turnOffWorkProfile();

Sahte bağlayıcıyı ve profiller arası sınıfını test edilen kodunuza geçirin ve telefon edebilir.

Aramalar doğru hedefe yönlendirilir ve bağlantısı kesilen veya kullanılamayan profilleri aramak.

Desteklenen Türler

Aşağıdaki türler, sizden herhangi bir çaba gerektirmeden desteklenir. Bunlar kullanılmamalıdır.

  • Temel öğeler (byte, short, int, long, float, double, char, boolean),
  • Kutulu Temel Öğeler (java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double, java.lang.Character, java.lang.Boolean, java.lang.Void),
  • java.lang.String,
  • android.os.Parcelable öğesini uygulayan herhangi bir şey,
  • java.io.Serializable öğesini uygulayan herhangi bir şey,
  • Tek boyutlu primitif olmayan diziler
  • java.util.Optional,
  • java.util.Collection,
  • java.util.List,
  • java.util.Map,
  • java.util.Set,
  • android.util.Pair,
  • com.google.common.collect.ImmutableMap.

Desteklenen genel türler (ör. java.util.Collection) herhangi bir desteklenen türü seçin. Örneğin:

java.util.Collection<java.util.Map<java.lang.String,MySerializableType[]>> geçerli bir tür olarak kabul edilir.

Vadeli İşlemler

Aşağıdaki türler yalnızca dönüş türü olarak desteklenir:

  • com.google.common.util.concurrent.ListenableFuture

Özel Paketlenebilir Ambalajlar

Türünüz önceki listede yoksa öncelikle android.os.Parcelable veya java.io.Serializable öğelerini doğru şekilde uygulayabilirsiniz. Eğer parcelable sarmalayıcıları türünüz için destek ekleyin.

Gelecekteki Özel Sarmalayıcılar

Önceki listede bulunmayan gelecek bir türü kullanmak isterseniz gelecekte sarmalayıcılar ekleyin.

Paketlenebilir sarmalayıcılar

Ayrıştırılabilir Sarmalayıcılar, SDK'nın ayrıştırılabilir olmayan öğeler için destek ekleme şeklidir. izin modudur. SDK, birçok doküman için sarmalayıcılar içerir types, ancak dahil değil, kendi başlığınızı yazmanız gerekiyor.

Paketlenebilir Sarmalayıcı, başka bir sınıfı sarmalamak ve her bir sınıf için ayrıştırılabilir. Tanımlanmış bir statik sözleşmeye uygun ve SDK'ya kayıtlıdır Böylece, belirli bir türü ayrıştırılabilir bir türe dönüştürmek için kullanılabilir ve ayrıca, bu türü ayrıştırılabilir türünden çıkarır.

Ek Açıklama

Paketlenebilir sarmalayıcı sınıfı için @CustomParcelableWrapper ek açıklaması olmalıdır. sarmalanmış sınıfı originalType olarak belirtir. Örneğin:

@CustomParcelableWrapper(originalType=ImmutableList.class)
``` ###
Format

Parcelable wrappers must implement `Parcelable` correctly, and must have a
static `W of(Bundler, BundlerType, T)` method which wraps the wrapped type and a
non-static `T get()` method which returns the wrapped type.

The SDK will use these methods to provide seamless support for the type.

### Bundler

To allow for wrapping generic types (such as lists and maps), the `of` method is
passed a `Bundler` which is capable of reading (using `#readFromParcel`) and
writing (using `#writeToParcel`) all supported types to a `Parcel`, and a
`BundlerType` which represents the declared type to be written.

`Bundler` and `BundlerType` instances are themselves parcelable, and should be
written as part of the parcelling of the parcelable wrapper, so that it can be
used when reconstructing the parcelable wrapper.

If the `BundlerType` represents a generic type, the type variables can be found
by calling `.typeArguments()`. Each type argument is itself a `BundlerType`.

For an example, see `ParcelableCustomWrapper`:

```java
public class CustomWrapper<F> {
  private final F value;

  public CustomWrapper(F value) {
    this.value = value;
  }
  public F value() {
    return value;
  }
}

@CustomParcelableWrapper(originalType = CustomWrapper.class)
public class ParcelableCustomWrapper<E> implements Parcelable {

  private static final int NULL = -1;
  private static final int NOT_NULL = 1;

  private final Bundler bundler;
  private final BundlerType type;
  private final CustomWrapper<E> customWrapper;

  /**
  *   Create a wrapper for a given {@link CustomWrapper}.
  *
  *   <p>The passed in {@link Bundler} must be capable of bundling {@code F}.
  */
  public static <F> ParcelableCustomWrapper<F> of(
      Bundler bundler, BundlerType type, CustomWrapper<F> customWrapper) {
    return new ParcelableCustomWrapper<>(bundler, type, customWrapper);
  }

  public CustomWrapper<E> get() {
    return customWrapper;
  }

  private ParcelableCustomWrapper(
      Bundler bundler, BundlerType type, CustomWrapper<E> customWrapper) {
    if (bundler == null || type == null) {
      throw new NullPointerException();
    }
    this.bundler = bundler;
    this.type = type;
    this.customWrapper = customWrapper;
  }

  private ParcelableCustomWrapper(Parcel in) {
    bundler = in.readParcelable(Bundler.class.getClassLoader());

    int presentValue = in.readInt();

    if (presentValue == NULL) {
      type = null;
      customWrapper = null;
      return;
    }

    type = (BundlerType) in.readParcelable(Bundler.class.getClassLoader());
    BundlerType valueType = type.typeArguments().get(0);

    @SuppressWarnings("unchecked")
    E value = (E) bundler.readFromParcel(in, valueType);

    customWrapper = new CustomWrapper<>(value);
  }

  @Override
  public void writeToParcel(Parcel dest, int flags) {
    dest.writeParcelable(bundler, flags);

    if (customWrapper == null) {
      dest.writeInt(NULL);
      return;
    }

    dest.writeInt(NOT_NULL);
    dest.writeParcelable(type, flags);
    BundlerType valueType = type.typeArguments().get(0);
    bundler.writeToParcel(dest, customWrapper.value(), valueType, flags);
  }

  @Override
  public int describeContents() {
    return 0;
  }

  @SuppressWarnings("rawtypes")
  public static final Creator<ParcelableCustomWrapper> CREATOR =
    new Creator<ParcelableCustomWrapper>() {
      @Override
      public ParcelableCustomWrapper createFromParcel(Parcel in) {
        return new ParcelableCustomWrapper(in);
      }

      @Override
      public ParcelableCustomWrapper[] newArray(int size) {
        return new ParcelableCustomWrapper[size];
      }
    };
}

SDK ile kaydolun

Oluşturduktan sonra, özel ayrıştırılabilir sarmalayıcınızı kullanmak için kaydetmeniz gerekir Google'dan bahsetmek istiyorum.

Bunu yapmak için, aşağıdakilerden birinde parcelableWrappers={YourParcelableWrapper.class} değerini belirtin: bir CustomProfileConnector ek açıklaması veya CrossProfile ek açıklaması gönderebilir.

Gelecekteki Sarmalayıcılar

Gelecekteki Sarmalayıcılar, SDK'nın profiller genelinde gelecekler için destek sağlama şeklidir. İlgili içeriği oluşturmak için kullanılan SDK varsayılan olarak ListenableFuture desteğini içerir ancak gelecektekiler için destek sunar farklı türleri de belirtebilirsiniz.

Future Wrapper, belirli bir Future türünü sarmalamak ve paketlemeyi SDK'da kullanılabilir. Tanımlanmış bir statik sözleşmeye uygun olmalıdır ve SDK'da kayıtlı olduğundan emin olun.

Ek Açıklama

Gelecekteki sarmalayıcı sınıfına, şunu belirten @CustomFutureWrapper ek açıklaması verilmelidir: originalType olarak sarmalanır. Örneğin:

@CustomFutureWrapper(originalType=SettableFuture.class)
``` ### Format

Future wrappers must extend
`com.google.android.enterprise.connectedapps.FutureWrapper`.

Future wrappers must have a static `W create(Bundler, BundlerType)` method which
creates an instance of the wrapper. At the same time this should create an
instance of the wrapped future type. This should be returned by a non-static `T`
`getFuture()` method. The `onResult(E)` and `onException(Throwable)` methods
must be implemented to pass the result or throwable to the wrapped future.

Future wrappers must also have a static `void writeFutureResult(Bundler,`
`BundlerType, T, FutureResultWriter<E>)` method. This should register with the
passed in future for results, and when a result is given, call
`resultWriter.onSuccess(value)`. If an exception is given,
`resultWriter.onFailure(exception)` should be called.

Finally, future wrappers must also have a static `T<Map<Profile, E>>`
`groupResults(Map<Profile, T<E>> results)` method which converts a map from
profile to future, into a future of a map from profile to result.
`CrossProfileCallbackMultiMerger` can be used to make this logic easier.

For example:

```java
/** A very simple implementation of the future pattern used to test custom future
wrappers. */
public class SimpleFuture<E> {
  public static interface Consumer<E> {
    void accept(E value);
  }
  private E value;
  private Throwable thrown;
  private final CountDownLatch countDownLatch = new CountDownLatch(1);
  private Consumer<E> callback;
  private Consumer<Throwable> exceptionCallback;

  public void set(E value) {
    this.value = value;
    countDownLatch.countDown();
    if (callback != null) {
      callback.accept(value);
    }
  }

  public void setException(Throwable t) {
    this.thrown = t;
    countDownLatch.countDown();
    if (exceptionCallback != null) {
      exceptionCallback.accept(thrown);
    }
  }

  public E get() {
    try {
      countDownLatch.await();
    } catch (InterruptedException e) {
      eturn null;
    }
    if (thrown != null) {
      throw new RuntimeException(thrown);
    }
    return value;
  }

  public void setCallback(Consumer<E> callback, Consumer<Throwable>
exceptionCallback) {
    if (value != null) {
      callback.accept(value);
    } else if (thrown != null) {
      exceptionCallback.accept(thrown);
    } else {
      this.callback = callback;
      this.exceptionCallback = exceptionCallback;
    }
  }
}
/** Wrapper for adding support for {@link SimpleFuture} to the Connected Apps SDK.
*/
@CustomFutureWrapper(originalType = SimpleFuture.class)
public final class SimpleFutureWrapper<E> extends FutureWrapper<E> {

  private final SimpleFuture<E> future = new SimpleFuture<>();

  public static <E> SimpleFutureWrapper<E> create(Bundler bundler, BundlerType
bundlerType) {
    return new SimpleFutureWrapper<>(bundler, bundlerType);
  }

  private SimpleFutureWrapper(Bundler bundler, BundlerType bundlerType) {
    super(bundler, bundlerType);
  }

  public SimpleFuture<E> getFuture() {
    return future;
  }

  @Override
  public void onResult(E result) {
    future.set(result);
  }

  @Override
  public void onException(Throwable throwable) {
    future.setException(throwable);
  }

  public static <E> void writeFutureResult(
      SimpleFuture<E> future, FutureResultWriter<E> resultWriter) {

    future.setCallback(resultWriter::onSuccess, resultWriter::onFailure);
  }

  public static <E> SimpleFuture<Map<Profile, E>> groupResults(
      Map<Profile, SimpleFuture<E>> results) {
    SimpleFuture<Map<Profile, E>> m = new SimpleFuture<>();

    CrossProfileCallbackMultiMerger<E> merger =
        new CrossProfileCallbackMultiMerger<>(results.size(), m::set);
    for (Map.Entry<Profile, SimpleFuture<E>> result : results.entrySet()) {
      result
        .getValue()
        .setCallback(
          (value) -> merger.onResult(result.getKey(), value),
          (throwable) -> merger.missingResult(result.getKey()));
    }
    return m;
  }
}

SDK ile kaydolun

Oluşturulan gelecekteki özel sarmalayıcınızı kullanmak için SDK'yı kullanabilirsiniz.

Bunun için futureWrappers={YourFutureWrapper.class} öğesini Bir sınıfta CustomProfileConnector ek açıklaması veya CrossProfile ek açıklaması.

Doğrudan Başlatma modu

Uygulamanız doğrudan başlatmayı destekliyorsa mod ise profilin kilidi açılmadan önce profiller arası aramalar yapmanız gerekebilir. SDK, varsayılan olarak yalnızca diğer profilin kilidi açıkken bağlantılara izin verir.

Bu davranışı değiştirmek için, özel profil bağlayıcısı kullanıyorsanız belirtin availabilityRestrictions=AvailabilityRestrictions.DIRECT_BOOT_AWARE:

@GeneratedProfileConnector
@CustomProfileConnector(availabilityRestrictions=AvailabilityRestrictions.DIRECT_BO
OT_AWARE)
public interface MyProfileConnector extends ProfileConnector {
  public static MyProfileConnector create(Context context) {
    return GeneratedMyProfileConnector.builder(context).build();
  }
}

CrossProfileConnector kullanıyorsanız .setAvailabilityRestrictions(AvailabilityRestrictions.DIRECT_BOOT _AWARE açık en iyi uygulamadır.

Bu değişiklikle birlikte, kullanılabilirlik hakkında bilgilendirilecek ve karşılıklı olarak profil aramaları (diğer profilin kilidi kapalıyken) Bu sizin sorumluluğunuzdadır Böylece, aramalarınızın yalnızca cihazla şifrelenmiş depolama alanına eriştiğinden emin olabilirsiniz.