AsyncTask와 같이 사용하는 ProgressDialog를 사용해서 태스크를 바로 종료시키기

안드로이드 애플리케이션이 데이터를 로딩하는데 AsyncTask를 사용하고, 사용자에게 데이터 로딩을 알려주려고 ProgressDialog를 같이 사용하는 경우를 종종 볼 수 있다. 아래의 그림이 이 예제이다.

위 그림의 상황에서, 사용자가 다른 화면을 보길 원하거나 더는 로딩하기 원하지 않는 경우에도 ProgressDialog 클래스의 cancelable 변수를 false로 설정해서 백 키를 눌러도 데이터 로딩창을 종료하지 않고 계속 보이는 경우를 볼 수 있다. UX의 관점에서 보면, 이런 경우는 좋지 않다.

같은 상황에서 iOS 애플리케이션들의 UX를 보니, 많은 경우 화면을 전환하기 전에 데이터를 로딩하고 로딩을 완료하면 화면을 전환한다. 이 UX로 인해서 사용자는 로딩을 취소하고 쉽게 다른 화면으로 전환할 수 있다. 물론 꼭 그렇지는 않지만, 필자의 경우에는 비교적 iOS의 UX가 안드로이드보다 좋게 느껴진다. 그래서 안드로이드 애플리케이션도 ProgressDialog 창을 종료시키고, 데이터 로딩과 같은 비동기 요청(AsyncTask를 사용하는 경우)을 바로 취소하는 방법을 사용해서 비교적 UX를 개선할 수 있다. 사용자에게 조금 더 좋은 UX를 제공할 수 있는 이 방법을 살펴보자.

1. 백 키를 사용해서 종료하는 ProgressDialog 객체 생성

		pd = new ProgressDialog(this);
		pd.setTitle("");		
		pd.setMessage("Loading...");
		pd.setCancelable(true);
		pd.setOnDismissListener(new OnDismissListener() {
			@Override
			public void onDismiss(DialogInterface dialog) {
				Log.d(TAG, "onDismissed() ");
				
				task.cancel(true);
				//task.cancel(false);
			}
		});

이 코드는 ProgressDialog 객체를 생성하면서 백 키로 창을 종료하면서 Dismiss 이벤트를 받아서 비동기 데이터를 로딩하는 태스크인 task를 취소한다.

2. 비동기로 데이터를 로딩하는 AsyncTask 클래스

	static class LoadingTask extends AsyncTask<String, Integer, Boolean> {
		private ProgressDialog pd = null;
		
		public LoadingTask(ProgressDialog pd) {
			this.pd = pd;
		}
		
		@Override
		protected void onPreExecute() {	
			super.onPreExecute();
			if(pd != null)
				pd.show();
		}
		
		@Override
		protected Boolean doInBackground(String... params) {		
			try {
				Thread.sleep(1000 * 20);
			} catch(InterruptedException e) {
				Log.d(TAG, "Exception : " + e.getLocalizedMessage());
			}
			
			return Boolean.TRUE;
		}

		@Override
		protected void onCancelled(Boolean result) {
			Log.d(TAG, "onCancelled : " + result);
			
			if(pd != null)
				pd.dismiss();
		}
		
		@Override
		protected void onPostExecute(Boolean result) {
			Log.d(TAG, "onPostExecute : " + result);
			
			if(pd != null)
				pd.dismiss();
		}
	}

이 클래스는 비동기로 데이터를 로딩하는 형태로 비동기의 태스크 로직 메서드에서 20초를 Sleep하고 있다. 그리고 이 태스크의 취소에 대한 콜백 메서드인 onCancelled(Boolean result) 메서드를 확인할 수 있다.

이제 위에서 살펴본 코드를 사용해서 사용자가 백 키를 누른 경우, ProgressDialog를 종료하고 LoadingTask도 종료시킬 수 있다. AsyncTask를 종료하는 방법으로 cancel(boolean mayInterruptIfRunning) 메서드를 호출한다. 이 메서드를 호출하면 내부에서 cancelled 변수를 true로 설정한다. 그리고 mayInterruptIfRunning를 true로 호출하면 바로 종료시키기 위한 인터럽트를 던지게 된다. 그래서 AsyncTask가 IO를 looping하면서 읽어들이지 않는 경우에는 cancel(true)로 바로 종료시킬 수 있다.

다음으로 ProgressDialog가 종료하면서 AsyncTask를 취소하는 메서드의 호출 결과를 살펴보자.
– 아래는 cancel(false)을 호출한 결과이다.

01-30 05:56:05.720: D/MainActivity(1416): onDismissed() 
01-30 05:56:24.136: D/MainActivity(1416): onCancelled : true

이 결과로 cancel(false)을 호출하면, doInBackground()가 종료된 후에 onCancelled가 호출된 것을 알 수 있다.

– 아래는 cancel(true)을 호출한 결과이다.

01-30 05:58:33.038: D/MainActivity(1584): onDismissed()
01-30 05:58:33.038: D/MainActivity(1584): Exception : null
01-30 05:58:33.038: D/MainActivity(1584): onCancelled : true

cancel(true)을 호출 결과로 AsyncTask의 doInBackground() 메서드에서 InterruptedException이 발생하게 되고, 이것으로 바로 태스크 로직을 빠져나오게 한다.

이상 살펴본 형태로 AsyncTask와 ProgressDialog를 결합해서 사용하는 경우에 사용자의 반응에 즉시 반응할 수 있는 형태로 개발해서 UX를 개선할 수 있다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.