Bir etkinlikten sonuç alma

İster uygulamanızın içinden ister başka bir yerden başka bir etkinlik başlatma uygulamanızın tek yönlü bir işlem olması gerekmez. Bir etkinlik de başlatabilirsiniz. ve sonucu geri alırsınız. Örneğin, uygulamanız bir kamera uygulaması başlatabilir ve Böylece, çekilen fotoğrafı alabilirsiniz. Alternatif olarak Kişiler uygulamasını başlatabilirsiniz kullanıcının bir kişi seçmesi ve ardından kişiyi karar vermektir.

Temelde startActivityForResult() ve onActivityResult() API'ler Activity sınıfında tüm API düzeylerinde kullanılabilir. AndroidX'te sunulan Activity Result API'lerinin kullanılmasını önerir. Activity ve Fragment sınıfa sahip.

Activity Result API'leri bir sonuca kaydolmaya yönelik bileşenleri sağlar. Sonucu üreten etkinliği başlatmak ve sonuçtan sonra sistem tarafından gönderilir.

Bir etkinlik sonucu için geri çağırma kaydedin

Bir sonuç için etkinlik başlatırken bu mümkün kamera kullanımı gibi belleği yoğun bir şekilde kullanan işlemlerdir. işlemi tamamlamaz. Düşük bellek nedeniyle etkinliğiniz yok olur.

Bu nedenle, Activity Result API'leri sonucu ayırır. kodunuzda diğer etkinliği başlattığınız yerden çağrılıyor. Çünkü İşleminiz ve etkinliğiniz aşağıdaki durumlarda olduğunda geri çağırma işlevinin kullanılabilir olması gerekir: geri çağırmanın, oluşturduğunuz her yalnızca diğer etkinliğin başlatılmasının mantığıyla dahi olsa bir etkinlik oluşturulur. kullanıcı girişlerine veya diğer iş mantığına bağlı olarak gerçekleşir.

Bir ComponentActivity veya bir Fragment, Etkinlik Sonucu API'ler registerForActivityResult() Sonuç geri çağırmasını kaydetme API'si. registerForActivityResult() ActivityResultContract ve ActivityResultCallback ve ActivityResultLauncher başka bir aktiviteye başlamak için kullanacağınız araç var.

ActivityResultContract, sonuç üretmek için gereken giriş türünü tanımlar ve sonucun çıkış türüyle birlikte gösterilir. API'ler, varsayılan sözleşmeler Resim çekme, izin isteme gibi temel amaca yönelik işlemler için beklemeye gerek yoktur. Ayrıca transkriptinizi özel bir sözleşme oluşturabilirsiniz.

ActivityResultCallback, tek yöntemli arayüzdür. onActivityResult() yönteminde tanımlanan çıkış türündeki bir nesneyi alan 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
        }
});

Birden fazla etkinlik sonucu çağrınız varsa ve bunlardan birini kullanıyorsanız sözleşmeler veya ayrı geri çağırmalar isterseniz registerForActivityResult() için birden çok kez kaydedin.ActivityResultLauncher Şunu yapmalısınız: her oluşturulduğunda registerForActivityResult() öğesini aynı sırayla çağırın parça veya etkinlik ile birlikte yayındaki sonuçların doğru geri arama.

registerForActivityResult(), parçanızdan veya etkinliğinizden önce güvenle çağrılabilir oluşturulur ve üye değişkenleri bildirilirken doğrudan kullanılmasına izin verilir döndürülen ActivityResultLauncher örnektir.

Sonuç için etkinlik başlat

registerForActivityResult() geri aramanızı kaydeder ancak kaydetmez diğer etkinliği başlatın ve sonuç isteğini başlatın. Bunun yerine, döndürülen ActivityResultLauncher örneğinin sorumluluğundadır.

