Place Picker 예제

|

Place Picker를 사용하기 위해서는 Google API Key를 등록해야 합니다. Google 라이브러리들 중 ‘Google Places API for Android’를 등록하면 됩니다.


예제 코드

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.google.android.gms.location.places.Place;
import com.google.android.gms.location.places.ui.PlacePicker;

public class MainActivity extends AppCompatActivity {

  int PLACE_PICKER_REQUEST = 1;

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

    findViewById(R.id.btn_place_picker).setOnClickListener(mOnClickListener);
  }

  View.OnClickListener mOnClickListener = new View.OnClickListener() {

    @Override
    public void onClick(View v) {
      switch (v.getId()) {
        case R.id.btn_place_picker:
          callPlacePicker();
          break;
      }
    }
  };

  void callPlacePicker() {
    PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder();
    try {
      startActivityForResult(builder.build(this), PLACE_PICKER_REQUEST);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == PLACE_PICKER_REQUEST) {
      if (resultCode == RESULT_OK) {
        Place place = PlacePicker.getPlace(data, this);
        String toastMsg = String.format("Place: %s", place.getName());
        Toast.makeText(this, toastMsg, Toast.LENGTH_LONG).show();
      }
    }
  }
}

Android Studio의 Debug에서의 SHA-1 값 알아내기

|

키스토어(Keystore)의 SHA-1 값이 필요한 경우가 있습니다. 보통 Keystore를 직접 생성하고 콘솔 창에서 SHA-1 값을 확인한다음 해당 Key를 이용해서 배포용 apk 파일을 만드는 경우가 많습니다.

하지만, 개발 도중에 Signed Key를 계속 적용하는 것은 번거롭기 때문에 여기서는 Android Sutdio에서 Debug 모드로 빌드할 때 사용하는 SHA-1 값을 확인하는 방법을 포스팅합니다.


Android Studio에서 SHA-1 값 확인하기

image -fullwidth

Android Studio 오른편에 보면 ‘Gradle’라는 버튼이 있습니다. Gradle 뷰안에서 SHA-1 값을 조회하기를 원하는 프로젝트를 선택한 다음 ‘Tasks → android → signingReport’를 더블 클릭하면 위 그림에서처럼 ‘Gradle Console’에 SHA-1 값이 출력됩니다.

Gradle 뷰 → 프로젝트 선택 → Tasks → android → signingReport


Google 공식 가이드

Google에서는 여기에서 디버그 모드에서의 SHA-1 값을 획득하는 방법을 잘 설명하고 있습니다.

디버그 모드에서 사용하는 Keystore는 debug.keystore 파일이며, 이 파일의 위치는

  • Windows : C:\Users\your_user_name.android\
  • MacOS 및 Linux : ~/.android/

입니다. 해당 위치로 이동하여 다음 커맨드를 입력하면 SHA-1 값을 획득할 수 있습니다.

  • Windows : keystore” -alias androiddebugkey -storepass android -keypass android
  • MacOS 및 Linux : keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

C++11 Future와 Async

|

Thread의 결과값 획득하는 방법

std::futurestd::promise를 이용하면 다른 Thread의 결과값을 쉽게 획득할 수 있습니다. Thread에서 연산을 완료한 후 그 결과값은 promise에 저장합니다. 이후, future를 이용해서 그 값을 획득할 수 있습니다.

Thread 결과값은 다음과 같은 코드를 이용해서 획득가능합니다. Thread의 결과값을 받을 때까지 get() 부분은 Blocking 되어 대기합니다.

  future<T> fut = ...; 
  T res = fut.get();

promise의 사용은 다음과 같이 할 수 있습니다.

  promise prom = ...;
  T val = ...;
  prom.set_value(val);


async 함수

async 함수를 사용하면 특정 함수 등을 Thread로 구동시키고 그 결과를 리턴받을 수 있습니다. 다음과 같은 예제 코드를 살펴보면,

#include <cstdio>
#include <future>

using namespace std;

int fun() {
  for (int i = 1; i <= 10; i++) {
    printf("fun[%d]\n", i);
  }

  return 200;
}

int main() {
  //auto fut = async(fun);
  auto fut = async(launch::async, fun);
  //auto fut = async(launch::deferred, fun);

  for (int i = 1; i <= 10; i++) {
    printf("main[%d]\t", i);
  }
  printf("\n");

  int result = fut.get();

  printf("result : %d\n", result);

  return 0;
}

async를 통해 실행한 결과값을 get() 함수를 이용해서 돌려받을 수 있는 것을 확인할 수 있습니다.

위 예제에서 async 호출하는 부분을 3가지 예시로 들었는데, 만약

  auto fut = async(launch::async, fun);

으로 수행하면, 함수 fun()은 즉시 실행이 되고 그 결과는 future에 저장이 됩니다. (실행해보면 메인 Thread와 별도 Thread가 동시에 돌아가는 것을 확인할 수 있습니다.)


만약, async 부분을 다음과 같이 호출한 경우는

  auto fut = async(fun);    // 또는
  auto fut = async(launch::deferred, fun);

fun() 함수는 바로 실행되는 것이 아니라 ‘int result = fut.get()’ 코드가 실행될 때 fun() 함수가 실행되는 것을 확인할 수 있습니다.

C++11 조건 변수(Condition Variable)

|

조건 변수

조건 변수는 헤더 파일을 `include` 해야 사용할 수 있습니다.

조건 변수를 사용하면 특정 조건이 만족될 때까지 현재 Thread를 Blocking 할 수 있습니다. 이 때 특정 조건으로는 notify 이벤트를 주거나 타임 아웃 등이 될 수 있습니다.

조건 변수는 각 Thread를 Blocking 함으로 호출 순서를 조절하게 해서 결과적으로는 Thread 간 통신을 가능하게 해주는 효과를 가집니다.


