Uzyskiwanie wyniku z aktywności

rozpoczynanie innej aktywności w aplikacji lub innej; aplikacji, nie musi to być operacja jednokierunkowa. Możesz też rozpocząć aktywność i otrzymać wynik. Aplikacja może na przykład uruchamiać aplikację aparatu, otrzyma zrobione zdjęcie. Możesz też uruchomić aplikację Kontakty wybranego przez użytkownika kontaktu, a następnie otrzymywać go dzięki czemu zyska dostęp do szczegółowych informacji.

Chociaż podstawowa wartość startActivityForResult() oraz onActivityResult() Interfejsy API są dostępne w klasie Activity na wszystkich poziomach API, zdecydowanie Google zaleca korzystanie z interfejsów API wyników związanych z aktywnością wprowadzonych w AndroidzieX Activity i Fragment.

Interfejsy API wyników działań zapewniają komponenty do rejestracji wyniku, uruchomienia działania, które przynosi oczekiwane rezultaty, i obsługi jego wyniku, jest wysyłana przez system.

Rejestrowanie wywołania zwrotnego dla wyniku działania

Rozpoczynając aktywność dla danego wyniku, jest to możliwe – a w przypadku operacje intensywnie korzystające z pamięci, takie jak korzystanie z aparatu, są prawie pewne, , a Twoja aktywność zostanie zniszczona z powodu małej ilości pamięci.

Z tego powodu interfejsy API wyników działań rozdzielają wynik wywołanie zwrotne w miejscu w kodzie, w którym uruchamiasz inne działanie. Ponieważ wywołanie zwrotne musi być dostępne, gdy proces i aktywność są wywołanie zwrotne musi być rejestrowane bezwarunkowo za każdym razem, gdy nawet jeśli logika uruchamiania innego działania ma tylko zależy od danych wejściowych użytkownika lub innej logiki biznesowej.

W ComponentActivity lub Fragment, wynik aktywności Interfejsy API zapewniają registerForActivityResult() Interfejs API do rejestrowania wywołania zwrotnego wyniku. registerForActivityResult() zajmuje ActivityResultContract. oraz ActivityResultCallback i zwraca ActivityResultLauncher której używasz do uruchamiania innych działań.

ActivityResultContract określa typ danych wejściowych potrzebny do wygenerowania wyniku wraz z typem wyniku. Interfejsy API zapewniają umowy domyślne do wykonywania podstawowych działań związanych z zamiarem, np. robienia zdjęć, wysyłania próśb o zgodę na przetwarzanie danych itp. włącz. Możesz też utworzyć niestandardową umowę.

ActivityResultCallback to interfejs jednometodowy z onActivityResult(). która przyjmuje obiekt typu wyjściowego zdefiniowanego w ActivityResultContract:

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

Jeśli masz wiele połączeń z wynikami aktywności i używasz różnych umowy lub chcesz mieć oddzielne wywołania zwrotne, możesz dzwonić do registerForActivityResult() wiele do zarejestrowania wielu instancji ActivityResultLauncher. Musisz wywołaj registerForActivityResult() w tej samej kolejności przy każdym tworzeniu fragment lub działanie, tak aby przesyłane wyniki były przesyłane poprawne wywołanie zwrotne.

Przed fragmentem lub działaniem można bezpiecznie zadzwonić pod numer registerForActivityResult() został utworzony, dzięki czemu można go używać bezpośrednio przy deklarowaniu zmiennych członkowskich dla zwróconych ActivityResultLauncher instancji.

Uruchom aktywność dla wyniku

registerForActivityResult() rejestruje wywołanie zwrotne, ale nie uruchomić inne działanie i uruchomić żądanie wyniku. Zamiast tego jest odpowiedzialna za zwrócona instancję ActivityResultLauncher.

Jeśli istnieją dane wejściowe, program uruchamiający przyjmuje dane wejściowe pasujące do typu ActivityResultContract Łączę launch() rozpoczyna proces generowania wyniku. Gdy użytkownik skończy korzystać z działanie i zwrot, onActivityResult() z Następnie jest wykonywane polecenie ActivityResultCallback, tak jak w tym przykładzie:

