인증 및 초기설정, 지도 띄우기

ICS 이하 단말기 지원하기 : SupportMapFragment

http://developer.android.com에 있는 Testing 관련 글을 번역한 블로그


http://huewu.blog.me/110089695301

1. AndroidManifest.xml  파일의 manifest 엘리먼트 안에 아래와 같이 추가해준다.


<instrumentation android:name="android.test.InstrumentationTestRunner"

android:targetPackage="[앱 페키지 명]"

android:label="Test(임의 수정 가능)" />


2.    application 엘리먼트 바로 안에 다음과 같이 추가해준다.


<uses-library android:name="android.test.runner" />


1. 해당 프로젝트클릭->오른쪽마우스-> 맨 마지막의 properties 로 들어갑니다.

2. java Build Path 에서 Libraries 의 Add Library로 들어갑니다.

3. 여러 Library 중에서 JUnit 를 선택합니다.

4. JUnit Library 에서 버전을 4버전으로 선택하고 Finish를 눌러줍니다.


여기에서 자세한 설명을 보시길!

SurfaceView 사용법 

1. SurfaceView를 상속 받고, 

2. SurfaceHolder.Callback을 구현하여준 후에, 

3. SurfaceView의 그리기 작업을 핸들링 해줄, Thread를 구현한다.


SurfaceView 사용목적?

책을 보며 공부하는 중에, 여러 이미지를 연속으로 바꾸면서 애니메이션 효과를 만드는 예제가 나왔다. 이 예제에선 화면을 디스플레이하는 뷰로써 SurfaceView를 사용했다. 그런데 왜 굳이 SurfaceView를 사용했는지 전혀 나와있지 않아서 찾아왔다. (책이 좀 별로인 듯...)


가장 큰 이유는 성능이었다. 윈도우에서 DirectX라는 것을 써서 게임을 만드는 것 처럼 안드로이드에선  카메라, 비디오, 3D, OpenGL 같은 종류에 대해 Surface를 사용하며 그 Surface를 일반적인 윈도우 구성요소인 View에서 사용할수 있게 만들어주는 것이 SurfaceView라고 한다.


참조

http://www.androidpub.com/13353

메시지 기반의 쓰레드 이용은 핸들러를 사용하면서 코드가 복잡해지는 문제가 있다. 백그라운드 작업을 좀 더 쉽게 하고 싶다면 AsyncTask 클래스를 사용할 수 있다.


AsyncTask 객체를 만들고 execute 메소드를 실행하면 이 객체는 정의된 백그라운드 작업을 수행하고 필요한 경우에 그 결과를 메인 쓰레드에서 실행한다.


메소드명 

설명 

 execute

  • 백그라운드 작업 수행

 cancel

  • 백그라운드 작업 취소

doInBackground

  • 새로 만든 쓰레드에서 백그라운드 작업 수행
  • execute 메소드를 호출할 때 사용된 파라미터를 배열로 전달받음

onPreExecute

  • 백그라운드 작업 수행 전에 호출됨

onProgressUpdate

  • 백그라운드 작업의 진행 상태를 표시하기 위해 호출됨
  • 작업 수행 중간 중간에 UI 객체에 접근하는 경우에 사용됨
  • 이 메소드가 호출되려면 publishProgress 메소드를 호출하여야 함

onPostExecute

  • 백그라운드 작업이 끝난 후 호출됨

 getStatus

  • 작업의 진행 상황을 리턴
  • 상태는 PENDING, RUNNUNG, FINISHED로 표현됨


아래는 백그라운드에서 프로그레스바를 진행시키는 코드이다.

public class MainActivity extends Activity {

	TextView textView01;
	ProgressBar progress;
	BackgroundTask task;
	int value;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView01 = (TextView) findViewById(R.id.textView01);
        progress = (ProgressBar) findViewById(R.id.progress);

