شروع کردن فعالیتی دیگر، چه در برنامه شما باشد چه در برنامهای دیگر، لازم نیست عملیاتی یکطرفه باشد. همچنین میتوانید فعالیتی را شروع کنید و نتیجه را دریافت کنید. برای مثال، برنامهتان میتواند برنامه دوربین را راهاندازی کند و عکس گرفتهشده را بهعنوان نتیجه دریافت کند. یا ممکن است برنامه «مخاطبین» را راهاندازی کنید تا کاربر مخاطبی را انتخاب کند و سپس جزئیات مخاطب را بهعنوان نتیجه دریافت کنید.
While the underlying
startActivityForResult()
and
onActivityResult()
APIs are available on the Activity class on all API levels, Google strongly
recommends using the Activity Result APIs introduced in AndroidX
Activity
and Fragment classes.
Activity Result API مؤلفههایی برای ثبت نتیجه، راهاندازی فعالیتی که نتیجه را تولید میکند، و مدیریت نتیجه پساز ارسال آن توسط سیستم ارائه میدهد.
ثبت کردن پاسخ تماس برای نتیجه فعالیت
هنگام شروع فعالیتی برای نتیجه، ممکن است (و در موارد عملیاتهای با مصرف حافظه بالا مانند استفاده از دوربین، تقریباً قطعی است) که فرایند و فعالیت شما بهدلیل کمبود حافظه ازبین برود.
به همین دلیل، Activity Result APIها فراخوانی برگشت نتیجه را از جایی در کد شما که فعالیت دیگر را راهاندازی میکنید، جدا میکنند. ازآنجاییکه فراخوان برگشتی نتیجه باید هنگام بازآفرینی فرایند و فعالیت شما دردسترس باشد، فراخوان برگشتی باید هربار که فعالیت شما ایجاد میشود بدون قیدوشرط ثبت شود، حتی اگر منطق راهاندازی فعالیت دیگر فقط براساس ورودی کاربر یا منطق کسبوکار دیگری اتفاق بیفتد.
وقتی در
ComponentActivity یا
Fragment هستید، میاناهای برنامهسازی کاربردی Activity Result
یک میانای برنامهسازی کاربردی
registerForActivityResult()
برای ثبت فراخوانی برگشتی نتیجه ارائه میدهند. registerForActivityResult()
ActivityResultContract
و
ActivityResultCallback
را میگیرد و
ActivityResultLauncher
را برمیگرداند که از آن برای راهاندازی فعالیت دیگر استفاده میکنید.
ActivityResultContract نوع ورودی موردنیاز برای تولید نتیجه را
بههمراه نوع خروجی نتیجه تعریف میکند. میاناهای برنامهسازی کاربردی
قراردادهای پیشفرضی
برای کنشهای هدف پایه مثل عکس گرفتن، درخواست اجازهها، و غیره ارائه میدهند. همچنین میتوانید
قرارداد سفارشی ایجاد کنید.
ActivityResultCallback یک میانای تکروشی با یک
onActivityResult()
روش است که شیئی از نوع خروجی تعریفشده در
ActivityResultContract را میگیرد:
کاتلین
val getContent = registerForActivityResult(GetContent()) { uri: Uri? -> // Handle the returned Uri }
جاوا
// 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 اجرا میشود، همانطور که در مثال زیر نشان داده شده است:
کاتلین
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/*") } }
جاوا
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
را پیادهسازی کنید که ثبت قرارداد را همراه با راهاندازی راهانداز مدیریت میکند:
کاتلین
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() } } }
جاوا
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 برای دریافت
ریزعکس
تصویر استفاده میکند ممکن است مشابه کد زیر نوشته شود:
کاتلین
class MyFragment( private val registry: ActivityResultRegistry ) : Fragment() { val thumbnailLiveData = MutableLiveData<Bitmap?> val takePicture = registerForActivityResult(TakePicturePreview(), registry) { bitmap: Bitmap? -> thumbnailLiveData.setValue(bitmap) } // ... }
جاوا
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 را نشان میدهد:
کاتلین
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) } }
جاوا
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 را بهعنوان بخشی از فراخوانی برگشتی خود استخراج کنید،
همانطور که در مثال زیر نشان داده شده است:
کاتلین
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)) } }
جاوا
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)); } }); }