조건 변수의 구성

조건 변수는 크게 wait()notify_one()이나 notify_all() 함수를 세트로 구성되어집니다.

notify_one() 함수는 해당 조건 변수를 기다리고 있는 Thread들 중 한 개의 Thread를 깨웁니다. notify_all() 함수는 조건 변수를 기다리는 모든 Thread를 깨웁니다.

wait()를 호출하는 Thread는 먼저 락 객체를 점유하고 있는 상태여야 합니다. wait()를 호출하면 해당 락 객체의 unlock()이 호출되고 Thread가 Blocking 됩니다.


조건 변수의 활용

조건 변수를 가장 잘 활용할 수 있는 예제로 메세지 큐(Message Queue)를 들 수 있습니다.

다음과 같은 코드를 이용해서 Queue에 명령을 집어넣고 notify_one() 이벤트를 날립니다.

  void enqueue(T t) {
    unique_lock<std::mutex> lock(mMutex);
    mQueue.push(t);
    mCondition.notify_one();
  }


또한 해당 Thread는 메세지를 처리하고, 다시 wait()로 Blocking 상태로 들어갑니다.

  T dequeue(void) {
   unique_lock<std::mutex> lock(mMutex);
    while((mIsLoop)&&(mQueue.empty())) {
      mCondition.wait(lock);
    }
    T val = mQueue.front();
    mQueue.pop();
    return val;
  }

C++11 상호 배제(Mutex)

|

Mutex 종류

C++ 표준에서는 Mutex 종류는 크게 2가지가 있습니다. 하나는 일반 Mutex이며, 다른 하나는 타임 아웃 Mutex 입니다.


일반 Mutex

일반 Mutex에는 std::mutex, std::recursive_mutext 두 종류가 있습니다. 두 클래스는 공통으로 다음과 같은 함수를 갖고 있습니다.

  • lock() : Thread에서 락 점유를 시도하며, 락을 획득할 때까지 무한정 대기함.
  • try_lock() : 락 점유를 시도하며, 성공시 true, 실패시 false를 바로 리턴함.
  • unlock() : 락을 해제함.

std::mutex는 중복해서 lock()을 호출할 경우 ‘데드락(Deadlock)’ 상황에 빠질 수 있습니다.

std::recursive_mutexlock()을 여러 번 호출해도 상관없지만, 대신 락을 해제할 때는 동일한 횟수만큼 unlock()을 호출해주어야 락이 해제됩니다.


타임 아웃 Mutex

타임 아웃 Mutex에는 std::timed_mutex, std::recursive_timed_mutex가 있습니다. 그리고 다음과 같은 함수들을 갖고 있습니다.

  • lock() : Thread에서 락 점유를 시도하며, 락을 획득할 때까지 무한정 대기함.
  • try_lock() : 락 점유를 시도하며, 성공시 true, 실패시 false를 바로 리턴함.
  • unlock() : 락을 해제함.
  • try_lock_for(rel_time) : 주어진 시간까지 락 점유를 시도. 시간은 상대 시간임. 성공시 true, 실패시 false를 바로 리턴.
  • try_lock_until(abs_time) : 주어진 시간까지 락 점유를 시도. 시간을 절대 시간임. 성공시 true, 실패시 false를 바로 리턴.


lock 클래스

Mutex를 좀 더 사용하기 쉽게 Wrapping한 클래스입니다. lock 클래스의 소멸자는 자동으로 내부의 Mutex를 해제해주기 때문에, 각 함수의 로컬 변수로 사용할 경우 함수를 빠져나오면서 자연스럽게 락이 해제됩니다. 즉, 락 해제에 신경을 덜 쓸 수 있습니다.

lock_guard는 생성자에서 락 점유를 시도하며, 점유를 성공할 때까지 Blocking 됩니다.

unique_lock은 객체를 미리 선언해두고, 나중에 필요한 시점에 락 점유를 시도할 수 있습니다. lock(), try_lock(), try_lock_for(), try_lock_until() 등의 함수도 제공합니다.


lock 예제

#include <mutex>

using namespace std;

class LockExample {
 public:
  void sendMessage(int what, int arg1, int arg2);

 private:
  mutable mutex mMutex;
};

void LockExample::sendMessage(int what, int arg1, int arg2) {
  unique_lock<mutex> lock<mMutex>

  mMessageSender->sendMessage(what, arg1, arg2);
}


이중 검사 락 알고리즘

멀티 쓰레드 환경에서 어떤 부분이 단 한 번만 호출이 되어야 한다면 이중 검사 락 알고리즘을 사용할 수 있습니다. 물론, call_once()를 사용한다면 굳이 이 알고리즘을 사용할 필요는 없을 것 같습니다.

아래 예제는 isInitialized 변수를 확인한 다음 초기화를 수행하고 isInitialized 변수의 값을 true로 세팅하는 예제입니다. isInitialized 변수가 true로 세팅되기 직전에 다른 쓰레드가 끼어드는 것을 방지하기 위해서 이중 검사 락 알고리즘을 사용했습니다.

#include <cstdio>
#include <thread>
#include <mutex>
#include <vector>

using namespace std;

class LockExample {
 public:
  void init() {
    printf("Init()\n");
  }
};

bool isInitialized = false;
mutex mtx;

LockExample exam;

void func() {
  if (isInitialized == false) {
    unique_lock<mutex> lock(mtx);

    if (isInitialized == false) {
      exam.init();
      isInitialized = true;
    }
  }

  printf("func()\n");
}

int main() {
  vector<thread> threads;

  for (int i = 0; i < 5; i++) {
    threads.push_back(thread{func});
  }

  for (auto &t : threads) {
    t.join();
  }

  return 0;
}