Ergebnis aus einer Aktivität abrufen

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