How to manage startActivityForResult on Android?

В своей деятельности я называю второе действие из основного действия startActivityForResult, В моем втором задании есть несколько методов, которые завершают это задание (возможно, без результата), однако только один из них возвращает результат.

Например, из основного занятия я называю второе. В этом упражнении я проверяю некоторые функции телефона, такие как наличие камеры. Если этого не произойдет, я закрою это занятие. Кроме того, во время подготовки MediaRecorder или же MediaPlayer если возникнет проблема, я закрою это действие.

Если на его устройстве есть камера и запись завершена полностью, то после записи видео, если пользователь нажмет кнопку "Готово", я отправлю результат (адрес записанного видео) обратно в основное действие.

Как я могу проверить результат основной деятельности?

15 ответов

Решение

От твоего FirstActivity позвонить SecondActivity с помощью startActivityForResult() метод

Например:

Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, 1);

В вашем SecondActivity установить данные, которые вы хотите вернуть обратно FirstActivity, Если вы не хотите возвращаться назад, не устанавливайте ничего.

Например: в secondActivity, если вы хотите отправить обратно данные:

Intent returnIntent = new Intent();
returnIntent.putExtra("result",result);
setResult(Activity.RESULT_OK,returnIntent);
finish();

Если вы не хотите возвращать данные:

Intent returnIntent = new Intent();
setResult(Activity.RESULT_CANCELED, returnIntent);
finish();

Теперь в вашем классе FirstActivity напишите следующий код для onActivityResult() метод.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

    if (requestCode == 1) {
        if(resultCode == Activity.RESULT_OK){
            String result=data.getStringExtra("result");
        }
        if (resultCode == Activity.RESULT_CANCELED) {
            //Write your code if there's no result
        }
    }
}//onActivityResult

Как проверить результат от основной деятельности?

Вы должны переопределить Activity.onActivityResult() затем проверьте его параметры:

  • requestCode определяет, какое приложение вернуло эти результаты. Это определяется вами, когда вы звоните startActivityForResult(),
  • resultCode информирует вас о том, успешно ли выполнено это приложение или нет
  • data содержит любую информацию, возвращенную этим приложением. Это может быть null,

пример

Чтобы увидеть весь процесс в контексте, вот дополнительный ответ. Смотрите мой более полный ответ для более подробного объяснения.

введите описание изображения здесь

MainActivity.java

public class MainActivity extends AppCompatActivity {

    // Add a different request code for every activity you are starting from here
    private static final int SECOND_ACTIVITY_REQUEST_CODE = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // "Go to Second Activity" button click
    public void onButtonClick(View view) {

        // Start the SecondActivity
        Intent intent = new Intent(this, SecondActivity.class);
        startActivityForResult(intent, SECOND_ACTIVITY_REQUEST_CODE);
    }

    // This method is called when the second activity finishes
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // check that it is the SecondActivity with an OK result
        if (requestCode == SECOND_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) { // Activity.RESULT_OK

                // get String data from Intent
                String returnString = data.getStringExtra("keyName");

                // set text view with string
                TextView textView = (TextView) findViewById(R.id.textView);
                textView.setText(returnString);
            }
        }
    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
    }

    // "Send text back" button click
    public void onButtonClick(View view) {

        // get the text from the EditText
        EditText editText = (EditText) findViewById(R.id.editText);
        String stringToPassBack = editText.getText().toString();

        // put the String to pass back into an Intent and close this activity
        Intent intent = new Intent();
        intent.putExtra("keyName", stringToPassBack);
        setResult(RESULT_OK, intent);
        finish();
    }
}

В дополнение к ответу от @Nishant, лучший способ вернуть результат активности:

Intent returnIntent = getIntent();
returnIntent.putExtra("result",result);
setResult(RESULT_OK,returnIntent);
finish();

У меня были проблемы с

new Intent();

Тогда я узнал, что правильный путь использует

getIntent();

чтобы получить текущее намерение

startActivityForResult: устарело в androidx

Для нового способа у нас есть `registerForActivityResult`