        // 실행 버튼 이벤트 처리
        Button executeBtn = (Button) findViewById(R.id.executeBtn);
        executeBtn.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		// 새로운 Task 객체를 만들고 실행
        		task = new BackgroundTask();
        		// 백그라운드 작업 수행
        		// doInBackground에 100을 전달
        		task.execute(100);
        	}
        });

        // 취소 버튼 이벤트 처리
        Button cancelBtn = (Button) findViewById(R.id.cancelBtn);
        cancelBtn.setOnClickListener(new OnClickListener() {
        	public void onClick(View v) {
        		task.cancel(true);
        	}
        });
    }


    /**
     * 새로운 Task 객체를 정의
     */
    class BackgroundTask extends AsyncTask<Integer, Integer, Integer> {
    	protected void onPreExecute() {
    		value = 0;
    		progress.setProgress(value);
    	}

    	protected Integer doInBackground(Integer ... values) {
    		//values[0] equal to 100;
    		while (isCancelled() == false) {
    			value++;
    			if (value >= 100) {
    				break;
    			} else {
    				publishProgress(value);
    			}

    			try {
    				Thread.sleep(100);
    			} catch (InterruptedException ex) {}
    		}

    		return value;
    	}

    	protected void onProgressUpdate(Integer ... values) {
    		progress.setProgress(values[0].intValue());
    		textView01.setText("Current Value : " + values[0].toString());
    	}

    	protected void onPostExecute(Integer result) {
    		progress.setProgress(0);
    		textView01.setText("Finished.");
    	}

    	protected void onCancelled() {
    		progress.setProgress(0);
    		textView01.setText("Cancelled.");
    	}
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
}


N-Queens Problem

  • N×N의 체스판에 N개의 퀸이 서로 위협하지 않도록(Path가 겹치지 않도록) 위치시키는 문제

4-Queens Problem의 상태공간트리


깊이우선검색(DFS)

  • 깊이우선검색(Depth First Search)을 사용하면 해답을 찾을 수 있다. 그러나 이 방법을 사용하면 해답이 될 가능성이 전혀 없는 노드의 후손(descendant) 노드까지 검색하기 때문에 비요율적이다. 위 그림에서 보면, 첫 번째 퀸이 (1, 1)에 놓일 경우 두 번째 퀸은 (2, 1)에  놓일 수 없으므로 (2, 1)의 후손노드는 검색할 필요가 없다.


노드의 유망성(Promising)

  • 전혀 해답이 나올 가능성이 없는 노드는 유망하지 않다(non-promising)고 하고, 그렇지 않으면 유망하다(promising)고 한다.
  • 위의 DFS에서 (2, 1)의 후손 노드는 non-promising하다.


되추적(Backtracking)

  • 어떤 노드의 유망성을 검사하여 유망하지 않다고 판정되면 후손 노드(서브트리)들에 대한 검색을 중지하고(Pruning), 그 노드의 부모노드로 돌아가서 다른 후손 노드에 대해 검색을 수행한다.


가지치기(Pruning)

  • 유망하지 않은 노드에 대에 진행을 하지 않는 것을 말한다.

4-Queens Problem의 되추적 상태공간트리


DFS VS Backtracking

  • 검색하는 노드 개수
    • DFS = 155 개 (전체 (1+4+16+64+256)개 중 155번째에서 솔루션을 찾게 됨)
    • Backtracking = 27 개


되추적 알고리즘의 개선

  • 유망성 여부에 대한 확인을 노드 방문 전에 실시


Java 소스코드

분석은 나중에...

public class NQueens {
  private static final int N = 4;
  private static int[] b = new int[N];
  private static int s = 0;
 
  static boolean unsafe(int y) {
    int x = b[y];
    for (int i = 1; i <= y; i++) {
      int t = b[y - i];
      if (t == x ||
          t == x - i ||
          t == x + i) {
        return true;
      }
    }
 
    return false;
  }
 
  public static void putboard() {
    System.out.println("\n\nSolution " + (++s));
    for (int y = 0; y < N; y++) {
      for (int x = 0; x < N; x++) {
        System.out.print((b[y] == x) ? "|Q" : "|_");
      }
      System.out.println("|");
    }
  }
 
