Android2014. 12. 24. 16:20

최근에는 구식인 UI흐름이지만,

로그아웃을 했을 때, 로그인 화면을 띄워야 할 때가 있다.

이때, 스택에 쌓여있는 기존 Activity들을 모두 제거할 수 있다면 흐름에 방해되는 골치아픈 문제들을 한 방에 해결할 수 있다.


Intent intent = new Intent(context, classToShow);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
	intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
}
else {
	intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
startActivity(intent);

간단히 플래그에 대해 설명하면, 

FLAG_ACTIVITY_CLEAR_TASK : 현재 TASK를 비운다.

FLAG_ACTIVITY_NEW_TASK : 새로운 TASK를 생성한다. (Activity가 아닌 곳에서 Activity를 띄울 때, 종종 사용하는 플래그)



한 가지 문제는 첫 번째 플래그, FLAG_ACTIVITY_CLEAR_TASK 는 API Level 11(허니콤)부터 사용이 가능해서 진저브레드를 지원하는 앱에서는 사용이 불가능하다. (당연하지만 플래그를 적용한다해도 원하는데로 동작하지 않는다.)

만약, 진저브레드를 지원해야한다면 버전코드를 확인해서 동작을 달리해야한다.

진저브레드는 FLAG_ACTIVITY_CLEAR_TOP를 사용해서 처리했는데,

애초에 진저브레드의 경우에는 로그인화면에서 로그인이 완료되었다고 해도 Activity를 종료하지 않았다.

반면에, 허니콤 이상의 경우 쿨하게 finish()를 호출했다. 아래 코드 처럼.


if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
	finish();
}


즉, 진저브레드는 FLAG_ACTIVITY_CLEAR_TOP을 이용해서, 스택 맨 하단에 있는 로그인 Activity를 남기고 중간의 Activity는 종료시키는 방법을 적용. 상위 OS 버전과 동작을 맞췄다.


이 경우, 또 다른 문제가 있는데, 진저브레드에서는 앱을 종료시킬 때 단순히 메인 Activity를 finish()를 호출하면 밑에 있었던 로그인 Activity가 보이게된다. 결국에는 (권장하는 방법은 아니라고 하지만...) killProcess를 사용하면 된다.


if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD_MR1) {
	finish();
}
else {
	android.os.Process.killProcess(android.os.Process.myPid());
}



지금까지 찾은 가장 깔끔한 방법인데, 더 우아한  방법이 있다면 조언바랍니다.

사실은 최근 트랜드에 맞는 UI흐름을 구성하면 좋겠지만

살다보면 참 마음대로 안 될때가 많네요. ^0^




Posted by 데브로망스
Android2014. 12. 18. 20:44

남들이 만든 리소스를 훔쳐보고 싶을 때가 있네요.

이것 저것 찾다가 가장 깔끔한 방법이 있어서 정리해 놓습니다.


apktool 이네요.


JAR파일은 여기서 다운로드 합니다.


윈도우즈 커멘트 창에서 apttool jar를 실행시키면 되는데, 방법은 아래와 같습니다.


>java -jar apktool_2.0.0b9.jar decode --no-src -f DOWNLOADED.apk


여러 옵션들이 있는데, 여기서 사용한 옵션이면 충분하네요.


"--no-src" : JAVA코드는 건너뛰기

"-f" : 기존에 있는 폴더는 강제로 삭제하고 다시 디컴파일하기


더 많은 옵션들은 여기를 보시면 됩니다.


오류가 발생할 때는 새 버전을 다운로드 받으면 되더군요.


^^

Posted by 데브로망스
Android2014. 8. 21. 12:41


[문제]
InentService를 상속받아서 구현을 했는데, onHandleIntent()가 호출되지 않는 경우가 발생했습니다.

[해결방법]
구글 문서에 따르면, Intent Service를 구현할 때는 생성자와 onHandleIntent()만 구현하면 된다라고 설명하고 있습니다. 다른 콜백 함수들은 구현해도 되고 안해도 되는 것 처럼 적어놨습니다.

That's all you need: a constructor and an implementation of onHandleIntent().

그런데, (저의 경우) onHandleIntent()가 호출되지 않아서 방법을 찾아봤더니, onStartCommand()함수를 구현하면 해결이 되었습니다. (그렇다면 항상 onStartCommand()를 재정의해야 하는 것 아닌가 싶은데 왜 저렇게 적어놨는지는 모르겠습니다.) 

이때, 반드시, 부모 함수(super)를 호출해줘야 하고 반환 값은 반드시 default로 구현된 내용을 유지해줘야 합니다. (즉, 반환값을 임의로 "START_STICKY"를 반환하면 안됩니다.)

If you decide to also override other callback methods, such as onCreate(), onStartCommand(), or onDestroy(), be sure to call the super implementation, so that the IntentService can properly handle the life of the worker thread.

For example, onStartCommand() must return the default implementation (which is how the intent gets delivered to onHandleIntent()):

결과적으로 코드는 아래와 같은 형태가 됩니다. 이제 다른 Activity나 다른 App에서 startService()를 호출하면 onHandleIntent()가 잘 호출됩니다. 당연히 startService()를 호출할 때 넣어준 Intent도 파라메터로 잘 들어옵니다.

public class MyIntentService extends IntentService {
	public static final String PARAM_IN_MSG = "IN_MSG";

	public MyIntentService() {
		super("MyIntentService");
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		return super.onStartCommand(intent, flags, startId);
	}
	
	@Override
	protected void onHandleIntent(Intent intent) {
		String msg = intent.getStringExtra(PARAM_IN_MSG);
		String resultTxt = msg + " "+ DateFormat.format("MM/dd/yy h:mmaa", System.currentTimeMillis());
		Toast.makeText(this, "onHandleIntent Called : " + resultTxt, Toast.LENGTH_LONG).show();
	}
}




[원문보기]


[추가1]
아무래도 뭔가 이상해서, IntentService의 코드를 찾아봤습니다.
코드에서는 onStartCommand()에서 onStart()를 호출. onStart()에서는 핸들러에 메시지를 던지고 결국 onHandleIntent()를 호출하고 있습니다. 코드에는 문제가 없어보이는데, 왜 상속받아 만든 클래스에서 onStartCommand()를 재정의해야만 onHandleIntent()가 호출되는 걸까요? 혹시, 아시는 분 계시면 한 수 가르쳐주세요 ^^

private final class ServiceHandler extends Handler {
	public ServiceHandler(Looper looper) {
		super(looper);
	}
	@Override
	public void handleMessage(Message msg) {
		onHandleIntent((Intent) msg.obj);
	}
}
@Override
public void onStart(Intent intent, int startId) {
	Message msg = mServiceHandler.obtainMessage();
	msg.arg1 = startId;
	msg.obj = intent;
	mServiceHandler.sendMessage(msg);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	onStart(intent, startId);
	return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}







Posted by 데브로망스