KotlinJava
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/*");
       
}
   
});
}

Przeciążona wersja aplikacji launch() umożliwia podanie ActivityOptionsCompat niż dane wejściowe.

Otrzymuj wyniki z aktywności na osobnych zajęciach

Chociaż klasy ComponentActivity i Fragment implementują makro ActivityResultCaller. interfejsu API registerForActivityResult(), możesz także otrzymają wynik działania do osobnej klasy, która nie implementuje ActivityResultCaller za pomocą ActivityResultRegistry bezpośrednio.

Możesz na przykład wdrożyć LifecycleObserver , który obsługuje rejestrację umowy przy uruchamianiu programu uruchamiającego:

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

Zdecydowanie zalecamy korzystanie z interfejsów API ActivityResultRegistry interfejsy API, które przyjmują LifecycleOwner automatycznie LifecycleOwner usuwa zarejestrowany program uruchamiający po zniszczeniu Lifecycle. Pamiętaj jednak: w przypadku, gdy interfejs LifecycleOwner jest niedostępny, każdy Klasa ActivityResultLauncher pozwala na ręczne wywołanie unregister(). jako alternatywę.

Test

Domyślnie registerForActivityResult() automatycznie używa ActivityResultRegistry dzięki aktywności. Jest to też przeciążenie, które pozwala w Twojej instancji ActivityResultRegistry, której możesz użyć do przetestowania wyniku aktywności bez uruchamiania kolejnej aktywności.

Podczas testowania fragmentów aplikacji udostępnij testowy element ActivityResultRegistry za pomocą FragmentFactory, aby zdać w ActivityResultRegistry do konstruktora fragmentu.

Na przykład fragment, który korzysta z umowy TakePicturePreview do uzyskania miniatura może wyglądać tak:

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

   
// ...
}

Tworząc tag ActivityResultRegistry przeznaczony do testów, musisz zaimplementować onLaunch(). . Zamiast wywoływać startActivityForResult(), Twój test implementacja może wywołać dispatchResult(). podając wyniki, których chcesz użyć w teście:

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

Ukończenie testu zapewnia oczekiwany wynik i konstruowanie testu ActivityResultRegistry, przekazuje go do fragmentu, uruchamia program uruchamiający bezpośrednio lub za pomocą innych testowych interfejsów API, takich jak Espresso, a następnie wyniki:

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

Utwórz umowę niestandardową

Podczas gdy ActivityResultContracts zawiera wiele gotowych klas ActivityResultContract do użytku, udostępniać własne umowy obejmujące dokładny bezpieczny typ interfejsu API, którego potrzebujesz.

Każdy element ActivityResultContract wymaga zdefiniowanych klas wejściowych i wyjściowych, używając Void jako typu danych wejściowych, jeśli nie wymagają żadnych danych wejściowych (w Kotlin użyj Void? lub Unit).

Każda umowa musi implementować createIntent() , która pobiera Context i dane wejściowe oraz tworzy obiekt Intent, który w użyciu dzięki startActivityForResult().

Każda umowa musi również obejmować parseResult(), , który generuje dane wyjściowe z danego resultCode, np. Activity.RESULT_OK lub Activity.RESULT_CANCELED oraz Intent.

Umowy można opcjonalnie stosować getSynchronousResult() jeśli można określić wynik dla danych wejściowych bez muszą zadzwonić do użytkownika createIntent(), rozpocząć inną aktywność i użyć parseResult(), aby uzyskać odpowiedni wynik.

Poniższy przykład pokazuje, jak utworzyć ActivityResultContract:

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

Jeśli nie potrzebujesz niestandardowej umowy, możesz użyć atrybutu StartActivityForResult umowy. To jest ogólna umowa, w której danymi wejściowymi jest dowolny Intent zwraca ActivityResult, pozwalając wyodrębnić wartości resultCode i Intent w ramach wywołania zwrotnego, jak w tym przykładzie:

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