  public static void main(String[] args) {
    int y = 0;
    b[0] = -1;
    while (y >= 0) {
      do {
        b[y]++;
      } while ((b[y] < N) && unsafe(y));
      if (b[y] < N) {
        if (y < N - 1) {
          b[++y] = -1;
        } else {
          putboard();
        }
      } else {
        y--;
      }
    }
  }
}


루퍼란?

  • 쓰레드에서 핸들러는 메시지큐에서 메시지를 읽어서 메인 쓰레드로 메시지를 전달하는 역할을 한다. 이 때 핸들러는 루퍼를 통해 메시지큐에서 메시지를 읽는다. 루퍼는 말 그대로 무한 루프 방식을 이용해 메시지 큐에 들어오는 메시지를 지속적으로 보면서 하나씩 처리한다. 메인 쓰레드는 UI 객체들을 처리하기 위해 메시지큐와 루퍼를 내부적으로 처리한다.
  • 그러나 별도의 쓰레드를 새로 만들었을 때는 해당 쓰레드에 루퍼가 존재하지 않기 때문에 다른 쓰레드에서 메시지를 전달받아 순차적으로 처리하고 싶다면 루퍼를 만들어야 한다.

루퍼는 아래와 같이 prepare와 loop 메소드를 통해 동작한다.

class ProcessThread extends Thread {
    ...
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
    ...
}


ListView는 화면에 보여줄 하나의 아이템에 해당하는 뷰를 ArrayAdapter의 getView 메소드에서 만들어낸다. getView에서 View를 생성하고, 데이터를 입혀서 반환하는 방식이다.


getView 메소드를 재정의하는 몇 가지 방식이 있는데, 어떻게 하느냐에 따라 성능의 차이가 있다.


1. 무식한 방법

  • 매 position마다 View를 생성한다.


2. 개선된 방법

  • 안드로이드에서는 선택위젯이 스크롤될 때 View를 재활용하는 메커니즘을 가지고 있어 한 번 만들어진 뷰가 화면 상에 그대로 다시 보일 수 있도록 되어 있다. getView 메소드의 convertView 인자가 null이 아니면 재활용하는 방법이다.


3. 더 개선된 방법

  • 2번 방법도 개선의 여지가 있는데, 바로 findViewById라는 메소드이다. 어떤 뷰에서 입력한 id에 해당하는 View를 리턴해 주는 메소드인데, 이 메소드는 layout에서 계층구조가 심해질수록 더 오래걸릴 것이다. 그래서 findViewById 메소드까지 최소한으로 사용하도록 개선하면 더 좋은 성능을 기대할 수 있다.
  • 이를 홀더 패턴을 사용하여 구현할 수 있다. View를 생성하는 시점에 홀더에 View에 대한 정보를 저장하고 setTag메소드를 사용하여 해당 View에 홀더를 지정한다. 이후 View를 재활용하는 시점에서 findViewById메소드를 사용하지 않고 getTag메소드를 통해 해당 View에 대한 정보를 가져온다.

자세한 내용은 여기

안드로이드에서는 단말의 해상도에 상관없이 크기를 지정하는 경우가 많다. 그런데 안드로이드에서는 이미지 원본이 있을 때 이 이미지가 보이는 영역이 이미지 원본의 크기와 다르면 자동으로 늘려주기 때문에 이미지에 왜곡이 발생할 수 있다.


이러한 문제를 나인패치를 사용하여 해결할 수 있다.


나인패치는 기본적으로 1픽셀의 검정색 선으로써 영역을 지정한다. 영역은 scalable area와 fill area가 있는데 LEFT&TOP이 scalable area가 되며, RIGHT&BOTTOM이 fill area가 된다.


이미지가 늘어날 때 scalable area만 늘어남으로써 왜곡을 최소화하며, 내용은 fill area에 채워진다.


나인패치로 만들어진 이미지는 파일 이름을 XXX.9.png처럼 파일 확장자 앞에 '.9'를 붙여주어야 한다.


참고 사이트

+ Recent posts