Eine neue Aktivität starten, unabhängig davon, ob sie in Ihrer App oder in einer anderen ausgeführt wird App, muss kein Einwegbetrieb sein. Sie können auch eine Aktivität starten, und ein Ergebnis erhalten. Ihre App kann z. B. eine Kamera-App starten und als Ergebnis das aufgenommene Foto erhalten. Oder Sie starten die Kontakte App um einen Kontakt auszuwählen und den Kontakt Details entsprechend gekennzeichnet.
Die zugrunde liegende
startActivityForResult()
und
onActivityResult()
APIs sind in der Activity
-Klasse auf allen API-Ebenen verfügbar.
empfiehlt die Verwendung der in AndroidX eingeführten Activity Result APIs.
Activity
und Fragment
Klassen.
Die Activity Result APIs bieten Komponenten zum Registrieren für ein Ergebnis, Starten der Aktivität, die das Ergebnis erzeugt, und Verarbeiten des Ergebnisses, sobald es vom System gesendet.
Callback für ein Aktivitätsergebnis registrieren
Wenn eine Aktivität für ein Ergebnis gestartet wird, ist es möglich – und im Fall von speicherintensive Vorgänge wie Kameranutzung, die mit ziemlicher Sicherheit bei der und Ihre Aktivität wird aufgrund von unzureichendem Arbeitsspeicher gelöscht.
Aus diesem Grund entkoppeln die Activity Result APIs das Ergebnis. an der Stelle im Code zurück, an der Sie die andere Aktivität gestartet haben. Weil Der Ergebnis-Callback muss verfügbar sein, wenn Ihr Prozess und Ihre Aktivität neu erstellt wurde, muss der Callback jedes Mal, wenn Ihr erstellt wird, auch wenn die Logik zum Starten der anderen Aktivität basierend auf Nutzereingaben oder anderer Geschäftslogik.
In einer
ComponentActivity
oder
Fragment
, das Aktivitätsergebnis
APIs bieten eine
registerForActivityResult()
API zum Registrieren des Ergebnis-Callbacks registerForActivityResult()
nimmt eine
ActivityResultContract
und
ActivityResultCallback
und gibt eine
ActivityResultLauncher
mit der Sie die andere Aktivität starten.
Ein ActivityResultContract
definiert den Eingabetyp, der zum Erzeugen eines Ergebnisses erforderlich ist.
mit dem Ausgabetyp des Ergebnisses. Die APIs bieten
Standardverträge
für grundlegende Intent-Aktionen wie das Aufnehmen von Fotos, das Anfordern von Berechtigungen usw.
aktiviert. Sie können auch
Sie können einen kundenspezifischen Vertrag erstellen.
ActivityResultCallback
ist eine einzelne Methodenschnittstelle mit einem
onActivityResult()
, die ein Objekt des Ausgabetyps verwendet, der im
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 } });
Wenn Sie mehrere Aktivitätsergebnisaufrufe erhalten und entweder verschiedene
Verträge
oder separate Callbacks wünschen, kannst du registerForActivityResult()
mehrere
um mehrere ActivityResultLauncher
-Instanzen zu registrieren. Du musst
rufen Sie registerForActivityResult()
für jede Erstellung Ihres
Fragment oder Aktivität, sodass die Inflight-Ergebnisse an die
Callback korrekt ist.
registerForActivityResult()
kann sicher vor dem Fragment oder der Aktivität aufgerufen werden
erstellt, sodass er direkt beim Deklarieren von Membervariablen verwendet werden kann.
für die zurückgegebenen ActivityResultLauncher
-Instanzen.
Aktivität starten, um Ergebnis zu erhalten
registerForActivityResult()
registriert zwar deinen Callback, aber nicht
die andere Aktivität starten
und die Anfrage für ein Ergebnis senden. Stattdessen
liegt in der Verantwortung der zurückgegebenen ActivityResultLauncher
-Instanz.
Wenn eine Eingabe vorhanden ist, übernimmt der Launcher die Eingabe, die dem Typ des
ActivityResultContract
Anrufen
launch()
das Generieren des Ergebnisses beginnt. Wenn die Nutzenden mit der
nachfolgende Aktivität und gibt die onActivityResult()
aus dem
ActivityResultCallback
wird dann ausgeführt, wie im folgenden Beispiel gezeigt:
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/*"); } }); }
Eine überlastete Version von
launch()
können Sie eine
ActivityOptionsCompat
zusätzlich zur Eingabe.
Aktivitätsergebnis in einem separaten Kurs erhalten
Die Klassen ComponentActivity
und Fragment
implementieren dagegen
ActivityResultCaller
zur Verwendung der registerForActivityResult()
-APIs verwenden,
das Aktivitätsergebnis in einer separaten Klasse erhalten, die nicht
ActivityResultCaller
mit
ActivityResultRegistry
.
Beispielsweise können Sie eine
LifecycleObserver
die die Registrierung eines Vertrags und das Starten des Launchers übernimmt:
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(); } }); } }
Wenn du die ActivityResultRegistry
-APIs verwendest, empfiehlt Google dringend die Verwendung von
die APIs, die eine LifecycleOwner
verwenden, da die LifecycleOwner
automatisch
entfernt Ihren registrierten Launcher, wenn Lifecycle
gelöscht wird. Sie können jedoch
in denen ein LifecycleOwner
nicht verfügbar ist,
Mit der Klasse ActivityResultLauncher
können Sie
unregister()
als Alternative.
Test
Standardmäßig verwendet registerForActivityResult()
automatisch den Parameter
ActivityResultRegistry
die durch die Aktivität bereitgestellt werden. Außerdem entsteht eine Überlastung,
die es Ihnen ermöglicht,
in Ihrer eigenen Instanz von ActivityResultRegistry
, mit der Sie Ihre
Aktivitätsergebnisaufrufe, ohne
tatsächlich eine weitere Aktivität zu starten.
Beim Testen der App-Fragmente
einen Test-ActivityResultRegistry
mit einem
Noch FragmentFactory
in ActivityResultRegistry
an den Konstruktor des Fragments.
Beispiel: Ein Fragment, das den TakePicturePreview
-Vertrag verwendet, um einen
Miniaturansicht
des Bildes könnte etwa so geschrieben sein:
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; } // ... }
Beim Erstellen einer testspezifischen ActivityResultRegistry
müssen Sie
die
onLaunch()
. Anstatt startActivityForResult()
aufzurufen,
kann die Implementierung
dispatchResult()
und erhalten genau die Ergebnisse, die Sie für Ihren Test verwenden möchten:
val testRegistry = object : ActivityResultRegistry() {
override fun <I, O> onLaunch(
requestCode: Int,
contract: ActivityResultContract<I, O>,
input: I,
options: ActivityOptionsCompat?
) {
dispatchResult(requestCode, expectedResult)
}
}
Der vollständige Test liefert das erwartete Ergebnis, erstellt einen Test
ActivityResultRegistry
, übergibt sie an das Fragment und löst den Launcher aus
entweder direkt oder über andere Test-APIs wie Espresso. Anschließend
die Ergebnisse:
@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)
}
}
}
Benutzerdefinierten Vertrag erstellen
Während ActivityResultContracts
mehrere vorkonfigurierte ActivityResultContract
-Klassen enthält, können Sie
Stellen Sie Ihre eigenen Verträge bereit, die genau die typsichere API bereitstellen, die Sie benötigen.
Für jede ActivityResultContract
sind definierte Eingabe- und Ausgabeklassen erforderlich.
Verwenden Sie Void
als Eingabetyp, wenn Sie
keine Eingabe erforderlich (in Kotlin können Sie entweder Void?
oder Unit
verwenden).
Jeder Vertrag muss die
createIntent()
, die mithilfe einer Context
und der Eingabe das Intent
-Element erstellt, das
wird verwendet
mit startActivityForResult()
.
In jedem Vertrag müssen auch
parseResult()
,
das die Ausgabe aus der angegebenen resultCode
erzeugt, z. B.
Activity.RESULT_OK
oder Activity.RESULT_CANCELED
und den Intent
.
Verträge können optional
getSynchronousResult()
ob es möglich ist, das Ergebnis für eine bestimmte Eingabe zu ermitteln,
createIntent()
anrufen, die andere Aktivität starten und
parseResult()
, um das Ergebnis zu erstellen.
Das folgende Beispiel zeigt, wie ein ActivityResultContract
erstellt wird:
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); } }
Falls Sie keinen kundenspezifischen Vertrag benötigen, können Sie das
StartActivityForResult
Vertrag. Dies ist ein allgemeiner Vertrag, der jede Intent
als Eingabe verwendet
gibt Folgendes zurück:
ActivityResult
,
Sie können resultCode
und Intent
als Teil Ihres Callbacks extrahieren.
Dies wird im folgenden Beispiel gezeigt:
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)); } }); }