안드로이드에서 이미지를 보여주는 방법은 여러 가지가 있습니다. 가장 기본적인 방법은 어플내에 있는 그림파일을 보여주는 것인데, 어플에서 여러 그림을 사용하는 경우 어플의 용량이 증가하는 단점이 있고, 동적으로 그림을 바꿔주지 못한다는 단점이 있습니다. 동적으로 그림을 바꿔주지 못한다는 것은 새로운 그림으로 교체하고 싶을 때는 어플을 업데이트해야지만 그림을 교체할 수 있다는 것입니다.
이런 문제점들을 해결하는 방법은 서버에서 이미지를 받아오는 방법입니다. 서버에서 그림을 받아오기 때문에 어플용량에 대한 부담을 덜어주고, 이미지를 바꾸고 싶은 경우에는 서버에서 이미지만 교체하면 됩니다.
단순히 서버에서 이미지를 받아오는 것은 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public void loadBitmap(String strUrl) { Bitmap bitmap = null; try { URL url = new URL(strUrl); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(is); imView.setImageBitmap(bmp); } catch (IOException e) { e.printStackTrace(); } } |
그림파일에 대한 URL을 받아와서 HttpURLConnection 을 이용해 연결한후, 그림을 스트림으로 받아와서 비트맵파일로 만들어 마지막에 imView.setImageBitmap 을 사용해 뷰에 그림을 띄웁니다.
하지만 이 코드를 사용해서 프로그램을 실행시키면 다음과 같은 오류가 발생합니다.
이는 허니콤버전 이후 안드로이드에서 네트워크 작업을 할 때 메인스레드에서 실행 시 발생하는 오류입니다. 네트워크 작업은 별도의 스레드에서 하는 것이 안정적입니다. 지난 포스팅에서 AsyncTask 를 배웠으니 AsynTask 를 사용해서 프로그램을 만들어 보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | package com.example.downloadimg; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Bundle; import android.view.Menu; import android.widget.ImageView; public class MainActivity extends Activity { ImageView imView; String imgUrl = "http://dnllab.incheon.ac.kr/appimg/"; Bitmap bmImg; back task; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); task = new back(); imView = (ImageView) findViewById(R.id.imageView1); task.execute(imgUrl+"img1"); } private class back extends AsyncTask<String, Integer,Bitmap>{ @Override protected Bitmap doInBackground(String... urls) { // TODO Auto-generated method stub try{ URL myFileUrl = new URL(urls[0]); HttpURLConnection conn = (HttpURLConnection)myFileUrl.openConnection(); conn.setDoInput(true); conn.connect(); InputStream is = conn.getInputStream(); bmImg = BitmapFactory.decodeStream(is); }catch(IOException e){ e.printStackTrace(); } return bmImg; } protected void onPostExecute(Bitmap img){ imView.setImageBitmap(bmImg); } } } |
저는 back이라는 클래스를 만들어서 AsyncTask 를 extend했습니다. AsyncTask 의 첫 번째 generic 은 백그라운드 작업을 담당하는 메소드인 doInBackground 에 넘길 generic 으로 그림의 주소 값을 가지고 있는 String을 넣었습니다. 가운데 generic은 onProgressUpdate 메소드를 위한 generic인데 사용하지 않아서 그냥 Integer를 넣었습니다. Void를 넣어도 상관없습니다. 마지막 generic은 doInBackground 메소드가 끝나고 반환하는 generic으로 비트맵 타입을 넣었습니다.
이렇게 되면 doInBackground 메소드는 그림파일의 주소가 있는 String타입의 인자값을 받아와서 그림파일을 bitmap형식으로 반환하게 됩니다. bitmap 형식으로 반환하도록 설정한 이유는, 메인스레드가 아닌 다른 스레드에서 UI 객체에 접근할 수 없기 때문입니다. 만약 bitmap을 반환하지 않고 doInBackground 에서 바로 UI 객체에 접근해 뷰.setImage 등을 사용하여 변화시키려 한다면 에러가 발생합니다. 따라서 메인쓰레드에서 실행되는 onPostExecute 메소드를 통해 그림파일을 전달해서 UI객체에 접근하는 방법을 사용합니다.
onPostExecute 메소드를 보면 인자값으로 Bitmap img 가 되어있는 것을 확인할 수 있습니다. onPostExecute 는 메인 스레드에서 실행되기 때문에 imView.setImageBitmap 을 사용하여 UI 객체에 접근이 가능합니다. 실질적으로 이미지를 변경하는 부분은 이 부분이라고 할 수 있습니다.
이렇게 back 클래스를 만들고 나서 메인클래스에서 back 클래스의 객체를 만들고 execute 메소드를 사용하면 asynctask 를 사용할 수 있습니다.