Uygulamanızdan veya başka bir uygulamadan başka bir etkinlik başlatmanın tek yönlü bir işlem olması gerekmez. Ayrıca bir etkinlik başlatıp geri bildirim alabilirsiniz. Örneğin, uygulamanız bir kamera uygulaması başlatabilir ve böylece çekilen fotoğrafı alabilir. Veya kullanıcının bir kişi seçmesi için Kişiler uygulamasını başlatabilir ve bunun sonucunda kişi ayrıntılarını alabilirsiniz.
Temel startActivityForResult()
ve onActivityResult()
API'leri tüm API düzeylerinde Activity
sınıfında bulunsa da Google, AndroidX Activity
ve Fragment
sınıflarında sunulan Activity Result API'lerinin kullanılmasını önemle tavsiye eder.
Etkinlik Sonucu API'leri bir sonuca kaydolma, sonucu başlatma ve sistem tarafından gönderildikten sonra işlemeyle ilgili bileşenler sağlar.
Bir etkinlik sonucu için geri çağırmayı kaydetme
Bir sonuçla ilgili bir etkinlik başlatırken, işlem ve etkinliğinizin bellek yetersiz olduğundan dolayı bozulması mümkündür (ve fotoğraf makinesi kullanımı gibi belleği yoğun bir şekilde kullanan işlemler söz konusu olduğunda).
Bu nedenle, Activity Result API'leri sonuç geri çağırmayı kodunuzda diğer etkinliği başlattığınız yerden ayırır. İşleminiz ve etkinliğiniz yeniden oluşturulduğunda sonuç geri çağırma işleminin kullanılabilir olması gerektiğinden, diğer etkinliği başlatma mantığı yalnızca kullanıcı girişi veya başka iş mantığına göre gerçekleşse bile, etkinliğiniz her oluşturulduğunda geri çağırmanın koşulsuz olarak kaydedilmesi gerekir.
ComponentActivity
veya Fragment
içindeyken Activity Result API'leri sonuç geri çağırmasını kaydetmek için bir registerForActivityResult()
API sağlar. registerForActivityResult()
, bir
ActivityResultContract
ve bir
ActivityResultCallback
değerini alır ve diğer etkinliği başlatmak için
kullanacağınız bir
ActivityResultLauncher
değerini döndürür.
ActivityResultContract
, bir sonuç üretmek için gereken giriş türünü ve sonucun çıkış türünü tanımlar. API'ler resim çekme, izin isteme gibi temel amaç işlemleri için varsayılan sözleşmeler sağlar. Özel bir sözleşme de oluşturabilirsiniz.
ActivityResultCallback
, ActivityResultContract
içinde tanımlanan çıkış türündeki bir nesneyi alan onActivityResult()
yöntemine sahip tek bir yöntem arayüzüdür:
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 } });
Birden fazla etkinlik sonucu çağrısı varsa ve farklı sözleşmeler kullanıyorsanız veya ayrı geri çağırma işlemleri istiyorsanız birden fazla ActivityResultLauncher
örneği kaydetmek için registerForActivityResult()
yöntemini birden fazla kez çağırabilirsiniz. Yayındaki sonuçların doğru geri çağırmaya teslim edilmesi için registerForActivityResult()
öğesini, parçanızın veya etkinliğinizin her oluşturulmasında aynı sırayla çağırmanız gerekir.
registerForActivityResult()
, parçanız veya etkinliğiniz oluşturulmadan önce güvenle çağrılabilir. Böylece, döndürülen ActivityResultLauncher
örnekleri için üye değişkenleri bildirilirken doğrudan kullanılabilir.
Sonuç için bir etkinlik başlat
registerForActivityResult()
geri aramanızı kaydederken, diğer etkinliği başlatmaz ve sonuç isteğini başlatmaz. Bunun yerine, döndürülen ActivityResultLauncher
örneğinin sorumluluğu budur.
Giriş varsa başlatıcı, ActivityResultContract
türüyle eşleşen girişi alır. launch()
çağrısı, sonucu oluşturma sürecini başlatır. Kullanıcı sonraki etkinliği tamamlayıp geri döndüğünde, ActivityResultCallback
öğesindeki onActivityResult()
aşağıdaki örnekte gösterildiği gibi yürütülür:
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()
'in aşırı yüklenmiş bir sürümü, girişe ek olarak bir ActivityOptionsCompat
geçmenize olanak tanır.
Ayrı bir sınıfta etkinlik sonucu al
ComponentActivity
ve Fragment
sınıfları, registerForActivityResult()
API'lerini kullanmanızı sağlamak için ActivityResultCaller
arayüzünü uygular. Ancak doğrudan ActivityResultRegistry
kullanarak etkinlik sonucunu, ActivityResultCaller
uygulanmayan ayrı bir sınıfla da alabilirsiniz.
Örneğin, başlatıcıyı başlatmanın yanı sıra sözleşme kaydetme işlemlerini de yapan bir LifecycleObserver
uygulamak isteyebilirsiniz:
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(); } }); } }
Lifecycle
kaldırıldığında LifecycleOwner
, kayıtlı başlatıcınızı otomatik olarak kaldırdığından Google, ActivityResultRegistry
API'lerini kullanırken LifecycleOwner
alan API'leri kullanmanızı önemle tavsiye eder. Bununla birlikte, LifecycleOwner
kullanılamadığında her ActivityResultLauncher
sınıfı alternatif olarak unregister()
öğesini manuel olarak çağırmanıza olanak tanır.
Test
Varsayılan olarak registerForActivityResult()
, etkinlik tarafından sağlanan ActivityResultRegistry
özelliğini otomatik olarak kullanır. Ayrıca, kendi ActivityResultRegistry
örneğinizi aktarmanıza olanak tanıyan bir aşırı yük de sağlar. Bu örneği, başka bir etkinlik başlatmadan etkinlik sonucu çağrılarınızı test etmek için kullanabilirsiniz.
Uygulamanızın parçalarını test ederken, ActivityResultRegistry
öğesini parçanın oluşturucusuna geçirmek için bir FragmentFactory
kullanarak test ActivityResultRegistry
sağlarsınız.
Örneğin, resmin küçük resmini almak için TakePicturePreview
sözleşmesini kullanan bir parça aşağıdakine benzer şekilde yazılabilir:
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; } // ... }
Teste özel bir ActivityResultRegistry
oluştururken onLaunch()
yöntemini uygulamanız gerekir. Test uygulamanız startActivityForResult()
yöntemini çağırmak yerine doğrudan dispatchResult()
yöntemini çağırarak testinizde kullanmak istediğiniz tam sonuçları sağlayabilir:
val testRegistry = object : ActivityResultRegistry() {
override fun <I, O> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
input: I,
options: ActivityOptionsCompat?
) {
dispatchResult(requestCode, expectedResult)
}
}
Tam test beklenen sonucu oluşturur, bir test ActivityResultRegistry
oluşturur, bunu parçaya iletir, başlatıcıyı doğrudan veya Espresso gibi diğer test API'lerini kullanarak tetikler ve daha sonra sonuçları doğrular:
@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)
}
}
}
Özel sözleşme oluşturun
ActivityResultContracts
, kullanım için önceden oluşturulmuş bir dizi ActivityResultContract
sınıfı içerir. Bununla birlikte, ihtiyaç duyduğunuz tür açısından güvenli API'yi sağlayan kendi sözleşmelerinizi de sunabilirsiniz.
Her ActivityResultContract
, tanımlı giriş ve çıkış sınıflarının olmasını gerektirir. Herhangi bir giriş gerekmiyorsa giriş türü olarak Void
kullanılır (Kotlin'de Void?
veya Unit
kullanın).
Her sözleşme, Context
ile girdiyi alıp startActivityForResult()
ile kullanılan Intent
öğesini oluşturan createIntent()
yöntemini uygulamalıdır.
Her sözleşme, belirtilen resultCode
'den çıktı üreten parseResult()
'ı da uygulamalıdır (ör. Activity.RESULT_OK
veya Activity.RESULT_CANCELED
ve Intent
).
Belirli bir giriş için sonucu createIntent()
çağırmaya, diğer etkinliği başlatmaya ve sonucu oluşturmak için parseResult()
kullanmaya gerek kalmadan belirlemek mümkünse sözleşmeleri isteğe bağlı olarak getSynchronousResult()
uygulayabilir.
Aşağıdaki örnekte ActivityResultContract
öğesinin nasıl oluşturulacağı gösterilmektedir:
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); } }
Özel bir sözleşmeye ihtiyacınız yoksa StartActivityForResult
sözleşmesini kullanabilirsiniz. Bu, herhangi bir Intent
öğesini girdi olarak alan ve bir ActivityResult
döndüren genel bir sözleşmedir. Aşağıdaki örnekte gösterildiği gibi, geri aramanızın bir parçası olarak resultCode
ve Intent
öğelerini çıkarmanızı sağlar:
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)); } }); }