-
[Deprecated] StartActivityForResultAndroid/Deprecated 2021. 8. 2. 17:19
빠른 개인 참고용 (kotlin)
mActivityResultLauncher = registerForActivityResult(StartActivityForResult()) { activityResult-> if(activityResult.resultCode== Activity.RESULT_OK){ val intent:Intent?=activityResult.data if(intent!=null){ //처리 } }else{ } }
정리
ComponentActivity의 registerForActivityResult메서드를 사용해야 한다.
registerForActivitiyResult메서드를 사용하기 위해서 return되는 것, 인자로 들어가는 것에 대해 알아보자.
java.lang.Object ↳ android.content.Context ↳ android.content.ContextWrapper ↳ android.view.ContextThemeWrapper ↳ android.app.Activity ↳ androidx.activity.ComponentActivity ↳ androidx.fragment.app.FragmentActivity ↳ androidx.appcompat.app.AppCompatActivity
Activity -> ComponentActivity ->FragmentActivity -> AppCompatActivity
- ComponentActivity implements ActivityResultCaller (액티비티 리저트 콜러)
ActivityResultCaller 인터페이스
that can call Activity.startActivityForResult(Intent, int) style APIs without having to manage request codes, and converting request/response to an Intent
리퀘스트 코드 없이 액티비티를 start시작시킬 수 있다.
ActivityResultCaller인터페이스를 구현한 ComponentActivity의 registerForActivityResult 메서드를 사용
@NonNull @Override public final <I, O> ActivityResultLauncher<I> registerForActivityResult( @NonNull ActivityResultContract<I, O> contract, @NonNull ActivityResultCallback<O> callback) { return registerForActivityResult(contract, mActivityResultRegistry, callback); }
메서드는 return <I, O>ActivityResultLauncher<I>
abstract class ActivityResultContract
어떠한 유형의 인텐트를 날릴 것인가
이 클래스를 구현해서 제공해주는 클래스가 있다. 밑에서 보자.
interface ActivityResultCallback
onActivityResult 하나의 메서드가 있고, 결과를 받을 때 호출될 콜백 메서드이다.
이 메서드에 우리가 결과를 받고 진행할 코드를 넣는다.
(기존 액티비티 내부의 onActivityResult로 보면된다.)
abstract class ActivityResultLauncher<I>
componentActivity의 registerForActivitiyResult로 리턴되는 인스턴스이다.
이 런쳐를 참조변수에 담아두어 launch메서드를 통해 액티비티를 시작한다.
ActivityResultContract: the contract, specifying conversions to/from Intents (Intent, Output)
ActivityResultCallback: the callback to be called on the main thread when activity result is available
- abstract ActivityResultContract<I, O>
액티비티간에 어떤 계약을 할 것인지( 다른 액티비티를 띄운다는지, 카메라를 실행한다든지)
public abstract class ActivityResultContract<I, O> { /** Create an intent that can be used for {@link Activity#startActivityForResult} */ public abstract @NonNull Intent createIntent(@NonNull Context context, @SuppressLint("UnknownNullness") I input); /** Convert result obtained from {@link Activity#onActivityResult} to O */ @SuppressLint("UnknownNullness") public abstract O parseResult(int resultCode, @Nullable Intent intent);
인텐트 생성, result파싱에 대한 추상 메서드가 정의되어 있다.
이 메서드는 직접사용하지 않고 이렇게 구성되어있다라는 것을 보여주기 위해 그냥 옮겨봤다.
구현된 클래스(프레임워크에서 제공해주는)는 ActivityResultContracts로 s가 붙어있다.
사용할 인텐트 유형 따라 이미 구현되어 있는 static nested 클래스를 사용하면 된다.
ActivitiyResultContracts의 static inner클래스들로 각 인텐트 유형 클래스가 정의되어있다.
다른 액티비티 호출하기 위해서 static inner 클래스 StartActivityForResult 사용
registerActivityForResult의 첫 인자에 new ActivityResultContracts.StartActivityForResult()
- interface ActivityResultCallback<O> (액티비티 리저트 콜백)
A type-safe callback to be called when an activity result is available.
activity result가 이용가능할 때 호출되는 callback메서드
- ActivityResultLauncher<I>
A launcher for a previously-prepared call to start the process of executing an ActivityResultContract
미리 준비된 call을 실행할 놈이다. 이놈의 launch메서드 호출로 액티비티를 띄우기 시작한다.
public abstract class ActivityResultLauncher<I> { /** * Executes an {@link ActivityResultContract}. * * <p>This method throws {@link android.content.ActivityNotFoundException} * if there was no Activity found to run the given Intent. * @param input the input required to execute an {@link ActivityResultContract}. * * @throws android.content.ActivityNotFoundException */ public void launch(@SuppressLint("UnknownNullness") I input) { launch(input, null); } /** * Executes an {@link ActivityResultContract}. * * <p>This method throws {@link android.content.ActivityNotFoundException} * if there was no Activity found to run the given Intent. * * @param input the input required to execute an {@link ActivityResultContract}. * @param options Additional options for how the Activity should be started. * * @throws android.content.ActivityNotFoundException */ public abstract void launch(@SuppressLint("UnknownNullness") I input, @Nullable ActivityOptionsCompat options); /** * Unregisters this launcher, releasing the underlying result callback, and any references * captured within it. * * You should call this if the registry may live longer than the callback registered for this * launcher. */ @MainThread public abstract void unregister(); /** * Get the {@link ActivityResultContract} that was used to create this launcher. * * @return the contract that was used to create this launcher */ @NonNull public abstract ActivityResultContract<I, ?> getContract(); }
Note : 프레그먼트 또는 액티비티가 생성되기 전에 registerForActivityResult()를 호출해도 안전하지만, 프래그먼트 또는 액티비티의 Lifecycle 상태가 CREATED가 되기 전까지는 ActivityResultLauncher를 시작할 수 없다.
(수정) ActivityResultLauncher의 초기화 , launch메서드를 적절한 위치에서 호출하지 않으면 에러가 날수있다.
나와 같은 경우는 버튼 리스너와 얽혀서 문제가 났던것 같은데 현재 수정 시점에는 기억이 나지 않는다.
총 코드
MainActivitiy (Intent 보내는)
public class MainActivity extends AppCompatActivity { Button button; //start시킬 런쳐를 담아둘 변수 ActivityResultLauncher<Intent> activityResultLauncher; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = findViewById(R.id.btn_send_to_2); //Intent생성 Intent intent = new Intent(getApplicationContext(), MainActivity2.class); intent.putExtra("activity1 key", "홍길동의 아이디 패스워드 요청"); //registerForActivityResult를 통해 런처 초기화 activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), //다른 액티비티 호출에 해당하는 계약 new ActivityResultCallback<ActivityResult>() { //액티비티 리저트 콜백을 구현한 익명 객체 @Override public void onActivityResult(ActivityResult result) { //구현 메서드에 처리과정 구현 if (result.getResultCode() == Activity.RESULT_OK) { //result ok // 액티비티 리저트 콜러 인터페이스의 registerForActivityResult메서드는 // request code 없이 진행 -> request code 필요 x Intent data = result.getData(); String recString = data.getStringExtra("activity2 key"); Toast.makeText(getApplicationContext(), recString, Toast.LENGTH_SHORT).show(); } else { //result canceled Toast.makeText(getApplicationContext(), "상대 쪽으로부터 result canceled 응답", Toast.LENGTH_SHORT).show(); } } }); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { activityResultLauncher.launch(intent); } }); } }
MainActivity2( Intent 받는쪽)
public class MainActivity2 extends AppCompatActivity { Button button2; String receivedValue; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); button2 = findViewById(R.id.btn_send_to_1); Intent receievedIntent = getIntent(); //받은 인텐트 Intent resultIntent = new Intent(getApplicationContext(), MainActivity.class); //결과를 보낼 인텐트 if (receievedIntent != null) { //잘 받았으면 Bundle bundle = receievedIntent.getExtras(); receivedValue = bundle.getString("activity1 key", "key에 해당하는 값이없다"); //키에 해당하는 값이 없을 때 사용할 default value Toast.makeText(this, receivedValue, Toast.LENGTH_SHORT).show(); //요청한 내용 확인 button2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (receivedValue.equals("key에 해당하는 값이없다")) { resultIntent.putExtra("activity2 key", "누구의 정보를 넘겨줘?"); //key에 해당하는 value 없어서 처리 불가 } else { resultIntent.putExtra("activity2 key", "아이디 패스워드는....이야"); //처리 완료 } setResult(Activity.RESULT_OK, resultIntent); finish(); } }); } else { //인텐트를 못 받았을 경우 setResult(Activity.RESULT_CANCELED, resultIntent); //canceled를 넘겨주고 종료 finish(); } } }
https://developer.android.com/training/basics/intents/result