в Java:
       // You need to create lanucher variable inside onAttach or onCreate or global, i.e, before the activity is displayed
 ActivityResultLauncher<Intent> launchSomeActivity = registerForActivityResult(
     new ActivityResultContracts.StartActivityForResult(),
     new ActivityResultCallback<ActivityResult>() {
              @Override
              public void onActivityResult(ActivityResult result) {
                   if (result.getResultCode() == Activity.RESULT_OK) {
                         Intent data = result.getData();
                         // your operation....
                    }
               }
      });
    
      public void openYourActivity() {
            Intent intent = new Intent(this, SomeActivity.class);
            launchSomeActivity.launch(intent);
      }

В Котлине:

      var resultLauncher = registerForActivityResult(StartActivityForResult()) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val data: Intent? = result.data
        // your operation...
    }
}

fun openYourActivity() {
    val intent = Intent(this, SomeActivity::class.java)
    resultLauncher.launch(intent)
}

Новый способ - уменьшить сложность, с которой мы столкнулись, когда вызываем активность из фрагмента или из другой активности.

ActivityResultRegistry - рекомендуемый подход

ComponentActivity теперь предоставляет ActivityResultRegistry что позволяет вам справиться с startActivityForResult()+onActivityResult() так же как requestPermissions()+onRequestPermissionsResult() потоков без переопределения методов в вашем Activity или Fragment, обеспечивает повышенную безопасность типов за счет ActivityResultContract, и предоставляет хуки для тестирования этих потоков.

Настоятельно рекомендуется использовать API результатов деятельности, представленные в AndroidX Activity 1.2.0-alpha02 и Fragment 1.3.0-alpha02.

Добавьте это в свой build.gradle

def activity_version = "1.2.0-alpha03"

// Java language implementation
implementation "androidx.activity:activity:$activity_version"
// Kotlin
implementation "androidx.activity:activity-ktx:$activity_version"

Как использовать готовый контракт?

Этот новый API имеет следующие предварительно встроенные функции

  1. TakeVideo
  2. PickContact
  3. GetContent
  4. GetContents
  5. OpenDocument
  6. OpenDocuments
  7. OpenDocumentTree
  8. CreateDocument
  9. Набирать номер
  10. Сделать фотографию
  11. Просить разрешения
  12. RequestPermissions

Пример использования контракта takePicture:

private val takePicture = prepareCall(ActivityResultContracts.TakePicture()) { bitmap: Bitmap? ->
    // Do something with the Bitmap, if present
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    button.setOnClickListener { takePicture() }
}

Так что здесь происходит? Давайте немного разберемся.takePicture - это просто обратный вызов, который возвращает растровое изображение, допускающее значение NULL - значение NULL зависит от того, является ли onActivityResult процесс прошел успешно. prepareCall затем регистрирует этот вызов в новой функции на ComponentActivity называется ActivityResultRegistry - мы вернемся к этому позже. ActivityResultContracts.TakePicture() является одним из встроенных помощников, которые Google создал для нас, и, наконец, вызывает takePicture фактически запускает намерение так же, как раньше с Activity.startActivityForResult(intent, REQUEST_CODE).

Как написать индивидуальный договор?

Простой контракт, который принимает Int в качестве ввода и возвращает строку, запрашиваемую Activity, возвращается в результате Intent.

class MyContract : ActivityResultContract<Int, String>() {

    companion object {
        const val ACTION = "com.myapp.action.MY_ACTION"
        const val INPUT_INT = "input_int"
        const val OUTPUT_STRING = "output_string"
    }

    override fun createIntent(input: Int): Intent {
        return Intent(ACTION)
            .apply { putExtra(INPUT_INT, input) }
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        return when (resultCode) {
            Activity.RESULT_OK -> intent?.getStringExtra(OUTPUT_STRING)
            else -> null
        }
    }
}

class MyActivity : AppCompatActivity() {

    private val myActionCall = prepareCall(MyContract()) { result ->
        Log.i("MyActivity", "Obtained result: $result")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        button.setOnClickListener {
            myActionCall(500)
        }
    }
}

Проверьте эту официальную документацию для получения дополнительной информации.

Для тех, у кого проблема с неправильным requestCode в onActivityResult

Если вы звоните startActivityForResult() от твоего Fragmentкод запроса изменяется действием, которому принадлежит фрагмент.

Если вы хотите получить правильный код результата в вашей деятельности, попробуйте это:

Изменить:

