Android2012. 1. 30. 17:48
기획자가 원하는 대로 액티비티를 띄우고, 이동하고, 점프하는 방법에 대해 알아본다.


[관련글 링크]
1. Activity Jump #1 "홈 액티비티로 이동하기"
2. Activity Jump #2 "액티비티 스택에 없었던 메인액티비티로 이동하기"


[목표]
1번 글에서는 어느 액티비티에서든 홈 액티비티로 이동하는 방법에 대해 정리해봤다.

이번 글에서는 시작 액티비티가 기획자가 요구하는 UI흐름의 중간인 경우. 그래서 시작 액티비티에서 [이전] 키를 눌렀을 때, 안드로이드 홈스크린이 아닌 UI흐름 상의 액티비티를 보여줘야 하는 경우에 대해 알아본다.

그림으로 표현하면 다음과 같다.


안드로이드 홈스크린에서 "위젯" 또는 "실행 아이콘"을 눌렀을 때, 액티비티 B를 보여주는 것. 또한, [이전]의 의미가안드로이드 홈스크린이 아닌, 한 번도 띄운적 없는(액티비티 스택에 존재하지 않는) Activity A인 경우 어떻게 구현해야 할까?


[구현하기]

다음과 같이 액티비티 4개를 준비한다.



우선, AndroidManifest.xml을 수정하여, Activity B를 시작액티비티로 지정해 준다.
이렇게 하면 안드로이드홈에서 아이콘을 클릭하면 제일 먼서 Activity B가 보여진다.
당연히 액티비티 스택에는 Activity B 만 쌓여있는 상태가 된다.


<activity
          android:name=".ActivityJumpDemoActivity"
          android:label="@string/app_name">
</activity>
<activity android:name="ActivityA" 
	android:configChanges="orientation" 
	android:screenOrientation="sensor">
</activity>
<activity android:name="ActivityB" 
	android:configChanges="orientation" 
	android:screenOrientation="sensor">
	<intent-filter>
		<action android:name="android.intent.action.MAIN" />
		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
</activity>
<activity android:name="ActivityC" 
	android:configChanges="orientation" 
	android:screenOrientation="sensor">
</activity>
*맨 아래 링크되어 있는  전체 코드는 이전 글을 기준으로 구현되어 있어서 이 부분이 적용이 되어 있지 않다. 테스트 해보려면 위의 내용을 수정해서 테스트해야 한다.


이제 Activity B에서 [이전]키를 눌렀을 때,
Activity A를 보여주려면 Activity B에 onKeyUp(...)을 구현해준다. 
나머지 액티비티들도 띄워줄 액티비티를 변경/적용해준다.
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
	if (keyCode == KeyEvent.KEYCODE_BACK) {
		Intent intent = new Intent(getBaseContext(), ActivityA.class);
		startActivity(intent);
		finish();
		return true;
	}
	return super.onKeyDown(keyCode, event);
}
Line 3
"KeyEvent.KEYCODE_BACK" [이전]키가 눌렸을 때를 처리한다.
Line 4 : [이전]키가 눌렸을 때 보여줄 ActivityA.class를 지정한다.
Line 6 : 현재 창은 종료시켜 액티비티 스택에서 깔끔히 없앤다.

이렇게 구현된 상태에서 "안드로이드홈->B액티비티->[이전]키->A액티비티"의 흐름은 정상적으로 동작한다.


