Bu bölümler referans amaçlıdır ve bunları okumanız gerekmez. yukarıdan aşağıya doğru.
Kullanıcı izni isteme
Çerçeve API'lerini kullanın:
CrossProfileApps.canInteractAcrossProfiles()
CrossProfileApps.canRequestInteractAcrossProfiles()
CrossProfileApps.createRequestInteractAcrossProfilesIntent()
CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED
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şisel | Kullanı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. |
İkincil | Birincil tanımlanmışsa, "ikincil", birincil değildir. |
Tedarikçi | Birincil 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.
Önerilen mimari çözümler
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 ofandroid.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ızconnector=
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.