startActivityForResult(intent, 1); Для того, чтобы:

getActivity().startActivityForResult(intent, 1);

Если вы хотите обновить пользовательский интерфейс с результатом активности, вы не можете использовать this.runOnUiThread(new Runnable() {}При этом пользовательский интерфейс не будет обновляться с новым значением. Вместо этого вы можете сделать это:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_CANCELED) {
        return;
    }

    global_lat = data.getDoubleExtra("LATITUDE", 0);
    global_lng = data.getDoubleExtra("LONGITUDE", 0);
    new_latlng = true;
}

@Override
protected void onResume() {
    super.onResume();

    if(new_latlng)
    {
        PhysicalTagProperties.this.setLocation(global_lat, global_lng);
        new_latlng=false;
    }
}

Это кажется глупым, но работает довольно хорошо.

В Котлине

Предположим, что A и B являются действиями, а навигация происходит от A -> B. Нам нужен результат от A <- B.

в А

          // calling the Activity B
    resultLauncher.launch(Intent(requireContext(), B::class.java))

    // we get data in here from B
    private var resultLauncher =
    registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        when (result.resultCode) {
            Activity.RESULT_OK -> {
                result.data?.getStringExtra("VALUE")?.let {
                    // data received here
                }
            }
            Activity.RESULT_CANCELED -> {
                // cancel or failure
            }
        }
    }

В Б

          // Sending result value back to A
    if (success) {
       setResult(RESULT_OK, Intent().putExtra("VALUE", value))
    } else {
       setResult(RESULT_CANCELED)
    }

Я опубликую новый "способ" с androidxвкратце (потому что в некоторых случаях заказной реестр или договор не требуется). Если вам нужна дополнительная информация, см. Https://developer.android.com/training/basics/intents/result

Важно : на самом деле есть ошибка с обратной совместимостью androidx, поэтому вам нужно добавить fragment_versionв вашем файле gradle. В противном случае вы получите исключение «Ошибка API нового результата: для requestCode можно использовать только младшие 16 бит».

      dependencies {

    def activity_version = "1.2.0-beta01"
    // Java language implementation
    implementation "androidx.activity:activity:$activity_version"
    // Kotlin
    implementation "androidx.activity:activity-ktx:$activity_version"

    def fragment_version = "1.3.0-beta02"
    // Java language implementation
    implementation "androidx.fragment:fragment:$fragment_version"
    // Kotlin
    implementation "androidx.fragment:fragment-ktx:$fragment_version"
    // Testing Fragments in Isolation
    debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
}

Теперь вам просто нужно добавить эту переменную-член вашей активности. При этом используется предопределенный реестр и общий контракт.

      public class MyActivity extends AppCompatActivity{

   ...

    /**
     * Activity callback API.
     */
    // https://developer.android.com/training/basics/intents/result
    private ActivityResultLauncher<Intent> mStartForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),

            new ActivityResultCallback<ActivityResult>() {

                @Override
                public void onActivityResult(ActivityResult result) {
                    switch (result.getResultCode()) {
                        case Activity.RESULT_OK:
                            Intent intent = result.getData();
                            // Handle the Intent
                            Toast.makeText(MyActivity.this, "Activity returned ok", Toast.LENGTH_SHORT).show();
                            break;
                        case Activity.RESULT_CANCELED:
                            Toast.makeText(MyActivity.this, "Activity canceled", Toast.LENGTH_SHORT).show();
                            break;
                    }
                }
            });

До нового API у вас было:

      btn.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MyActivity .this, EditActivity.class);
                startActivityForResult(intent, Constants.INTENT_EDIT_REQUEST_CODE);
            }
        });

Вы можете заметить, что код запроса теперь генерируется (и сохраняется) структурой Google.Ваш код станет.

       btn.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(MyActivity .this, EditActivity.class);
                    mStartForResult.launch(intent);
                }
            });

Надеюсь, мой ответ поможет некоторым людям!

Очень распространенная проблема в Android
Это может быть разбито на 3 части
1) начать упражнение B (происходит в упражнении A)
2) Установите запрошенные данные (происходит в действии B)
3) Получить запрошенные данные (происходит в действии А)

1) начало деятельности B

Intent i = new Intent(A.this, B.class);
startActivity(i);