Giriş mevcutsa başlatıcı ActivityResultContract Telefon etme launch() ve sonucu üretme sürecini başlatır. Kullanıcı etkinlikleri ve getirileri gösteren onActivityResult() Ardından ActivityResultCallback 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/*");
        }
    });
}

Şunun aşırı yüklenmiş sürümü: launch() bir ActivityOptionsCompat olduğunu düşünelim.

Etkinlik sonucunu ayrı bir sınıfta alın

ComponentActivity ve Fragment sınıfları ActivityResultCaller registerForActivityResult() API'ları kullanmanıza olanak tanıyan arayüzü kullanarak etkinlik sonucunu, Şunu kullanarak ActivityResultCaller: ActivityResultRegistry doğrudan ekleyebilirsiniz.

Örneğin, ekip arkadaşlarınızla birlikte çalışmak isteyebilirsiniz. LifecycleObserver bir sözleşmenin kaydedilmesi ve başlatıcının başlatılmasıyla ilgili işlemler:

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

Google, ActivityResultRegistry API'lerini kullanırken otomatik olarak LifecycleOwner gibi LifecycleOwner alan API'ler Lifecycle silindiğinde kayıtlı başlatıcınızı kaldırır. Ancak, LifecycleOwner öğesinin kullanılamadığı durumlarda, her bir ActivityResultLauncher sınıfta manuel olarak arama yapabilirsiniz unregister() kullanabilirsiniz.

Test

Varsayılan olarak, registerForActivityResult() otomatik olarak ActivityResultRegistry etkinliği tarafından sağlandığından emin olun. Ayrıca aşırı yüklenmeyi sağlayarak test etmek için kullanabileceğiniz, kendi ActivityResultRegistry örneğinizde aslında başka bir etkinlik başlatmadan etkinlik sonucu çağrıları.

Uygulamanızın parçalarını test ederken, kullanarak bir test ActivityResultRegistry sağlayın: Geçmek için FragmentFactory kaldı ActivityResultRegistry öğesini parçanın oluşturucusuna ekleyin.

Örneğin, TakePicturePreview sözleşmesini kullanan bir parçanın küçük resim aşağıdakine benzer bir resim 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 "the" onLaunch() yöntemidir. startActivityForResult() yöntemini çağırmak yerine testiniz uygulama, dispatchResult() testinizde kullanmak istediğiniz tam sonuçları sağlayarak:

val testRegistry = object : ActivityResultRegistry() {
    override fun <I, O> onLaunch(
            requestCode: Int,
            contract: ActivityResultContract<I, O>,
            input: I,
            options: ActivityOptionsCompat?
    ) {
        dispatchResult(requestCode, expectedResult)
    }
}

Eksiksiz test beklenen sonucu verir, yeni bir test oluşturur ActivityResultRegistry, parçaya iletir, başlatıcıyı tetikler test API'lerini kullanarak veya Espresso gibi diğer test API'lerini kullanarak sonuçlar:

@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 iken bir dizi önceden oluşturulmuş ActivityResultContract sınıfı içerirse İhtiyacınız olan tür açısından güvenli API'yi sağlayan kendi sözleşmelerinizi sağlayın.

Her ActivityResultContract, tanımlanmış giriş ve çıkış sınıfları gerektirir. giriş türü olarak Void kullanma herhangi bir giriş gerektirmez (Kotlin'de, Void? veya Unit'i kullanın).

Her sözleşme, createIntent() yöntemini kullanır. Bu yöntem, bir Context ile girdiyi alır ve şunu oluşturur: Intent kullanılıyor startActivityForResult() ile.

Her sözleşme ayrıca şunları da yapmalıdır: parseResult() Bu, verilen resultCode çıktısını oluşturur; örneğin, Activity.RESULT_OK veya Activity.RESULT_CANCELED ve Intent.

Sözleşmeler isteğe bağlı olarak getSynchronousResult() belirli bir girdinin sonucunun rastgele belirlenmeden createIntent() numaralı telefonu arayıp diğer etkinliği başlatmanız ve parseResult() tuşuna basın.

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şme imzalamaz. Bu, herhangi bir Intent girdisini alan ve şunu döndürür: ActivityResult, geri aramanızın bir parçası olarak resultCode ve Intent öğelerini çıkarmanıza olanak tanır. aşağıdaki örnekte gösterildiği gibi:

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