티스토리 뷰

요즘 자바를 하면서 느끼는건데, 자바는 멀티스레딩이 다른 언어처럼 복잡하지 않고 참 깔끔하게 되네요. 보통 C++같은 언어라면 스레드간 통신할때 패킷을 먼저 정의한 다음, 패킷 날려서 받으면 그 스레드가 정해진 코드를 실행하는 식이겠죠. 

그런데 자바는 anonymous class가 있으니 실행하고 싶은 코드를 만들어서 post해주면 바로 실행되니 참 편하죠. 특히 뷰와 로직이 섞여있는 클라이언트 프로그램에서 정말 편합니다.

일반적으로 이렇게 할 것 같습니다. 

<소스 코드는 이해를 돕기 위해 단순하게 변형시켰습니다.>

Button postButton = (Button) findViewById(R.id.PostButton);
postButton.setOnClickListener(new OnClickListener() {
	@Override
	public void onClick(View v) {
		final GeoPoint myloc = getMyLocation();

		if (myloc != null) {
			// 스레드를 생성해서 Runnable을 실행시키는 헬퍼입니다
			ThreadHelper.runAsync(new Runnable() {
				@Override
				public void run() {
					// 서버에 요청하는 코드입니다.
					final int postid = XmlRpc.post(myloc, 100, 100, "hello andro", 10000);

					mHandler.post(new Runnable() {
						@Override
						public void run() {
							if (postid != -1) {
								// 알림 메시지를 띄웁니다.
								Helper.toast(R.string.POSTED);
								
								update();
							} else {
								Helper.toast(R.string.POST_FAILED);
							}									
						}
					});
				}
			});
		}
	}
});

안드로이드 코드인데, 간단히 설명하면 post 버튼이 눌렸을때 다음 작업을 합니다.

1. UI스레드 - 내 위치를 가져와서
2. 로직스레드 - 서버에 포스트를 하고
3. UI스레드 - 포스트 결과에 따라서 알림 메시지를 뿌림.

보니까 UI스레드에서 처리할 작업이 두개고 로직스레드에서 처리할 작업이 하나입니다. 그리고 이 코드가 한곳에 다 몰려있죠.
다행이도 이 코드는 짧아서 로직에서 처리하는 작업도 별거 없습니다. 하지만 로직이 복잡해지면 - 항상 볼수있듯이 - UI와 로직이 섞이겠죠.


다음 예제를 봅시다.

이번엔 새로운 기능을 추가하겠습니다. 서버에서 메시지 목록을 가져와서 보여주는 기능입니다.
우선, 서버에서 전체 메시지를 요청할 필요가 없게 받은 메시지는 메모리에 저장해놓습니다.
그리고 새로운 메시지가 있는지만 서버에 요청하면 되죠.

이 작업은 첫번째 예와 마찬가지로 UI->로직->UI의 흐름이 필요합니다.
하지만 앞의 예와는 다르게 코드가 조금 길어지니 UI와 로직이 많이 섞일 것 같네요. 그래서 조금 다르게 생각해봤습니다.

public void updateMessageList(final int id, final Handler uihandler, 
	final ParamRunnable before, final ParamRunnable after) {
	final MessageList saved = getMessageList(id);

	before.setParam(saved);
	uihandler.post(before);
	
	ThreadHelper.runAsync(new Runnable() {
		@Override
		public void run() {
			MessageList lastlist = XmlRpc.getMessageList(id, saved.getLastMessageId());
			
			if(lastlist == null) {
				after.setParam(null);					
			}
			else {
				saved.getList().addAll(lastlist.getList());
				after.setParam(lastlist);
			}
			
			uihandler.post(after);
		};
	});
}

updateMessageList 메소드는 업데이트 전과 후로 Runnable 인스턴스를 두개 받습니다. 첫번째는 업데이트 전, 전체 목록을 보여줄 UI코드이고 두번째는 업데이트 된 최신 메시지를 추가해서 보여줄 UI 코드이죠.

위 코드는 UI 코드가 섞이지 않은 순수 로직 코드입니다. 물론 최초 updateMessaegList를 실행하는 스레드는 UI스레드겠지만, 이후 로직 스레드를 새로 생성하고, 로직이 리턴되면 바로 UI스레드로 Runnable 코드를 넘겨버립니다.

그리고 이 updateMessageList메소드를 사용하는 UI코드에서는 로직 없이 메시지 업데이트 전과 후에 UI를 보여줄 코드만 넣어주면 됩니다.

DataManager.getInstance().updateMessageList(id, mHandler,
		// before
		new ParamRunnable() {
			@Override
			public void run() {
				mListAdapter.set(getParam().getList());
			}
		}, 
		
		// after
		new ParamRunnable() {
			@Override
			public void run() {
				MessageList lastlist = getParam();
				
				if(lastlist == null){
					Helper.toast(R.string.LOAD_ERROR);
					return;
				}

				mListAdapter.addAll(lastlist.getList());
			}
		});

이렇게 UI와 로직을 서로 격리시켰습니다. 

어떤가요? 솔직히 이게 정말 좋은 방법이다! 라고 말할수는 없겠습니다. 이렇게 했더니 없던 문제가 생겨났으니까요.

UI 코드와 로직 코드의 통신이 불편해집니다. 

맨 처음 예제처럼 한곳에 모든 코드가 있으면 하위 코드에서 상위 변수를 마음대로 참조할 수 있죠. 하지만 이 솔루션으에서는 ParamRunnable이라는 클래스가로 로직이 UI에게 파라매터를 넘겨줬습니다. 물론 이 부분은 ParamRunnable에 들어갈 파라매터로 데이터 클래스를 일일히 만들면 되면 동일한 효과입니다만... 불편하죠.

어떤게 더 좋은 방법일지는 모르겠습니다만... 로직과 UI의 결합도가 낮고 코드가 좀 길어지는 상황이라면 이것도 괜찮은 방법일 수도 있습니다.

'Programming > Java' 카테고리의 다른 글

자바 초 간단 로거  (2) 2011.05.26
자바 뷰와 로직의 코드&스레드 분리  (0) 2011.01.27
댓글
댓글쓰기 폼