2) Установите запрашиваемые данные

В этой части вы решаете, хотите ли вы отправлять данные обратно или нет, когда происходит определенное событие.
Например: в действии B есть EditText и две кнопки b1, b2.
Нажатие на кнопку b1 отправляет данные обратно в операцию A
Нажатие на кнопку b2 не отправляет никаких данных.

Отправка данных

b1......clickListener
{
   Intent resultIntent = new Intent();
   resultIntent.putExtra("Your_key","Your_value");
   setResult(RES_CODE_A,resultIntent);
   finish();
}

Не отправка данных

b2......clickListener
    {
       setResult(RES_CODE_B,new Intent());
       finish();
    }

пользователь нажимает кнопку назад
По умолчанию результат устанавливается с кодом ответа Activity.RESULT_CANCEL

3) Получить результат

Для этого переопределить метод onActivityResult

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RES_CODE_A) {

     // b1 was clicked 
   String x = data.getStringExtra("RES_CODE_A");

}
else if(resultCode == RES_CODE_B){

   // b2 was clicked

}
else{
   // back button clicked 
}
}

Сначала вы используете startActivityForResult() с параметрами в первую очередь Activity и если вы хотите отправить данные со второго Activity к первому Activity затем передать значение с помощью Intent с setResult() метод и получить эти данные внутри onActivityResult() метод в первую очередь Activity,

You need to override Activity.onActivityResult()

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_CODE_ONE) {


   String a = data.getStringExtra("RESULT_CODE_ONE");

}
else if(resultCode == RESULT_CODE_TWO){

   // b was clicked

}
else{

}
}

В вашей основной деятельности

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    findViewById(R.id.takeCam).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent=new Intent(getApplicationContext(),TakePhotoActivity.class);
            intent.putExtra("Mode","Take");
            startActivity(intent);
        }
    });
    findViewById(R.id.selectGal).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent=new Intent(getApplicationContext(),TakePhotoActivity.class);
            intent.putExtra("Mode","Gallery");
            startActivity(intent);
        }
    });
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

}

Во втором действии для отображения

private static final int CAMERA_REQUEST = 1888;
private ImageView imageView;
private static final int MY_CAMERA_PERMISSION_CODE = 100;
private static final int PICK_PHOTO_FOR_AVATAR = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_take_photo);

    imageView=findViewById(R.id.imageView);

    if(getIntent().getStringExtra("Mode").equals("Gallery"))
    {
        pickImage();
    }
    else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(new String[]{Manifest.permission.CAMERA}, MY_CAMERA_PERMISSION_CODE);
            } else {
                Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                startActivityForResult(cameraIntent, CAMERA_REQUEST);
            }
        }
    }
}
public void pickImage() {
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setType("image/*");
    startActivityForResult(intent, PICK_PHOTO_FOR_AVATAR);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == MY_CAMERA_PERMISSION_CODE)
    {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
        {
            Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(cameraIntent, CAMERA_REQUEST);
        }
        else
        {
            Toast.makeText(this, "Camera Permission Denied..", Toast.LENGTH_LONG).show();
        }
    }
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK) {
        Bitmap photo = (Bitmap) data.getExtras().get("data");
        imageView.setImageBitmap(photo);
    }
        if (requestCode == PICK_PHOTO_FOR_AVATAR && resultCode == Activity.RESULT_OK) {
            if (data == null) {
                Log.d("ABC","No Such Image Selected");
                return;
            }
            try {
                Uri selectedData=data.getData();
                Log.d("ABC","Image Pick-Up");
                imageView.setImageURI(selectedData);
                InputStream inputStream = getApplicationContext().getContentResolver().openInputStream(selectedData);
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                Bitmap bmp=MediaStore.Images.Media.getBitmap(getContentResolver(),selectedData);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch(IOException e){

            }
    }
}

Ответ @Nishant правильный. В моем случае onActivityResult никогда не вызывался из-за настроек в AndroidManifest. Убедитесь, что ваше второе действие (или отправитель) не имеет этой строки в манифесте Android.

<activity android:name=".SecondViewActivity"
            android:parentActivityName=".FirstActivity"/>

Если это так, пожалуйста, удалите android:parentActivityName=".FirstActivity"

Другие вопросы по тегам