Android gets results from other activities: registerForActivityResult()

Posted by thebighere on Thu, 04 Nov 2021 23:51:33 +0100

Android gets results from other activities: registerForActivityResult()

brief introduction

  • Activity Result APIs can replace the startActivityForResult method to start an activity to obtain results
  • Activity Result APIs can replace the requestPermissions method to request runtime permissions

background

Starting an activity (whether in this application or in other applications) is not necessarily a one-way operation. You can also start another activity and receive the returned results.
Common scenarios are calling the system camera, calling the photo album to obtain photos, calling the address book, obtaining some special permissions, etc. the traditional way is to carry data through Intent, then use the startActivityForResult method to start the next Activity, and then receive the returned data through onActivityResult.
The problem with the traditional approach is:

  • When you start an activity to get results, it is possible that the process and activity are destroyed due to insufficient memory
  • onActivityResult callback method is seriously nested and coupled, and it is difficult to maintain due to logical confusion

Register a result callback for the results obtained by the start Activity

Based on the above problems, the Activity Result APIs will start the result callback of the Activity, which is separated from the logic of starting the Activity.

When in a ComponentActivity or Fragment, the Activity Result API provides a registerForActivityResult() API to register the result callback.
registerForActivityResult() accepts ActivityResultContract and ActivityResultCallback as parameters and returns ActivityResultLauncher to start another activity, where:

  • ActivityResultContract defines the input type required to generate the result and the output type of the result. These API s can provide default contracts for basic intent operations such as taking photos and requesting permission, and can also create custom contracts.
  • ActivityResultCallback is a single method interface with onActivityResult() method, which can accept objects of output type defined in ActivityResultContract:
ActivityResultLauncher<String> mGetContent = registerForActivityResult(new GetContent(),
    new ActivityResultCallback<Uri>() {
        @Override
        public void onActivityResult(Uri uri) {
            // Handle the returned Uri
        }
});

Start Activity to get results

registerForActivityResult() only registers a result callback for the result obtained by the activity, but it does not start another activity and issue a result request itself. The operation of starting an activity is the responsibility of the instance object of the ActivityResultLauncher returned by registerForActivityResult().

If there are input parameters, the instance object of ActivityResultLauncher will match the type of ActivityResultContract according to the input parameters. Calling the launch() method of the instance object of ActivityResultLauncher will start the activity and get the results. When the user completes the subsequent activities and returns, the system will execute the onActivityResult() method in the ActivityResultCallback.

ActivityResultLauncher<String> mGetContent = registerForActivityResult(new GetContent(),
    new ActivityResultCallback<Uri>() {
        @Override
        public void onActivityResult(Uri uri) {
            // Handle the returned Uri
        }
});

@Override
public void onCreate(@Nullable savedInstanceState: Bundle) {
    // ...

    Button selectButton = findViewById(R.id.select_button);

    selectButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View view) {
            // Pass in the mime type you'd like to allow the user to select
            // as the input
            mGetContent.launch("image/*");
        }
    });
}

Note: since the process and activity may be destroyed between the two time points when the launch() is called and the onActivityResult() callback is triggered, any other state required to process the results must be saved and restored separately from these API s.

Predefined Contract

A series of predefined contracts are provided in the Activity Result APIs for developers to start activities to obtain results and request runtime permissions:

  • StartActivityForResult: a general Contract without any conversion, with Intent as input and ActivityResult as output. This is also the most commonly used Contract.
  • RequestMultiplePermissions: used to request a set of permissions
  • RequestPermission: used to request a single permission
  • TakePicturePreview: call MediaStore.ACTION_IMAGE_CAPTURE takes pictures, and the return value is Bitmap pictures
  • TakePicture: call MediaStore.ACTION_IMAGE_CAPTURE takes pictures and saves the pictures to the given Uri address. Returning true indicates that the saving is successful.
  • TakeVideo: call MediaStore.ACTION_VIDEO_CAPTURE captures a video, saves it to a given Uri address, and returns a thumbnail.
  • PickContact: get contacts from the address book APP
  • GetContent: prompt to select a content and return a Uri address (in the form of content: / /) that accesses the native data through ContentResolver#openInputStream(Uri). By default, it adds intent#category_ Open, returns the content that can represent the stream.
  • CreateDocument: prompts the user to select a document and returns a Uri starting with (file:/http:/content:).
  • OpenMultipleDocuments: prompt the user to select documents (multiple documents can be selected), and return their URIs respectively in the form of List.
  • OpenDocumentTree: prompts the user to select a directory and returns the selected as a Uri. The application can fully manage the documents in the returned directory.

Classic examples of development using the above predefined contracts:

StartActivityForResult

    ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
        @Override
        public void onActivityResult(ActivityResult result) {
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
        activityResultLauncher.launch(intent);
    }

RequestMultiplePermissions

    ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
        @Override
        public void onActivityResult(Map<String, Boolean> result) {
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE};
        activityResultLauncher.launch(permissions);
    }

Comparison between old and new: onactivityresult & activity result APIs

Old:

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    startActivityForResult(intent, 123);
}

@Override
protected void onActivityResult (int requestCode, int resultCode, Intent data) {
    if (resultCode == Activity.RESULT_OK && requestCode == 123) {
        doSomeOperations();
    }
}

New:

// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() {
            @Override
            public void onActivityResult(ActivityResult result) {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    // There are no request codes
                    Intent data = result.getData();
                    doSomeOperations();
                }
            }
        });

public void openSomeActivityForResult() {
    Intent intent = new Intent(this, SomeActivity.class);
    someActivityResultLauncher.launch(intent);
}

reference resources

https://segmentfault.com/a/11...
https://developer.android.com...

Topics: Android