* 안드로이드홈->B액티비티->[이전]키->A액티비티 후 스택 확인

  Running activities (most recent first):

    TaskRecord{300bdd60 #5 A com.juno.activityjump.demo}

      Run #1: HistoryRecord{3012fe80 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

    TaskRecord{2fe0c3e8 #2 A com.lge.launcher}

      Run #0: HistoryRecord{300d62c0 com.lge.launcher/.Launcher}



그런데, 문제가 있다.
위와 같이 액티비티 마다 onKeyUp(...) 별도로 구현해 놓으면, [이전]키가 눌렸을 때 항상 새롭게 액티비티를 띄우게 된다.
만약 "메인->A->메인"를 반복하면, A액티비티는 finish()로 종료되지만, 스택에 있었던 녀석을 놔두고 새롭게 메인액티비티를 띄우게 된다. 즉, 메인액티비티가 여러 벌이 생기고 메모리에 계속 남게 된다.


* "메인->A->[이전]키->메인"를 두 번 반복했을 때의 스택 확인

  Running activities (most recent first):

    TaskRecord{301628a0 #6 A com.juno.activityjump.demo}

      Run #3: HistoryRecord{30311070 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

      Run #2: HistoryRecord{301e5650 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

      Run #1: HistoryRecord{301c5ea8 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

    TaskRecord{2fe0c3e8 #2 A com.lge.launcher}

      Run #0: HistoryRecord{300d62c0 com.lge.launcher/.Launcher}


이를 해결하는 방법은
"1. Activity Jump #1 "홈 액티비티로 이동하기" 글에서 소개한 인텐트 플래그를 동일하게 사용하면 해결된다.

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
	if (keyCode == KeyEvent.KEYCODE_BACK) {
		Intent intent = new Intent(getBaseContext(), ActivityJumpDemoActivity.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
		intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
		startActivity(intent);
		finish();
		return true;
	}
	return super.onKeyDown(keyCode, event);
}

Line 5 : FLAG_ACTIVITY_CLEAR_TOP 플래그. 혹시 모를 중간의 액티비티를 스택에서 없앤다.
Line 6 : FLAG_ACTIVITY_SINGLE_TOP 플래그. 최상위 스택에 보여줄 액티비티가 존재하는 경우, 이를 재사용한다.

위와 같이 적용한 후에는 액티비티 사이를 이리저리 왔다 갔다 한 후, 메인액티비티로 돌아온다고 해도 스택은 아래와 같이 깔끔해진다.

  Running activities (most recent first):

    TaskRecord{3027dff8 #7 A com.juno.activityjump.demo}

      Run #1: HistoryRecord{301e89e8 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

    TaskRecord{2fe0c3e8 #2 A com.lge.launcher}

      Run #0: HistoryRecord{300d62c0 com.lge.launcher/.Launcher}



[다운로드]
    전체소스


Posted by 데브로망스
Android2012. 1. 30. 10:12
기획자가 원하는 대로 액티비티를 띄우고, 이동하고, 점프하는 방법에 대해 알아본다.


[관련글 링크] 
1. Activity Jump #1 "홈 액티비티로 이동하기"
2. Activity Jump #2 "액티비티 스택에 없었던 메인액티비티로 이동하기"


[목표]
아래와 같이 메인 액티비티에서 액티비티C까지 4개의 화면이 존재하는 앱을 구현하는데, 갑자기 기획자가 "어떤 페이지에서도 [HOME] 버튼을 누르면 메인 액티비티로 이동이 가능하도록 구현해주세요."라고 한다면.. 

"그냥 띄우면 되겠지~"한다면, 안드로이드 액비티비의 스택구조를 알아볼 필요가 있고, 
"뭔가 좀 꼬일 것 같은데~"한다면 액티비티를 띄울 때, 인텐트에 적용할 플래그(Intent.addFlags(...))에 대해 알아보면 된다.

위의 내용을 정리해서, 구현해본다.



[구현하기]
아래와 같이 4개의 액티비티를 준비한다.
[NEXT PAGE >] 버튼을 누르면 왼쪽에서 오른쪽 순서로 이동하도록 구현한다.
어느 페이지에서든 [Go to MAIN] 버튼을 누르면 무조건 맨 왼쪽의 메인액티비티로 이동한다.



아래는 "B 액티비티"의 구현한 예이다. (다른 페이지도 똑같이 구현한다.)
눈여겨 볼 부분은 Line 19~22 이다.
public class ActivityB extends Activity implements OnClickListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_b);
        
        Button btnGoToMain = (Button)findViewById(R.id.btnActB);
        btnGoToMain.setOnClickListener(this);
        Button btnNextPage = (Button)findViewById(R.id.btnActB_Next_Page);
        btnNextPage.setOnClickListener(this);
    }

	@Override
	public void onClick(View v) {
		int id = v.getId();
		
		switch (id) {
		case R.id.btnActB:
			Intent intentHome = new Intent(this, ActivityJumpDemoActivity.class);
			intentHome.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
			intentHome.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
			startActivity(intentHome);
			finish();
			break;
		case R.id.btnActB_Next_Page:
			Intent intent = new Intent(this, ActivityC.class);
			startActivity(intent);
		default:
			break;
		}
	}
}

Line 20 : "FLAG_ACTIVITY_CLEAR_TOP" 플래그. 간단히 현재 액티비티에서 어느 액티비티로 이동하는데, 스택 중간에 있었던 액티비티들을 지우는 역할은 한다고 이해하면 된다. 이 플래그가 없으면, 중간에 액티비티는 스택에 그대로 남아있기 때문에 이동 중간에  화면에 표출되어 UI 흐름을 망친다. 또한 시간이 지나면서 수 많은 액티비티가 쌓이게 되어 메모리 낭비를 초래한다.

FLAG_ACTIVITY_CLEAR_TOP 
If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.


Line 21 : "FLAG_ACTIVITY_SINGLE_TOP" 플래그. 띄우려는 액티비티가 스택 맨위에 이미 실행 중이라면 재사용하겠다는 의미로 해석하면 된다.

FLAG_ACTIVITY_SINGLE_TOP
If set, the activity will not be launched if it is already running at the top of the history stack. 


 즉, 이 플래그를 설정하지 않았을 때, 메인액티비티는 새롭게 생성된다.
onCreate(...)와 onResume(...) 함수에 로그를 찍어 테스트 한 결과는 아래와 같다.


"A액티비티->메인" 으로 이동할때, 

1. FLAG_ACTIVITY_SINGLE_TOP 플래그 ON



2. FLAG_ACTIVITY_SINGLE_TOP 플래그 OFF 


결국,  FLAG_ACTIVITY_SINGLE_TOP를 설정하면, 기존에 띄워져있던 액티비티를 재사용하기 때문에 onCreate(...)가 불리지 않는다.


[액티비티 스택으로 확인하기]
(스택을 확인하는 방법은 이전 글을 참고하세요.)

public void onClick(View v) {
	int id = v.getId();
	
	switch (id) {
	case R.id.btnActC:
		Intent intentHome = new Intent(this, ActivityJumpDemoActivity.class);
		intentHome.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
		intentHome.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
		startActivity(intentHome);
		finish();
		break;
	default:
		break;
	}
}


"매인액티비티 -> ... -> 액티비티 C"까지 흘러온 상태에서 [Go to MAIN] 버튼을 눌렀을 때의 스택을 확인해봤다.
위에 코드의 Line 7,8을 ON했을 때와 OFF했을 때를 비교한 결과는 아래와 같다.



MAIN->A->B->C까지 정상적으로 이동했을 때

  Running activities (most recent first):

    TaskRecord{3026a910 #82 A com.juno.activityjump.demo}

      Run #4: HistoryRecord{30186310 com.juno.activityjump.demo/.ActivityC}

      Run #3: HistoryRecord{3021e1e0 com.juno.activityjump.demo/.ActivityB}

      Run #2: HistoryRecord{303b3e08 com.juno.activityjump.demo/.ActivityA}

      Run #1: HistoryRecord{300251c8 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

    TaskRecord{30064818 #2 A com.lge.launcher}

      Run #0: HistoryRecord{2fcff6e0 com.lge.launcher/.Launcher}



C 에서 -> HOME으로 이동했을 때 (2개의 플래그 ON)

  Running activities (most recent first):

    TaskRecord{3026a910 #82 A com.juno.activityjump.demo}

      Run #1: HistoryRecord{300251c8 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

    TaskRecord{30064818 #2 A com.lge.launcher}

      Run #0: HistoryRecord{2fcff6e0 com.lge.launcher/.Launcher}



C 에서 -> HOME으로 이동했을 때 (2개의 플래그 OFF)

  Running activities (most recent first):

    TaskRecord{301996d8 #83 A com.juno.activityjump.demo}

      Run #4: HistoryRecord{3035d6c0 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

      Run #3: HistoryRecord{302c4f30 com.juno.activityjump.demo/.ActivityB}

      Run #2: HistoryRecord{3012aa08 com.juno.activityjump.demo/.ActivityA}

      Run #1: HistoryRecord{2ffb9170 com.juno.activityjump.demo/.ActivityJumpDemoActivity}

    TaskRecord{30064818 #2 A com.lge.launcher}

      Run #0: HistoryRecord{2fcff6e0 com.lge.launcher/.Launcher}




[참고자료]
    http://developer.android.com/reference/android/content/Intent.html


[다운로드]
    전체소스





Posted by 데브로망스