ส่วนเหล่านี้มีไว้สำหรับใช้อ้างอิง และคุณไม่จำเป็นต้องอ่านตั้งแต่ต้นจนจบ
ขอความยินยอมจากผู้ใช้
ใช้ API ของเฟรมเวิร์ก
CrossProfileApps.canInteractAcrossProfiles()
CrossProfileApps.canRequestInteractAcrossProfiles()
CrossProfileApps.createRequestInteractAcrossProfilesIntent()
CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED
API เหล่านี้จะรวมอยู่ใน SDK เพื่อให้แพลตฟอร์ม API ทำงานสอดคล้องกันมากขึ้น (เช่น การหลีกเลี่ยงออบเจ็กต์ UserHandle) แต่ตอนนี้คุณเรียกใช้ API เหล่านี้ได้โดยตรง
การติดตั้งใช้งานนั้นง่ายมาก หากโต้ตอบได้ ก็ดำเนินการได้เลย หากไม่ได้ แต่คุณขอได้ ให้แสดงพรอมต์/แบนเนอร์/เคล็ดลับเครื่องมือ/ฯลฯ แก่ผู้ใช้ หากผู้ใช้ตกลงที่จะไปที่การตั้งค่า ให้สร้าง Intent คำขอและใช้ Context#startActivity
เพื่อส่งผู้ใช้ไปที่นั่น คุณสามารถใช้การออกอากาศเพื่อตรวจหาเมื่อความสามารถนี้เปลี่ยนแปลง หรือจะตรวจสอบอีกครั้งเมื่อผู้ใช้กลับมาก็ได้
หากต้องการทดสอบ คุณต้องเปิด TestDPC ในโปรไฟล์งาน ไปที่ด้านล่างสุด แล้วเลือกเพิ่มชื่อแพ็กเกจลงในรายการที่อนุญาตของแอปที่เชื่อมต่อ ซึ่งจะทําให้ดูเหมือนว่าผู้ดูแลระบบ "เพิ่มแอปของคุณลงในรายการที่อนุญาต"
อภิธานศัพท์
ส่วนนี้กําหนดคําศัพท์สําคัญที่เกี่ยวข้องกับการพัฒนาข้ามโปรไฟล์
การกําหนดค่าข้ามโปรไฟล์
การกําหนดค่าข้ามโปรไฟล์จะจัดกลุ่มผู้ให้บริการข้ามโปรไฟล์ที่เกี่ยวข้องไว้ด้วยกัน และระบุการกําหนดค่าทั่วไปสําหรับฟีเจอร์ข้ามโปรไฟล์
โดยปกติแล้วจะมีคำอธิบายประกอบ @CrossProfileConfiguration
1 รายการต่อโค้ดเบส แต่ในบางแอปพลิเคชันที่ซับซ้อนอาจมีหลายรายการ
เครื่องมือเชื่อมต่อโปรไฟล์
ตัวเชื่อมต่อจะจัดการการเชื่อมต่อระหว่างโปรไฟล์ โดยปกติแล้ว โปรไฟล์ข้ามประเภทแต่ละประเภทจะชี้ไปยังตัวเชื่อมต่อที่เฉพาะเจาะจง โปรไฟล์ข้ามประเภททั้งหมดในการกําหนดค่าเดียวต้องใช้ตัวเชื่อมต่อเดียวกัน
คลาสผู้ให้บริการข้ามโปรไฟล์
คลาสผู้ให้บริการข้ามโปรไฟล์จะจัดกลุ่มประเภทข้ามโปรไฟล์ที่เกี่ยวข้องไว้ด้วยกัน
Mediator
ตัวสื่อกลางจะอยู่ระหว่างโค้ดระดับสูงและระดับต่ำ โดยกระจายการเรียกไปยังโปรไฟล์ที่ถูกต้องและผสานผลลัพธ์ นี่เป็นโค้ดเพียงรายการเดียวที่ต้องรับรู้โปรไฟล์ นี่เป็นแนวคิดเชิงสถาปัตยกรรม ไม่ใช่สิ่งที่สร้างไว้ใน SDK
ประเภทโปรไฟล์ข้าม
ประเภทข้ามโปรไฟล์คือคลาสหรืออินเทอร์เฟซที่มีเมธอดที่มีคำอธิบายประกอบ
@CrossProfile
โค้ดประเภทนี้ไม่จำเป็นต้องรับรู้โปรไฟล์และควรดำเนินการกับข้อมูลในเครื่องเท่านั้น
ประเภทโปรไฟล์
ประเภทโปรไฟล์ | |
---|---|
ปัจจุบัน | โปรไฟล์ที่ใช้งานอยู่ซึ่งเรากําลังดําเนินการ |
อื่นๆ | (หากมี) โปรไฟล์ที่เราไม่ได้ใช้งาน |
ส่วนตัว | ผู้ใช้ 0 คือโปรไฟล์ในอุปกรณ์ที่ปิดไม่ได้ |
ที่ทำงาน | โดยทั่วไปคือผู้ใช้ 10 แต่อาจสูงกว่านั้น เปิดและปิดได้ ใช้สำหรับเก็บแอปและข้อมูลงาน |
หลัก | แอปพลิเคชันกำหนดหรือไม่ก็ได้ โปรไฟล์ที่แสดงมุมมองที่ผสานรวมของทั้ง 2 โปรไฟล์ |
เสียงรอง | หากมีการระบุโปรไฟล์หลัก โปรไฟล์รองจะเป็นโปรไฟล์ที่ไม่ใช่โปรไฟล์หลัก |
ผู้จัดจำหน่าย | ผู้ให้บริการของโปรไฟล์หลักคือทั้ง 2 โปรไฟล์ ส่วนผู้ให้บริการของโปรไฟล์รองคือโปรไฟล์รองเท่านั้น |
ตัวระบุโปรไฟล์
คลาสที่แสดงถึงประเภทโปรไฟล์ (ส่วนตัวหรืองาน) ข้อมูลเหล่านี้จะแสดงโดยเมธอดที่ทำงานในหลายโปรไฟล์ และสามารถใช้เพื่อเรียกใช้โค้ดเพิ่มเติมในโปรไฟล์เหล่านั้น ซึ่งสามารถจัดเก็บเป็นint
เพื่อความสะดวก
โซลูชันที่แนะนำสำหรับสถาปัตยกรรม
คู่มือนี้จะอธิบายโครงสร้างที่แนะนําในการสร้างฟังก์ชันการทำงานข้ามโปรไฟล์ที่มีประสิทธิภาพและดูแลรักษาได้ภายในแอป Android
แปลง CrossProfileConnector
เป็น Singleton
คุณควรใช้อินสแตนซ์เดียวตลอดอายุการใช้งานของแอปพลิเคชัน ไม่เช่นนั้นคุณจะสร้างการเชื่อมต่อแบบขนาน ซึ่งทำได้โดยใช้เฟรมเวิร์กการฉีดข้อมูล Dependency Injection เช่น Dagger หรือใช้รูปแบบ Singleton แบบคลาสสิกในคลาสใหม่หรือคลาสที่มีอยู่
แทรกหรือส่งอินสแตนซ์ Profile ที่สร้างขึ้นไปยังคลาสเมื่อคุณทำการเรียกใช้แทนการสร้างในเมธอด
ซึ่งจะช่วยให้คุณส่งอินสแตนซ์ FakeProfile
ที่สร้างขึ้นโดยอัตโนมัติในยูนิตเทสต์ในภายหลังได้
ลองใช้รูปแบบสื่อกลาง
รูปแบบที่พบบ่อยนี้คือการทำให้ API ที่มีอยู่รายการใดรายการหนึ่ง (เช่น getEvents()
) รับรู้โปรไฟล์สำหรับผู้เรียก API ทั้งหมด ในกรณีนี้ API ที่มีอยู่สามารถเปลี่ยนเป็นเมธอดหรือคลาส "สื่อกลาง" ที่มีการเรียกใช้ใหม่เพื่อโค้ดข้ามโปรไฟล์ที่สร้างขึ้น
วิธีนี้จะช่วยให้คุณไม่ต้องบังคับให้ผู้เรียกทุกคนทราบเกี่ยวกับการเรียกข้ามโปรไฟล์ เพียงแค่ทำให้การเรียกข้ามโปรไฟล์เป็นส่วนหนึ่งของ API
พิจารณาว่าจะกำกับวิธีการของอินเทอร์เฟซเป็น @CrossProfile
แทนหรือไม่เพื่อหลีกเลี่ยงการเปิดเผยคลาสการใช้งานในผู้ให้บริการ
ซึ่งทำงานร่วมกับเฟรมเวิร์กการฉีดข้อมูลอย่างลงตัว
หากคุณได้รับข้อมูลจากคอลข้ามโปรไฟล์ ให้พิจารณาว่าจะเพิ่มช่องที่อ้างอิงถึงโปรไฟล์ต้นทางหรือไม่
ซึ่งอาจเป็นแนวทางปฏิบัติที่ดีเนื่องจากคุณอาจต้องการทราบข้อมูลนี้ที่เลเยอร์ UI (เช่น การเพิ่มไอคอนป้ายไปยังงาน) นอกจากนี้ คุณอาจต้องใช้แอตทริบิวต์นี้หากตัวระบุข้อมูลใดๆ จะไม่มีเอกลักษณ์อีกต่อไปหากไม่มีแอตทริบิวต์นี้ เช่น ชื่อแพ็กเกจ
ข้ามโปรไฟล์
ส่วนนี้จะอธิบายวิธีสร้างการโต้ตอบข้ามโปรไฟล์ของคุณเอง
โปรไฟล์หลัก
การเรียกใช้ส่วนใหญ่ในตัวอย่างในเอกสารนี้มีวิธีการที่ชัดเจนเกี่ยวกับโปรไฟล์ที่จะเรียกใช้ ซึ่งรวมถึงโปรไฟล์งาน โปรไฟล์ส่วนตัว และทั้ง 2 โปรไฟล์
ในทางปฏิบัติ สําหรับแอปที่รวมประสบการณ์การใช้งานไว้ในโปรไฟล์เดียว คุณอาจต้องการให้การตัดสินใจนี้ขึ้นอยู่กับโปรไฟล์ที่ใช้อยู่ ดังนั้นจึงมีวิธีการที่สะดวกซึ่งคล้ายกันซึ่งพิจารณาเรื่องนี้ด้วย เพื่อไม่ให้ฐานโค้ดเต็มไปด้วยเงื่อนไขโปรไฟล์แบบ if-else
เมื่อสร้างอินสแตนซ์ของเครื่องมือเชื่อมต่อ คุณสามารถระบุประเภทโปรไฟล์ที่จะเป็น "หลัก" ของคุณ (เช่น "WORK") ซึ่งจะเปิดใช้ตัวเลือกเพิ่มเติม เช่น ตัวเลือกต่อไปนี้
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();
ประเภทข้อมูลข้ามโปรไฟล์
คลาสและอินเทอร์เฟซที่มีเมธอดที่มีคำอธิบายประกอบ @CrossProfile
จะเรียกว่าประเภทข้ามโปรไฟล์
การใช้งานประเภทโปรไฟล์ข้ามโปรไฟล์ไม่ควรขึ้นอยู่กับโปรไฟล์ที่ใช้ วิธีการเหล่านี้ได้รับอนุญาตให้เรียกใช้เมธอดอื่นๆ และโดยทั่วไปควรทํางานเหมือนทํางานในโปรไฟล์เดียว โดยจะมีสิทธิ์เข้าถึงสถานะในโปรไฟล์ของตนเองเท่านั้น
ตัวอย่างประเภทโปรไฟล์ข้าม
public class Calculator {
@CrossProfile
public int add(int a, int b) {
return a + b;
}
}
คําอธิบายประกอบของชั้นเรียน
คุณควรระบุเครื่องมือเชื่อมต่อสำหรับประเภทข้ามโปรไฟล์แต่ละประเภทดังต่อไปนี้เพื่อให้ได้ API ที่มีประสิทธิภาพสูงสุด
@CrossProfile(connector=MyProfileConnector.class)
public class Calculator {
@CrossProfile
public int add(int a, int b) {
return a + b;
}
}
ตัวเลือกนี้ไม่บังคับ แต่จะทำให้ API ที่สร้างขึ้นมีความเฉพาะเจาะจงมากขึ้นเกี่ยวกับประเภทและการตรวจสอบเวลาคอมไพล์จะเข้มงวดมากขึ้น
อินเทอร์เฟซ
การกำกับเนื้อหาประกอบวิธีการในอินเทอร์เฟซเป็น @CrossProfile
หมายความว่าคุณระบุว่ามีการใช้งานวิธีการนี้บางอย่างที่ควรเข้าถึงได้ทั่วทั้งโปรไฟล์
คุณสามารถแสดงผลการใช้งานอินเทอร์เฟซข้ามโปรไฟล์ในผู้ให้บริการข้ามโปรไฟล์ได้ ซึ่งหมายความว่าคุณระบุว่าการใช้งานนี้ควรเข้าถึงได้ข้ามโปรไฟล์ คุณไม่จำเป็นต้องใส่คำอธิบายประกอบในคลาสการใช้งาน
ผู้ให้บริการข้ามโปรไฟล์
ประเภทข้ามโปรไฟล์ทุกประเภทต้องระบุโดยเมธอดที่มีคำอธิบายประกอบ
@CrossProfileProvider
ระบบจะเรียกใช้เมธอดเหล่านี้ทุกครั้งที่มีการเรียกใช้ข้ามโปรไฟล์ ดังนั้นเราขอแนะนำให้คุณดูแลรักษา Singleton สำหรับแต่ละประเภท
ผู้ผลิต
ผู้ให้บริการต้องมีตัวสร้างแบบสาธารณะที่ไม่รับอาร์กิวเมนต์หรือรับอาร์กิวเมนต์ Context
รายการเดียว
วิธีการของผู้ให้บริการ
เมธอดของผู้ให้บริการต้องไม่มีอาร์กิวเมนต์หรือมีอาร์กิวเมนต์ Context
รายการเดียว
Dependency Injection
หากคุณใช้เฟรมเวิร์กการฉีดข้อมูล Dependency เช่น Dagger เพื่อจัดการ Dependency เราขอแนะนำให้คุณให้เฟรมเวิร์กดังกล่าวสร้างประเภทข้ามโปรไฟล์ตามปกติ แล้วจึงฉีดประเภทเหล่านั้นลงในคลาสผู้ให้บริการ จากนั้นเมธอด @CrossProfileProvider
จะแสดงอินสแตนซ์ที่แทรกเหล่านั้น
เครื่องมือเชื่อมต่อโปรไฟล์
การกําหนดค่าข้ามโปรไฟล์แต่ละรายการต้องมีตัวเชื่อมต่อโปรไฟล์รายการเดียว ซึ่งมีหน้าที่จัดการการเชื่อมต่อกับโปรไฟล์อื่น
ตัวเชื่อมต่อโปรไฟล์เริ่มต้น
หากมีการกำหนดค่าข้ามโปรไฟล์เพียงรายการเดียวในโค้ดเบส คุณก็หลีกเลี่ยงการสร้างเครื่องมือเชื่อมต่อโปรไฟล์ของคุณเองได้และใช้com.google.android.enterprise.connectedapps.CrossProfileConnector
ซึ่งเป็นค่าเริ่มต้นที่ใช้หากไม่ได้ระบุ
เมื่อสร้างเครื่องมือเชื่อมต่อข้ามโปรไฟล์ คุณจะระบุตัวเลือกบางอย่างในเครื่องมือสร้างได้ ดังนี้
บริการผู้ดำเนินการตามกำหนดการ
หากต้องการควบคุมเธรดที่ SDK สร้างขึ้น ให้ใช้
#setScheduledExecutorService()
แฟ้ม
หากมีความต้องการเฉพาะเกี่ยวกับการเชื่อมโยงโปรไฟล์ ให้ใช้
#setBinder
ผู้ใช้ประเภทนี้น่าจะเป็นผู้ควบคุมนโยบายอุปกรณ์เท่านั้น
เครื่องมือเชื่อมต่อโปรไฟล์ที่กำหนดเอง
คุณจะต้องมีตัวเชื่อมต่อโปรไฟล์ที่กำหนดเองจึงจะกำหนดค่าบางอย่างได้ (โดยใช้ CustomProfileConnector
) และจะต้องมีตัวเชื่อมต่อดังกล่าวหากต้องการตัวเชื่อมต่อหลายตัวในโค้ดฐานเดียว (เช่น หากมีกระบวนการหลายรายการ เราขอแนะนำให้ใช้ตัวเชื่อมต่อ 1 ตัวต่อกระบวนการ)
เมื่อสร้าง ProfileConnector
ลักษณะควรเป็นดังนี้
@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
หากต้องการเปลี่ยนชื่อบริการที่สร้างขึ้น (ซึ่งควรอ้างอิงใน
AndroidManifest.xml
) ให้ใช้serviceClassName=
primaryProfile
หากต้องการระบุโปรไฟล์หลัก ให้ใช้
primaryProfile
availabilityRestrictions
หากต้องการเปลี่ยนข้อจำกัดที่ SDK กำหนดไว้สำหรับการเชื่อมต่อและความพร้อมใช้งานของโปรไฟล์ ให้ใช้
availabilityRestrictions
เครื่องมือควบคุมนโยบายด้านอุปกรณ์
หากแอปของคุณเป็นเครื่องมือควบคุมนโยบายด้านอุปกรณ์ คุณต้องระบุอินสแตนซ์ของ
DpcProfileBinder
ที่อ้างอิง DeviceAdminReceiver
หากคุณใช้เครื่องมือเชื่อมต่อโปรไฟล์ของคุณเอง ให้ทำดังนี้
@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();
}
}
หรือใช้ CrossProfileConnector
เริ่มต้น
CrossProfileConnector connector =
CrossProfileConnector.builder(context).setBinder(new DpcProfileBinder(new
ComponentName("com.google.testdpc", "AdminReceiver"))).build();
การกําหนดค่าข้ามโปรไฟล์
คําอธิบายประกอบ @CrossProfileConfiguration
ใช้เพื่อลิงก์ประเภทข้ามโปรไฟล์ทั้งหมดเข้าด้วยกันโดยใช้เครื่องมือเชื่อมต่อเพื่อส่งการเรียกเมธอดอย่างถูกต้อง โดยเราจะกำกับเนื้อหาของคลาสด้วย @CrossProfileConfiguration
ซึ่งชี้ไปยังผู้ให้บริการทุกราย ดังนี้
@CrossProfileConfiguration(providers = {TestProvider.class})
public abstract class TestApplication {
}
ซึ่งจะตรวจสอบว่าประเภทโปรไฟล์ข้ามทั้งหมดมีเครื่องมือเชื่อมต่อโปรไฟล์เดียวกันหรือไม่ระบุเครื่องมือเชื่อมต่อ
serviceSuperclass
โดยค่าเริ่มต้น บริการที่สร้างขึ้นจะใช้
android.app.Service
เป็นซุปเปอร์คลาส หากต้องการใช้คลาสอื่น (ซึ่งต้องเป็นคลาสย่อยของandroid.app.Service
) เป็นซุปเปอร์คลาส ให้ระบุserviceSuperclass=
serviceClass
หากระบุไว้ ระบบจะไม่สร้างบริการ ซึ่งต้องตรงกับ
serviceClassName
ในเครื่องมือเชื่อมต่อโปรไฟล์ที่คุณใช้ บริการที่กําหนดเองควรส่งการเรียกใช้โดยใช้คลาส_Dispatcher
ที่สร้างขึ้นดังนี้
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;
}
}
ซึ่งจะใช้ได้ในกรณีที่คุณต้องดําเนินการเพิ่มเติมก่อนหรือหลังการเรียกใช้ข้ามโปรไฟล์
ผู้ให้บริการเชื่อมต่อ
หากคุณใช้ตัวเชื่อมต่ออื่นที่ไม่ใช่
CrossProfileConnector
เริ่มต้น คุณต้องระบุตัวเชื่อมต่อนั้นโดยใช้connector=
ระดับการแชร์
ทุกส่วนของแอปพลิเคชันซึ่งโต้ตอบข้ามโปรไฟล์ต้องมองเห็นเครื่องมือเชื่อมต่อโปรไฟล์
@CrossProfileConfiguration
คลาสที่มีคำอธิบายประกอบต้องเห็นผู้ให้บริการทุกรายที่ใช้ในแอปพลิเคชัน
การเรียกแบบซิงโครนัส
Connected Apps SDK รองรับการเรียกแบบซิงค์ (การบล็อก) สำหรับกรณีที่หลีกเลี่ยงไม่ได้ อย่างไรก็ตาม การใช้การเรียกเหล่านี้มีข้อเสียหลายประการ (เช่น มีโอกาสที่การเรียกจะบล็อกเป็นเวลานาน) เราจึงขอแนะนำให้คุณหลีกเลี่ยงการเรียกแบบซิงค์เมื่อเป็นไปได้ สําหรับการใช้การโทรแบบไม่พร้อมกัน โปรดดูการโทรแบบไม่พร้อมกัน
ตัวยึดการเชื่อมต่อ
หากใช้การเรียกแบบซิงค์ คุณต้องตรวจสอบว่ามีโฮลเดอร์การเชื่อมต่อที่ลงทะเบียนไว้ก่อนที่จะทำการเรียกข้ามโปรไฟล์ ไม่เช่นนั้นระบบจะแสดงข้อยกเว้น ดูข้อมูลเพิ่มเติมได้ที่ผู้ถือการเชื่อมต่อ
หากต้องการเพิ่มผู้ถือการเชื่อมต่อ ให้เรียกใช้ ProfileConnector#addConnectionHolder(Object)
กับออบเจ็กต์ใดก็ได้ (อาจเป็นอินสแตนซ์ออบเจ็กต์ที่ทำการโทรข้ามโปรไฟล์) ซึ่งจะบันทึกว่าออบเจ็กต์นี้กำลังใช้การเชื่อมต่อและจะพยายามเชื่อมต่อ คุณต้องเรียกใช้สิ่งนี้ก่อนการเรียกใช้แบบซิงค์ การเรียกนี้เป็นแบบไม่บล็อก จึงเป็นไปได้ที่การเชื่อมต่อจะไม่พร้อม (หรืออาจไม่พร้อม) เมื่อคุณทำการเรียก ซึ่งในกรณีนี้ ลักษณะการประมวลผลข้อผิดพลาดตามปกติจะมีผล
หากคุณไม่มีสิทธิ์ข้ามโปรไฟล์ที่เหมาะสมเมื่อเรียกใช้ ProfileConnector#addConnectionHolder(Object)
หรือไม่มีโปรไฟล์ที่พร้อมเชื่อมต่อ ระบบจะไม่แสดงข้อผิดพลาด แต่จะไม่เรียกใช้การเรียกกลับที่เชื่อมต่อ หากได้รับสิทธิ์ในภายหลังหรือโปรไฟล์อื่นพร้อมใช้งานแล้ว ระบบจะเชื่อมต่อและเรียกใช้การติดต่อกลับ
หรือ ProfileConnector#connect(Object)
เป็นเมธอดการบล็อกที่จะเพิ่มออบเจ็กต์เป็นผู้ถือการเชื่อมต่อและสร้างการเชื่อมต่อหรือแสดง UnavailableProfileException
เรียกเมธอดนี้จาก
เธรด UI ไม่ได้
การเรียกใช้ ProfileConnector#connect(Object)
และ ProfileConnector#connect
ที่คล้ายกันจะแสดงผลออบเจ็กต์ที่ปิดโดยอัตโนมัติ ซึ่งจะนำตัวยึดการเชื่อมต่อออกโดยอัตโนมัติเมื่อปิด ซึ่งช่วยให้ใช้ในกรณีต่อไปนี้ได้
try (ProfileConnectionHolder p = connector.connect()) {
// Use the connection
}
เมื่อเรียกใช้การเรียกแบบซิงค์เสร็จแล้ว คุณควรเรียกใช้
ProfileConnector#removeConnectionHolder(Object)
เมื่อนำผู้ถือการเชื่อมต่อทั้งหมดออกแล้ว การเชื่อมต่อจะปิดลง
การเชื่อมต่อ
คุณสามารถใช้ตัวรับฟังการเชื่อมต่อเพื่อรับแจ้งเมื่อสถานะการเชื่อมต่อมีการเปลี่ยนแปลง และสามารถใช้ connector.utils().isConnected
เพื่อระบุว่ามีการเชื่อมต่ออยู่หรือไม่ เช่น
// Only use this if using synchronous calls instead of Futures.
crossProfileConnector.connect(this);
crossProfileConnector.registerConnectionListener(() -> {
if (crossProfileConnector.utils().isConnected()) {
// Make cross-profile calls.
}
});
การเรียกแบบอะซิงโครนัส
วิธีการทั้งหมดที่แสดงในโปรไฟล์ต้องได้รับการระบุว่าเป็นการบล็อก (แบบซิงค์) หรือไม่บล็อก (แบบแอซิงค์) เมธอดใดก็ตามที่แสดงผลประเภทข้อมูลแบบแอซิงโครนัส (เช่น ListenableFuture
) หรือยอมรับพารามิเตอร์การเรียกกลับจะได้รับการทําเครื่องหมายว่าไม่ใช่แบบบล็อก ส่วนวิธีการอื่นๆ ทั้งหมดจะทําเครื่องหมายเป็นการบล็อก
เราขอแนะนำให้ใช้การเรียกแบบอะซิงโครนัส หากต้องใช้การเรียกแบบซิงค์ โปรดดูการเรียกแบบซิงค์
การติดต่อกลับ
การเรียกแบบไม่บล็อกประเภทพื้นฐานที่สุดคือเมธอด void ซึ่งยอมรับอินเทอร์เฟซที่มีเมธอดที่จะเรียกใช้พร้อมกับผลลัพธ์เป็นหนึ่งในพารามิเตอร์ หากต้องการให้อินเทอร์เฟซเหล่านี้ทํางานร่วมกับ SDK อินเทอร์เฟซต้องได้รับการอธิบายประกอบ @CrossProfileCallback
เช่น
@CrossProfileCallback
public interface InstallationCompleteListener {
void installationComplete(int state);
}
จากนั้นอินเทอร์เฟซนี้จะใช้เป็นพารามิเตอร์ใน@CrossProfile
เมธอดที่มีคำอธิบายประกอบได้ และเรียกใช้ได้ตามปกติ เช่น
@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
});
หากอินเทอร์เฟซนี้มีเมธอดเดียวซึ่งใช้พารามิเตอร์ 0 หรือ 1 รายการ ก็จะสามารถใช้ในการเรียกใช้โปรไฟล์หลายรายการพร้อมกันได้
คุณสามารถส่งค่าจํานวนเท่าใดก็ได้โดยใช้การเรียกกลับ แต่การเชื่อมต่อจะเปิดอยู่สําหรับค่าแรกเท่านั้น ดูข้อมูลเกี่ยวกับการเปิดการเชื่อมต่อไว้เพื่อรับค่าเพิ่มเติมได้ที่ตัวยึดการเชื่อมต่อ
เมธอดแบบซิงโครนัสที่มี Callback
ฟีเจอร์ที่ผิดปกติอย่างหนึ่งของการใช้คอลแบ็กกับ SDK คือคุณสามารถเขียนเมธอดแบบซิงค์ที่ใช้คอลแบ็กได้ ดังนี้
public void install(InstallationCompleteListener callback) {
callback.installationComplete(1);
}
ในกรณีนี้ เมธอดเป็นแบบซิงค์ แม้ว่าจะมีคอลแบ็กก็ตาม โค้ดนี้จะทํางานอย่างถูกต้อง
System.out.println("This prints first");
installer.install(() -> {
System.out.println("This prints second");
});
System.out.println("This prints third");
อย่างไรก็ตาม เมื่อเรียกใช้โดยใช้ SDK การดำเนินการนี้จะทำงานไม่เหมือนกัน ไม่มีการรับประกันว่าจะมีการเรียกใช้วิธีการติดตั้งก่อนที่จะพิมพ์ "This prints third" การใช้เมธอดที่ SDK ทำเครื่องหมายเป็น "แบบไม่พร้อมกัน" ต้องไม่คาดเดาว่าระบบจะเรียกใช้เมธอดเมื่อใด
Callback แบบง่าย
"การเรียกกลับแบบง่าย" เป็นการเรียกกลับแบบจํากัดมากขึ้น ซึ่งอนุญาตให้ใช้ฟีเจอร์เพิ่มเติมเมื่อทําการเรียกข้ามโปรไฟล์ อินเทอร์เฟซแบบง่ายต้องมีเมธอดเดียว ซึ่งใช้พารามิเตอร์ได้ 0 หรือ 1 รายการ
คุณสามารถบังคับให้อินเทอร์เฟซการเรียกกลับต้องยังคงอยู่ได้โดยระบุ simple=true
ในการกำกับเนื้อหา @CrossProfileCallback
คุณสามารถใช้งาน Callback แบบง่ายกับเมธอดต่างๆ เช่น .both()
, .suppliers()
และอื่นๆ
ตัวยึดการเชื่อมต่อ
เมื่อทำการเรียกแบบไม่พร้อมกัน (โดยใช้การเรียกกลับหรือฟิวเจอร์) ระบบจะเพิ่มตัวเก็บการเชื่อมต่อเมื่อทำการเรียก และนำออกเมื่อมีการยกเว้นหรือส่งค่า
หากคาดว่าจะมีการส่งผลลัพธ์มากกว่า 1 รายการโดยใช้การเรียกกลับ คุณควรเพิ่มการเรียกกลับด้วยตนเองเป็นผู้ถือการเชื่อมต่อ ดังนี้
MyCallback b = //...
connector.addConnectionHolder(b);
profileMyClass.other().registerListener(b);
// Now the connection will be held open indefinitely, once finished:
connector.removeConnectionHolder(b);
สามารถใช้กับบล็อก try-with-resources ได้ด้วย
MyCallback b = //...
try (ProfileConnectionHolder p = connector.addConnectionHolder(b)) {
profileMyClass.other().registerListener(b);
// Other things running while we expect results
}
หากเราโทรด้วยระบบการโทรกลับหรือในอนาคต การเชื่อมต่อจะเปิดอยู่จนกว่าจะได้ผลลัพธ์ หากเราพิจารณาว่าผลลัพธ์จะไม่ผ่าน เราควรนําการเรียกกลับหรืออนาคตออกในฐานะผู้ถือการเชื่อมต่อ
connector.removeConnectionHolder(myCallback);
connector.removeConnectionHolder(future);
ดูข้อมูลเพิ่มเติมได้ที่ผู้ถือการเชื่อมต่อ
สัญญาซื้อขายล่วงหน้า
นอกจากนี้ SDK ยังรองรับฟิวเจอร์สแบบเนทีฟด้วย ประเภทอนาคตที่รองรับแบบดั้งเดิมมีเพียง ListenableFuture
เท่านั้น แต่คุณใช้ประเภทอนาคตที่กำหนดเองได้
หากต้องการใช้ Future คุณเพียงแค่ประกาศประเภท Future ที่รองรับเป็นประเภทผลลัพธ์ของเมธอดข้ามโปรไฟล์ แล้วนำไปใช้ตามปกติ
การดำเนินการนี้มี "ฟีเจอร์ที่ผิดปกติ" เช่นเดียวกับการเรียกกลับ โดยเมธอดแบบซิงค์ที่แสดงผลลัพธ์ในอนาคต (เช่น การใช้ immediateFuture
) จะทํางานแตกต่างกันเมื่อเรียกใช้ในโปรไฟล์ปัจจุบันกับเมื่อเรียกใช้ในโปรไฟล์อื่น การใช้เมธอดที่ SDK ทำเครื่องหมายว่าเป็นแบบไม่พร้อมกันต้องไม่คาดเดาว่าระบบจะเรียกใช้เมธอดเมื่อใด
ชุดข้อความ
อย่าบล็อกผลลัพธ์ของอนาคตข้ามโปรไฟล์หรือการเรียกกลับใน เธรดหลัก หากทําเช่นนี้ ในบางสถานการณ์ โค้ดจะบล็อกไว้อย่างไม่มีกำหนด เนื่องจากการเชื่อมต่อกับโปรไฟล์อื่นจะสร้างขึ้นบนเธรดหลักด้วย ซึ่งจะไม่เกิดขึ้นหากมีการบล็อกไว้เพื่อรอผลลัพธ์ข้ามโปรไฟล์
ความพร้อมใช้งาน
คุณสามารถรับฟังความพร้อมเพื่อรับแจ้งเมื่อสถานะความพร้อมมีการเปลี่ยนแปลงได้ และสามารถใช้ connector.utils().isAvailable
เพื่อระบุว่ามีโปรไฟล์อื่นให้ใช้งานหรือไม่ เช่น
crossProfileConnector.registerAvailabilityListener(() -> {
if (crossProfileConnector.utils().isAvailable()) {
// Show cross-profile content
} else {
// Hide cross-profile content
}
});
ตัวยึดการเชื่อมต่อ
ผู้ถือการเชื่อมต่อคือออบเจ็กต์ที่กำหนดเองซึ่งบันทึกไว้ว่ามีความเกี่ยวข้องกับการเชื่อมต่อข้ามโปรไฟล์ที่กำลังสร้างและทำงานอยู่
โดยค่าเริ่มต้น เมื่อทำการเรียกแบบไม่พร้อมกัน ระบบจะเพิ่มตัวเก็บการเชื่อมต่อเมื่อการเรียกเริ่มต้นขึ้น และนำออกเมื่อมีผลลัพธ์หรือข้อผิดพลาดเกิดขึ้น
นอกจากนี้ คุณยังเพิ่มและนำผู้ถือการเชื่อมต่อออกด้วยตนเองได้เพื่อควบคุมการเชื่อมต่อได้มากขึ้น คุณสามารถเพิ่มผู้ถือการเชื่อมต่อได้โดยใช้ connector.addConnectionHolder
และนำออกได้โดยใช้ connector.removeConnectionHolder
เมื่อเพิ่มผู้ถือการเชื่อมต่ออย่างน้อย 1 รายแล้ว SDK จะพยายามรักษาการเชื่อมต่อไว้ เมื่อไม่มีการเพิ่มผู้ถือการเชื่อมต่อ ระบบจะปิดการเชื่อมต่อได้
คุณต้องเก็บข้อมูลอ้างอิงเกี่ยวกับผู้ถือการเชื่อมต่อที่คุณเพิ่มไว้ และนำข้อมูลอ้างอิงดังกล่าวออกเมื่อไม่เกี่ยวข้องแล้ว
การเรียกใช้แบบซิงโครนัส
ก่อนทำการเรียกแบบซิงค์ คุณควรเพิ่มตัวยึดการเชื่อมต่อ ซึ่งทําได้โดยใช้ออบเจ็กต์ใดก็ได้ แต่คุณต้องติดตามออบเจ็กต์นั้นเพื่อนำออกเมื่อไม่จําเป็นต้องเรียกใช้แบบซิงค์อีกต่อไป
การเรียกใช้แบบอะซิงโครนัส
เมื่อทำการเรียกแบบไม่พร้อมกัน ระบบจะจัดการผู้ถือการเชื่อมต่อโดยอัตโนมัติเพื่อให้การเชื่อมต่อเปิดอยู่ระหว่างการเรียกและคำตอบหรือข้อผิดพลาดแรก หากต้องการให้การเชื่อมต่ออยู่รอดหลังจากนี้ (เช่น เพื่อรับการตอบกลับหลายรายการโดยใช้การเรียกกลับรายการเดียว) คุณควรเพิ่มการเรียกกลับนั้นเองเป็นตัวเก็บการเชื่อมต่อ และนำออกเมื่อไม่จําเป็นต้องรับข้อมูลเพิ่มเติมอีกต่อไป
การจัดการข้อผิดพลาด
โดยค่าเริ่มต้น การเรียกใช้โปรไฟล์อื่นเมื่อโปรไฟล์นั้นไม่พร้อมใช้งานจะส่งผลให้ระบบแสดงข้อผิดพลาด UnavailableProfileException
(หรือส่งไปยัง Future หรือ Callback ข้อผิดพลาดสําหรับการเรียกใช้แบบแอสซิงค์)
หากต้องการหลีกเลี่ยงปัญหานี้ นักพัฒนาแอปสามารถใช้ #both()
หรือ #suppliers()
และเขียนโค้ดเพื่อจัดการกับรายการในรายการผลลัพธ์ที่มีจำนวนเท่าใดก็ได้ (ค่านี้จะเท่ากับ 1 หากโปรไฟล์อื่นไม่พร้อมใช้งาน หรือ 2 หากพร้อมใช้งาน)
ข้อยกเว้น
ข้อยกเว้นที่ไม่ได้เลือกซึ่งเกิดขึ้นหลังจากการเรียกใช้โปรไฟล์ปัจจุบันจะได้รับการนำไปใช้งานตามปกติ การดำเนินการนี้จะมีผลไม่ว่าคุณจะใช้วิธีการใดในการเรียกใช้ (#current()
, #personal
, #both
ฯลฯ)
ข้อยกเว้นที่ตรวจไม่พบซึ่งเกิดขึ้นหลังจากการเรียกใช้โปรไฟล์อื่นจะส่งผลให้มีการโยน ProfileRuntimeException
โดยมีข้อยกเว้นเดิมเป็นสาเหตุ การดำเนินการนี้จะมีผลไม่ว่าคุณจะใช้วิธีใดในการโทร (#other()
,
#personal
, #both
ฯลฯ)
ifAvailable
นอกเหนือจากการจับและจัดการกับอินสแตนซ์ UnavailableProfileException
แล้ว คุณสามารถใช้เมธอด .ifAvailable()
เพื่อระบุค่าเริ่มต้นซึ่งระบบจะแสดงผลแทนการโยน UnavailableProfileException
เช่น
profileNotesDatabase.other().ifAvailable().getNumberOfNotes(/* defaultValue= */ 0);
การทดสอบ
หากต้องการให้โค้ดทดสอบได้ คุณควรแทรกอินสแตนซ์ของคอนเน็กเตอร์โปรไฟล์ลงในโค้ดที่ใช้ (เพื่อตรวจสอบความพร้อมใช้งานของโปรไฟล์ เพื่อเชื่อมต่อด้วยตนเอง เป็นต้น) นอกจากนี้ คุณควรแทรกอินสแตนซ์ของประเภทที่รับรู้โปรไฟล์ไว้ที่จุดที่ใช้
เรามีตัวเชื่อมต่อและประเภทจำลองซึ่งสามารถใช้ในการทดสอบ
ก่อนอื่น ให้เพิ่มการพึ่งพาการทดสอบ
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'
จากนั้นกำกับเนื้อหาในคลาสทดสอบด้วย @CrossProfileTest
โดยระบุคลาสที่มีคำอธิบายประกอบ @CrossProfileConfiguration
ที่จะทดสอบ
@CrossProfileTest(configuration = MyApplication.class)
@RunWith(RobolectricTestRunner.class)
public class NotesMediatorTest {
}
ซึ่งจะทำให้เกิดการสร้างไฟล์จำลองสำหรับขั้วต่อทุกประเภทที่ใช้ในการกำหนดค่า
สร้างอินสแตนซ์ของรายการจำลองเหล่านั้นในการทดสอบ
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();
ตั้งค่าสถานะโปรไฟล์
connector.setRunningOnProfile(PERSONAL);
connector.createWorkProfile();
connector.turnOffWorkProfile();
ส่งตัวเชื่อมต่อปลอมและคลาสข้ามโปรไฟล์ไปยังโค้ดที่อยู่ระหว่างการทดสอบ แล้วทำการเรียกใช้
ระบบจะกำหนดเส้นทางการโทรไปยังเป้าหมายที่ถูกต้อง และระบบจะแสดงข้อยกเว้นเมื่อโทรไปยังโปรไฟล์ที่ตัดการเชื่อมต่อหรือใช้งานไม่ได้
ประเภทที่รองรับ
ระบบรองรับประเภทต่อไปนี้โดยที่คุณไม่ต้องดำเนินการใดๆ เพิ่มเติม รายการเหล่านี้สามารถใช้เป็นอาร์กิวเมนต์หรือประเภทผลลัพธ์สำหรับการเรียกข้ามโปรไฟล์ทั้งหมด
- องค์ประกอบพื้นฐาน (
byte
,short
,int
,long
,float
,double
,char
,boolean
) - รูปแบบพื้นฐานในกล่อง (
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
- ทุกอย่างที่ใช้
java.io.Serializable
- อาร์เรย์ที่ไม่ใช่แบบพื้นฐานมิติข้อมูลเดียว
java.util.Optional
java.util.Collection
java.util.List
java.util.Map
java.util.Set
android.util.Pair
com.google.common.collect.ImmutableMap
ประเภททั่วไปที่รองรับ (เช่น java.util.Collection
) อาจมีประเภทที่รองรับใดก็ได้เป็นพารามิเตอร์ประเภท เช่น
java.util.Collection<java.util.Map<java.lang.String,MySerializableType[]>>
เป็นประเภทที่ถูกต้อง
สัญญาซื้อขายล่วงหน้า
ระบบรองรับประเภทต่อไปนี้เป็นประเภทผลลัพธ์เท่านั้น
com.google.common.util.concurrent.ListenableFuture
Wrapper ของ Parcelable ที่กําหนดเอง
หากประเภทของคุณไม่อยู่ในรายการก่อนหน้านี้ ให้พิจารณาก่อนว่าสามารถทำให้ประเภทนั้นใช้ android.os.Parcelable
หรือ java.io.Serializable
ได้อย่างถูกต้องหรือไม่ หากไม่เห็นตัวห่อที่แยกส่วนได้เพื่อเพิ่มการรองรับประเภทของคุณ
Wrapper ของอนาคตที่กําหนดเอง
หากต้องการใช้ประเภทในอนาคตซึ่งไม่อยู่ในรายการก่อนหน้านี้ โปรดดูWrapper ในอนาคตเพื่อเพิ่มการรองรับ
Wrapper ที่แยกส่วนได้
Wrapper แบบแยกส่วนได้คือวิธีที่ SDK เพิ่มการรองรับประเภทที่ไม่แยกส่วนได้ซึ่งแก้ไขไม่ได้ SDK มี Wrapper หลายประเภท แต่หากไม่มีประเภทที่คุณต้องการใช้ คุณต้องเขียน Wrapper ของคุณเอง
Wrapper ที่แยกส่วนได้คือคลาสที่ออกแบบมาเพื่อรวมคลาสอื่นเข้าด้วยกันและทำให้แยกส่วนได้ โดยเป็นไปตามสัญญาแบบคงที่ที่กําหนดไว้และจดทะเบียนกับ SDK เพื่อให้ใช้แปลงประเภทหนึ่งๆ เป็นประเภทที่แยกเป็นแพ็กเกจได้ รวมถึงดึงประเภทนั้นออกจากประเภทที่แยกเป็นแพ็กเกจได้
หมายเหตุ
ต้องใส่คำอธิบายประกอบ @CustomParcelableWrapper
ให้กับคลาส Wrapper ที่แยกส่วนได้ โดยระบุคลาสที่รวมไว้เป็น originalType
เช่น
@CustomParcelableWrapper(originalType=ImmutableList.class)
รูปแบบ
Wrapper ของ Parcelable ต้องใช้ Parcelable
อย่างถูกต้อง และต้องมีทั้งวิธี W of(Bundler, BundlerType, T)
แบบคงที่ซึ่งรวมประเภทที่รวมไว้ และวิธี T get()
แบบไม่คงที่ซึ่งแสดงผลประเภทที่รวมไว้
SDK จะใช้วิธีการเหล่านี้เพื่อรองรับประเภทดังกล่าวอย่างราบรื่น
Bundler
หากต้องการอนุญาตให้ตัดข้อมูลประเภททั่วไป (เช่น รายการและแผนที่) ระบบจะส่ง Bundler
ไปยังเมธอด of
ซึ่งสามารถอ่าน (โดยใช้ #readFromParcel
) และเขียน (โดยใช้ #writeToParcel
) ประเภทที่รองรับทั้งหมดลงใน Parcel
และ BundlerType
ซึ่งแสดงถึงประเภทที่ประกาศไว้ที่จะเขียน
อินสแตนซ์ Bundler
และ BundlerType
เองก็แยกเป็นแพ็กเกจได้ และควรเขียนเป็นส่วนหนึ่งของการแยกเป็นแพ็กเกจของ Wrapper ที่แยกเป็นแพ็กเกจได้ เพื่อให้นำไปใช้ได้เมื่อสร้าง Wrapper ที่แยกเป็นแพ็กเกจได้อีกครั้ง
หาก BundlerType
แสดงถึงประเภททั่วไป ตัวแปรประเภทจะพบได้ด้วยการเรียกใช้ .typeArguments()
อาร์กิวเมนต์ประเภทแต่ละรายการจะเป็น BundlerType
เอง
โปรดดูตัวอย่างที่ ParcelableCustomWrapper
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
เมื่อสร้างแล้ว หากต้องการใช้ Wrapper ที่แยกชิ้นส่วนได้ซึ่งกําหนดเอง คุณจะต้องลงทะเบียน Wrapper นั้นกับ SDK
โดยระบุ parcelableWrappers={YourParcelableWrapper.class}
ในคำอธิบายประกอบ CustomProfileConnector
หรือคำอธิบายประกอบ CrossProfile
ในคลาส
Wrapper ในอนาคต
Wrapper ในอนาคตคือวิธีที่ SDK เพิ่มการรองรับฟีเจอร์ใหม่ในโปรไฟล์ต่างๆ SDK จะรองรับ ListenableFuture
โดยค่าเริ่มต้น แต่คุณเพิ่มการรองรับประเภทอื่นๆ ในอนาคตได้
Wrapper ของ Future คือคลาสที่ออกแบบมาเพื่อรวม Future ประเภทหนึ่งๆ และทำให้ SDK ใช้งานได้ โดยเป็นไปตามสัญญาแบบคงที่ที่กําหนดไว้และต้องลงทะเบียนกับ SDK
หมายเหตุ
คลาส Wrapper ในอนาคตต้องมีการกำกับเนื้อหา @CustomFutureWrapper
โดยระบุคลาสที่รวมไว้เป็น originalType
เช่น
@CustomFutureWrapper(originalType=SettableFuture.class)
รูปแบบ
Wrapper ในอนาคตต้องขยาย com.google.android.enterprise.connectedapps.FutureWrapper
Wrapper ในอนาคตต้องมีเมธอด W create(Bundler, BundlerType)
แบบคงที่ซึ่งสร้างอินสแตนซ์ของ Wrapper ในขณะเดียวกัน การดำเนินการนี้ควรสร้างอินสแตนซ์ของประเภทอนาคตที่รวมไว้ ผลลัพธ์นี้ควรแสดงโดยเมธอด T
getFuture()
ที่ไม่คงที่ ต้องใช้เมธอด onResult(E)
และ onException(Throwable)
เพื่อส่งผลลัพธ์หรือรายการที่ทำให้เกิดข้อยกเว้นไปยัง Future ที่รวมไว้
Wrapper ในอนาคตต้องมีเมธอด void writeFutureResult(Bundler,
BundlerType, T, FutureResultWriter<E>)
แบบคงที่ด้วย ซึ่งควรลงทะเบียนกับ passed in future สำหรับผลลัพธ์ และเมื่อได้ผลลัพธ์แล้ว ให้เรียกใช้ resultWriter.onSuccess(value)
หากมีข้อยกเว้น ก็ควรเรียกใช้ resultWriter.onFailure(exception)
สุดท้าย Wrapper ในอนาคตต้องมีเมธอด T<Map<Profile, E>>
groupResults(Map<Profile, T<E>> results)
แบบคงที่ด้วย ซึ่งจะแปลงการแมปจากโปรไฟล์เป็นอนาคตเป็นการแมปจากโปรไฟล์ไปยังผลลัพธ์ในอนาคต
คุณใช้ CrossProfileCallbackMultiMerger
เพื่อทําให้ตรรกะนี้ง่ายขึ้นได้
เช่น
/** A basic 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
เมื่อสร้างแล้ว หากต้องการใช้ Wrapper ในอนาคตที่กําหนดเอง คุณจะต้องลงทะเบียน Wrapper กับ SDK
โดยระบุ futureWrappers={YourFutureWrapper.class}
ในคำอธิบายประกอบ CustomProfileConnector
หรือคำอธิบายประกอบ CrossProfile
ในชั้นเรียน
โหมดการบูตโดยตรง
หากแอปรองรับโหมดการบูตโดยตรง คุณอาจต้องโทรข้ามโปรไฟล์ก่อนจึงจะปลดล็อกโปรไฟล์ได้ โดยค่าเริ่มต้น SDK จะอนุญาตการเชื่อมต่อก็ต่อเมื่อโปรไฟล์อื่นปลดล็อกอยู่เท่านั้น
หากต้องการเปลี่ยนลักษณะการทำงานนี้เมื่อใช้ตัวเชื่อมต่อโปรไฟล์ที่กำหนดเอง คุณควรระบุ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
ให้ใช้ .setAvailabilityRestrictions(AvailabilityRestrictions.DIRECT_BOOT
_AWARE
ในเครื่องมือสร้าง
การเปลี่ยนแปลงนี้จะช่วยให้คุณทราบความพร้อมและสามารถโทรข้ามโปรไฟล์ได้เมื่อโปรไฟล์อื่นไม่ได้ปลดล็อก คุณมีหน้าที่ตรวจสอบว่าการโทรเข้าถึงเฉพาะพื้นที่เก็บข้อมูลที่เข้ารหัสของอุปกรณ์เท่านั้น