بدء نشاط آخر، سواء كان واحدًا داخل تطبيقك أو من آخر تطبيق ولا يلزم أن تكون عملية في اتجاه واحد. يمكنك أيضًا بدء نشاط. وتتلقى النتيجة مرة أخرى. على سبيل المثال، يمكن لتطبيقك تشغيل تطبيق كاميرا تلقي الصورة التي تم التقاطها نتيجةً لذلك. أو يمكنك بدء تشغيل تطبيق جهات الاتصال لتحديد جهة اتصال، ثم تلقّي جهة الاتصال التفاصيل كنتيجة لذلك.
وفي حين أن القيمة الأساسية
startActivityForResult()
أو
onActivityResult()
تتوفر واجهات برمجة التطبيقات في الفئة Activity
على جميع مستويات واجهات برمجة التطبيقات، وتشد Google بشدة
باستخدام واجهات برمجة تطبيقات نتائج الأنشطة المقدّمة في AndroidX
Activity
وFragment
صفوف.
توفر واجهات برمجة التطبيقات لنتائج الأنشطة مكونات لتسجيل إحدى النتائج، وإطلاق النشاط الذي ينتج النتيجة، والتعامل مع النتيجة بمجرد عن طريق النظام.
تسجيل معاودة الاتصال لنتيجة نشاط
عند بدء نشاط لتحقيق نتيجة، يمكن - وفي حالات عند استخدام العمليات الكثيفة للذاكرة مثل استخدام الكاميرا، شبه من المؤكد أن سيتم التخلص من نشاطك بسبب انخفاض الذاكرة.
لهذا السبب، تفكك واجهات برمجة التطبيقات لنتيجة الأنشطة النتيجة معاودة الاتصال من المكان الموجود في الرمز الخاص بك حيث تقوم بتشغيل النشاط الآخر. لأنّ يجب أن تكون نتيجة معاودة الاتصال متاحة عندما تتوفر المعالجة والنشاط إعادة إنشائها، فيجب أن يتم تسجيل رد الاتصال دون شرط في كل مرة يتم إنشاء نشاط، حتى إذا كان منطق إطلاق النشاط الآخر بناءً على إدخالات المستخدم أو منطق الأعمال الأخرى.
عندما تكون في
ComponentActivity
أو
Fragment
، نتيجة النشاط
توفر واجهات برمجة التطبيقات
registerForActivityResult()
واجهة برمجة تطبيقات لتسجيل معاودة الاتصال بالنتيجة. registerForActivityResult()
ActivityResultContract
و
ActivityResultCallback
و تُرجع
ActivityResultLauncher
،
الذي تستخدمه لبدء النشاط الآخر.
تحدد السمة ActivityResultContract
نوع الإدخال المطلوب للحصول على نتيجة
بالإضافة إلى نوع الإخراج للنتيجة. توفر واجهات برمجة التطبيقات
العقود التلقائية
لاتخاذ إجراءات أساسية حسب النية بالشراء، مثل التقاط صورة وطلب الأذونات وغير ذلك
مفعَّلة. يمكنك أيضًا
إنشاء عقد مخصّص
ActivityResultCallback
هي واجهة ذات طريقة واحدة لها
onActivityResult()
تأخذ كائنًا من نوع الإخراج المحدد في
ActivityResultContract
:
Kotlin
val getContent = registerForActivityResult(GetContent()) { uri: Uri? -> // Handle the returned Uri }
Java
// GetContent creates an ActivityResultLauncher<String> to let you pass // in the mime type you want to let the user select ActivityResultLauncher<String> mGetContent = registerForActivityResult(new GetContent(), new ActivityResultCallback<Uri>() { @Override public void onActivityResult(Uri uri) { // Handle the returned Uri } });
إذا كانت لديك عدة مكالمات لنتائج النشاط وكنت تستخدم إما
العقود
أو تريد معاودة الاتصال بشكل منفصل، يمكنك الاتصال بـ registerForActivityResult()
عدة مرات
مرات لتسجيل عدة مثيلات من ActivityResultLauncher
. يجب
يمكنك طلب registerForActivityResult()
بالترتيب نفسه لكل عملية إنشاء
الجزء أو النشاط بحيث يتم عرض نتائج البحث عن
بشكل صحيح.
يمكنك الاتصال بـ "registerForActivityResult()
" بأمان قبل الفصل أو النشاط.
مما يتيح استخدامها مباشرةً عند الإعلان عن متغيرات العضو
لمثيلات الـ ActivityResultLauncher
التي تم عرضها
تشغيل نشاط للنتائج
على الرغم من أنّ registerForActivityResult()
يسجّل معاودة الاتصال، إلا أنه لا
وإطلاق النشاط الآخر وبدء طلب النتيجة. بدلاً من ذلك،
هي مسؤولية مثيل ActivityResultLauncher
الذي تم عرضه.
في حالة وجود إدخال، يأخذ المشغّل الإدخال الذي يطابق نوع
ActivityResultContract
إجراء المكالمات
launch()
عملية إنتاج النتيجة. عندما ينتهي المستخدم من
النشاط والإرجاع اللاحق، وهو onActivityResult()
من
يتم بعد ذلك تنفيذ ActivityResultCallback
، كما هو موضّح في المثال التالي:
Kotlin
val getContent = registerForActivityResult(GetContent()) { uri: Uri? -> // Handle the returned Uri } override fun onCreate(savedInstanceState: Bundle?) { // ... val selectButton = findViewById<Button>(R.id.select_button) selectButton.setOnClickListener { // Pass in the mime type you want to let the user select // as the input getContent.launch("image/*") } }
Java
ActivityResultLauncher<String> mGetContent = registerForActivityResult(new GetContent(), new ActivityResultCallback<Uri>() { @Override public void onActivityResult(Uri uri) { // Handle the returned Uri } }); @Override public void onCreate(@Nullable Bundle savedInstanceState) { // ... Button selectButton = findViewById(R.id.select_button); selectButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { // Pass in the mime type you want to let the user select // as the input mGetContent.launch("image/*"); } }); }
هناك إصدار زائد من
launch()
يسمح لك باجتياز
ActivityOptionsCompat
بالإضافة إلى المدخلات.
تلقّي نتيجة نشاط في صف منفصل
في حين أنّ الصفَّين ComponentActivity
وFragment
ينفّذان
ActivityResultCaller
للسماح لك باستخدام واجهات برمجة تطبيقات registerForActivityResult()
، يمكنك أيضًا
تلقي النشاط في فئة منفصلة لا تنفذ
ActivityResultCaller
باستخدام
ActivityResultRegistry
مباشرةً.
على سبيل المثال، قد ترغب في تنفيذ
LifecycleObserver
تعالج عملية تسجيل العقد عند إطلاق مشغّل التطبيقات:
Kotlin
class MyLifecycleObserver(private val registry : ActivityResultRegistry) : DefaultLifecycleObserver { lateinit var getContent : ActivityResultLauncher<String> override fun onCreate(owner: LifecycleOwner) { getContent = registry.register("key", owner, GetContent()) { uri -> // Handle the returned Uri } } fun selectImage() { getContent.launch("image/*") } } class MyFragment : Fragment() { lateinit var observer : MyLifecycleObserver override fun onCreate(savedInstanceState: Bundle?) { // ... observer = MyLifecycleObserver(requireActivity().activityResultRegistry) lifecycle.addObserver(observer) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val selectButton = view.findViewById<Button>(R.id.select_button) selectButton.setOnClickListener { // Open the activity to select an image observer.selectImage() } } }
Java
class MyLifecycleObserver implements DefaultLifecycleObserver { private final ActivityResultRegistry mRegistry; private ActivityResultLauncher<String> mGetContent; MyLifecycleObserver(@NonNull ActivityResultRegistry registry) { mRegistry = registry; } public void onCreate(@NonNull LifecycleOwner owner) { // ... mGetContent = mRegistry.register(“key”, owner, new GetContent(), new ActivityResultCallback<Uri>() { @Override public void onActivityResult(Uri uri) { // Handle the returned Uri } }); } public void selectImage() { // Open the activity to select an image mGetContent.launch("image/*"); } } class MyFragment extends Fragment { private MyLifecycleObserver mObserver; @Override void onCreate(Bundle savedInstanceState) { // ... mObserver = new MyLifecycleObserver(requireActivity().getActivityResultRegistry()); getLifecycle().addObserver(mObserver); } @Override void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { Button selectButton = findViewById(R.id.select_button); selectButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { mObserver.selectImage(); } }); } }
عند استخدام واجهات برمجة تطبيقات ActivityResultRegistry
، تنصح Google بشدة باستخدام
واجهات برمجة التطبيقات التي تستخدم السمة LifecycleOwner
، مثل السمة LifecycleOwner
تلقائيًا
إزالة مشغِّل التطبيقات المسجَّل عند تلف Lifecycle
. ومع ذلك،
في الحالات التي لا يتوفّر فيها LifecycleOwner
،
يتيح لك الصف ActivityResultLauncher
الاتصال يدويًا
unregister()
كبديل.
الاختبار
بشكل تلقائي، يستخدم registerForActivityResult()
تلقائيًا
ActivityResultRegistry
التي يقدمها النشاط. كما أنه يوفر حملاً زائدًا يتيح لك اجتياز
في نسختك الخاصة من ActivityResultRegistry
والتي يمكنك استخدامها لاختبار
نتيجة النشاط دون بدء أي نشاط آخر.
عند اختبار أجزاء تطبيقك،
قدِّم اختبار ActivityResultRegistry
باستخدام
FragmentFactory
تمريرة حاسمة
في ActivityResultRegistry
إلى الدالة الإنشائية للجزء.
على سبيل المثال، الجزء الذي يستخدم عقد TakePicturePreview
للحصول على
صورة مصغّرة
من الصورة على النحو التالي:
Kotlin
class MyFragment( private val registry: ActivityResultRegistry ) : Fragment() { val thumbnailLiveData = MutableLiveData<Bitmap?> val takePicture = registerForActivityResult(TakePicturePreview(), registry) { bitmap: Bitmap? -> thumbnailLiveData.setValue(bitmap) } // ... }
Java
public class MyFragment extends Fragment { private final ActivityResultRegistry mRegistry; private final MutableLiveData<Bitmap> mThumbnailLiveData = new MutableLiveData(); private final ActivityResultLauncher<Void> mTakePicture = registerForActivityResult(new TakePicturePreview(), mRegistry, new ActivityResultCallback<Bitmap>() { @Override public void onActivityResult(Bitmap thumbnail) { mThumbnailLiveData.setValue(thumbnail); } }); public MyFragment(@NonNull ActivityResultRegistry registry) { super(); mRegistry = registry; } @VisibleForTesting @NonNull ActivityResultLauncher<Void> getTakePicture() { return mTakePicture; } @VisibleForTesting @NonNull LiveData<Bitmap> getThumbnailLiveData() { return mThumbnailLiveData; } // ... }
عند إنشاء ActivityResultRegistry
خاصة بالاختبار، يجب تنفيذها
الـ
onLaunch()
. بدلاً من الاتصال بـ startActivityForResult()
، يمكنك إجراء الاختبار
التنفيذ على
dispatchResult()
مباشرةً، مع تقديم النتائج الدقيقة التي تريد استخدامها في الاختبار:
val testRegistry = object : ActivityResultRegistry() {
override fun <I, O> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
input: I,
options: ActivityOptionsCompat?
) {
dispatchResult(requestCode, expectedResult)
}
}
ينشئ الاختبار الكامل النتيجة المتوقعة، وينشئ اختبارًا
ActivityResultRegistry
، ينقله إلى الجزء، يؤدي إلى تشغيل مشغّل التطبيقات
إما مباشرةً أو باستخدام واجهات برمجة تطبيقات اختبارية أخرى مثل Espresso، ثم التحقق من
النتائج:
@Test
fun activityResultTest {
// Create an expected result Bitmap
val expectedResult = Bitmap.createBitmap(1, 1, Bitmap.Config.RGBA_F16)
// Create the test ActivityResultRegistry
val testRegistry = object : ActivityResultRegistry() {
override fun <I, O> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
input: I,
options: ActivityOptionsCompat?
) {
dispatchResult(requestCode, expectedResult)
}
}
// Use the launchFragmentInContainer method that takes a
// lambda to construct the Fragment with the testRegistry
with(launchFragmentInContainer { MyFragment(testRegistry) }) {
onFragment { fragment ->
// Trigger the ActivityResultLauncher
fragment.takePicture()
// Verify the result is set
assertThat(fragment.thumbnailLiveData.value)
.isSameInstanceAs(expectedResult)
}
}
}
إنشاء عقد مخصّص
في حين أنّ ActivityResultContracts
يحتوي على عدد من فئات ActivityResultContract
المنشأة مسبقًا للاستخدام، يمكنك
توفير عقودك الخاصة التي توفّر واجهة برمجة التطبيقات الآمنة والدقيقة التي تحتاجها.
تتطلب كل ActivityResultContract
فئات إدخال وإخراج محدّدة،
باستخدام Void
كنوع إدخال إذا كنت
ولا تتطلب أي إدخال (استخدِم في لغة Kotlin إما Void?
أو Unit
).
يجب أن ينفذ كل عقد
createIntent()
والتي تأخذ Context
والمدخل وتنشئ Intent
الذي
يُستخدم
مع startActivityForResult()
.
يجب أن ينفذ كل عقد أيضًا
parseResult()
،
التي تُنتج المخرجات من resultCode
المعين، مثل
Activity.RESULT_OK
أو Activity.RESULT_CANCELED
، وIntent
.
يمكن للعقود تنفيذ
getSynchronousResult()
ما إذا كان من الممكن تحديد نتيجة مدخل معين بدون
الاتصال بـ createIntent()
وبدء النشاط الآخر واستخدام
parseResult()
لإنشاء النتيجة.
يوضّح المثال التالي كيفية إنشاء ActivityResultContract
:
Kotlin
class PickRingtone : ActivityResultContract<Int, Uri?>() { override fun createIntent(context: Context, ringtoneType: Int) = Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply { putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType) } override fun parseResult(resultCode: Int, result: Intent?) : Uri? { if (resultCode != Activity.RESULT_OK) { return null } return result?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI) } }
Java
public class PickRingtone extends ActivityResultContract<Integer, Uri> { @NonNull @Override public Intent createIntent(@NonNull Context context, @NonNull Integer ringtoneType) { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, ringtoneType.intValue()); return intent; } @Override public Uri parseResult(int resultCode, @Nullable Intent result) { if (resultCode != Activity.RESULT_OK || result == null) { return null; } return result.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI); } }
إذا لم تكن بحاجة إلى عقد مخصّص، يمكنك استخدام
StartActivityForResult
العقد. هذا عقد عام يأخذ أي Intent
كمدخل
يُنتج
ActivityResult
,
للسماح لك باستخراج resultCode
وIntent
كجزء من معاودة الاتصال،
كما هو موضح في المثال التالي:
Kotlin
val startForResult = registerForActivityResult(StartActivityForResult()) { result: ActivityResult -> if (result.resultCode == Activity.RESULT_OK) { val intent = result.data // Handle the Intent } } override fun onCreate(savedInstanceState: Bundle) { // ... val startButton = findViewById(R.id.start_button) startButton.setOnClickListener { // Use the Kotlin extension in activity-ktx // passing it the Intent you want to start startForResult.launch(Intent(this, ResultProducingActivity::class.java)) } }
Java
ActivityResultLauncher<Intent> mStartForResult = registerForActivityResult(new StartActivityForResult(), new ActivityResultCallback<ActivityResult>() { @Override public void onActivityResult(ActivityResult result) { if (result.getResultCode() == Activity.RESULT_OK) { Intent intent = result.getData(); // Handle the Intent } } }); @Override public void onCreate(@Nullable savedInstanceState: Bundle) { // ... Button startButton = findViewById(R.id.start_button); startButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { // The launcher with the Intent you want to start mStartForResult.launch(new Intent(this, ResultProducingActivity.class)); } }); }