Bir etkinlikten sonuç alma

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));
        }
    });
}