http://java.elex.pe.kr/2014/01/blog-post.html

동적할당을 사용하는 이유는 무엇일까?

c언어의 메모리 구조에 대해서 알고 있다면 동적할당을 사용하는 이유를 쉽게 알 것이다.


2014/06/26 - [Programming/C언어] - [C] 스택(Stack), 힙(Heap), 데이터(Data)영역


malloc 함수

 - 동적으로 메모리를 할당하는 함수 (힙 영역에 메모리를 할당)


#include <stdlib.h>

void* malloc(size_t size)	// malloc 함수의 원형

함수 호출시 할당하고자 하는 메모리의 크기를 바이트 단위로 전달하면 그 크기만큼 메모리를 할당하게 된다.

그리고 할당한 메모리의 주소(첫 번째 바이트의 주소)를 리턴한다.

메모리 할당에 실패하면 NULL이 리턴된다.


리턴형이 void*(void 포인터) ??


malloc은 단순히 메모리만 할당하는 함수이기 때문에 개발자가 어떠한 데이터 형을 저장하는지 예측할 수 없다.

예를들어 4바이트를 할당하였을 경우 int형 데이터를 저장하기 위해서 사용하는지, float형 데이터를 사용하는지 예측할 수 없기 때문에 void포인터를 반환하여 개발자가 알맞은 용도로 변환하여 사용할 수 있도록 만든것이다.


예를들어 int형 데이터를 저장하기 위해서는 리턴되는 void*을 int*로 변환해야 한다.

int *i = (int*) malloc (sizeof(int));



위의 그림은 포인터 변수 i에 4바이트를 할당하는 그림이다.


1. sizeof(int)의 값은 4이다. 4라는 값을 전달하면서 malloc 함수를 호출한다.

2. 할당된 메모리의 주소가 void*형으로 리턴된다. 리턴되는 void*를 사용하려는 int*형으로 변환한다.

3. 포인터 변수 i에 대입한다.


malloc함수 사용 예

동적 할당을 사용하여 arr_1의 배열의 값을 대입하는 소스를 보며 malloc함수 사용법을 이해해보자.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	return 0;
}

free 함수

 - 힙 영역에 할당된 메모리를 해제하는 함수


메모리를 할당만 하고 해제해 주지 않는다면, 언젠가는 메모리가 부족한 현상이 발생 할 것이다.

할당된 메모리가 더 이상 필요하지 않을경우 free함수를 이용하여 메모리를 해제시켜 줘야한다.


#include <stdlib.h>

void free(void* ptr)	// free 함수의 원형


free함수 사용 예

위의 예제에서 free함수만 추가시켰다.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}

calloc 함수

 - calloc함수는 malloc함수와 같은 기능을 지니고 있다. 다만 사용하는 형태가 조금 다를 뿐이다.


#include <stdlib.h>

void* calloc(size_t elt_count, size_t elt_size)	// calloc 함수 원형

calloc 함수는 elt_size 크기의 변수를 elt_count 개 만큼 저장할 수 있는 메모리 공간을 할당하라는 의미를 갖는다.


calloc함수 사용 예

위의 예제에서 malloc 함수대신 calloc 함수를 사용하였다.

#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[5];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 5; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	//arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함
	arr_2 = (int*) calloc(5, sizeof(int));	// sizoe(int)크기의 변수를 5개 저장할 수 있는 공간할당

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}

malloc함수와 calloc함수의 차이점!

malloc은 할당된 공간의 값을은 바꾸지 않는다.

calloc은 할당된 공간의 값을 모두 0으로 바꾼다.

배열을 할당하고 모두 0으로 초기화할 필요가 있을경우에는 calloc을 쓰면 편하다.


realloc 함수

 - 이미 할당한 공간의 크기를 바꿀 때 realloc 함수를 사용한다.


#include <stdlib.h>

void* realloc(void* memblock, size_t size);	// realloc 함수의 원형

이미 할당한 포인터 변수를 memblock에 넣고, 바꾸고 싶은 공간의 크기를 size에 입력하여 사용한다.


realloc함수 사용 예

malloc함수를 사용한 예제에서 realloc 함수를 사용하여 변경하였다.


#include <stdio.h>
#include <stdlib.h>

int main() {
	int arr_1[10];	// 배열 선언
	int *arr_2;		// 포인터 변수 선언
	int i;

	for(i = 0; i < 10; i++) {
		arr_1[i] = i+1;	// 배열에 값 대입
	}

	arr_2 = (int*) malloc(sizeof(int)*5);	// 메모리 할당, 배열의 크기만큼 할당하기 위해 5를 곱함

	for(i = 0; i < 5; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	printf("\n");

	// sizeof(int) = 4바이트
	realloc(arr_2, sizeof(int)*10);	// arr_2의 메모리를 40바이트로 재 할당
	// arr_2의 메모리 크기 : 20바이트 -> 40바이트

	for(i = 0; i < 10; i++) {
		arr_2[i] = arr_1[i];
		printf("%d ", arr_2[i]);
	}

	free(arr_2);	// free함수를 이용하여 메모리 해제

	return 0;
}



  1. 미소 2015.03.05 22:35 신고

    잘보고갑니다

  2. sms 2015.09.29 16:32 신고

    정리를 정말 깔끔하게 해두셨네요. 잘 보고 갑니다.

  3. 눈작은나 2015.10.03 11:44 신고

    디테일한 부분까지~~ 엔지니어의 숨결이..

  4. HightSchool Strudent 2015.12.23 00:04 신고

    안녕하세요, C언어를 공부하고 있는 고등학생입니다.

    malloc을 통한 동적할당을 쓰고 싶은 곳이 있어 공부하고 있는 중인데,
    자꾸 return이 실행되기 직전에 프로그램이 중지되네요...;;

    짠 코드는,

    int *seque, size;
    scanf("%d", &size);

    seque = (int *)malloc(size);
    int i;
    for(i=0; i<size; i++){
    seque[i]=i;
    }
    for(i=0; i<size; i++){
    printf("%d ", seque[i]);
    }
    printf("\n");
    printf("TheEnd_________________EndLine_________________\n");
    free(seque);

    return 0;
    }

    입니다. 16, 32와 같은 수를 입력해주면 분명히 그 전 수 까지 다 출력하고

    TheEnd_________________EndLine_________________ 도 문제 없이 출력합니다.
    그런데, 그 직후에 프로그램 실행 성공에 대한 정보(IDE는 Dev Cpp를 사용하고 있습니다.)를 띄우지 못하고 중지되었다는 알림이 뜹니다.

    같은 코드를 int형으로 하지 않고, char 형으로 바꾸어 하면 문제가 없이 실행 종료까지 되고요.

    뭐가 문제이고 어떻게 해야하나요?

    • 별거없는대학생 2016.01.11 16:51 신고

      size가 integer형이기 때문에 크기는 4byte입니다.
      동적할당을 seque에 하고 싶다면 seque = (int *)malloc(sizeof(int)*size)라고 하는게 맞는 것 같습니다.

  5. 주로스 2016.01.19 17:31 신고

    그렇군요... calloc( )의 예가 잘 정리되있네여 감사감사

  6. 2016.01.26 01:20

    비밀댓글입니다

  7. ralock 2016.05.04 13:09 신고

    Good!! 정리 잘되있네요

  8. 하영 2016.05.08 18:15 신고

    너무 좋은 글 감사합니다!

  9. internet 2016.06.15 09:04 신고

    동적할당 ㅎㅎ
    잘보고갑니다

  10. 굿굿 2016.07.05 17:56 신고


    리버싱 공부하면서 책이나 인터넷에 그렇다고 하니 그런줄말알고 넘어갔던 내용들인데
    정리가 잘 되어있어 공부하는데 많은 도움이 되었습니다. 감사합니다

  11. 대3 2016.10.12 21:02 신고

    대학교 컴공 3학년인데 먼말인지 모르겠네요 ㅎ

    자살각

    • 이제막신입 2016.10.18 10:14 신고

      허허;;; 컴공 3학년이시라면서;;;
      그럼 안 되는데.

  12. sad 2016.11.24 11:14 신고

    깔끔한 설명 감사합니다

  13. ck10 2017.09.05 22:56 신고

    감사합니다 덕분에 잘배워갑니다!

  14. c+=c++ 2018.06.09 22:37 신고

    와우, 깔끔한 설명 감사합니다.

C언어의 메모리 구조


프로그램을 실행시키면 운영체제는 우리가 실행시킨 프로그램을 위해 메모리 공간을 할당해준다. 

할당되는 메모리 공간은 크게 스택(Stack), 힙(Heap), 데이터(Data)영역으로 나뉘어진다. 

이러한 메모리 공간이 어떠한 용도로 언제, 어디서 할당되는지 알아보도록 하자.


할당 시기 : 프로그램이 실행될 때마다

할당 장소 : 메인 메모리(RAM)

할당 용도 : 프로그램 실행 시 필요한 메모리 공간(지역변수, 전역변수 선언을 위해) 할당




데이터(Data) 영역


 - 전역 변수와 static 변수가 할당되는 영역

 - 프로그램의 시작과 동시에 할당되고, 프로그램이 종료되어야 메모리에서 소멸됨

 

#include <stdio.h>

int a = 10;	// 데이터 영역에 할당
int b = 20;	// 데이터 영역에 할당

int main() {

	...

	return 0;
}

위와 같은 코드에서 int형 변수 a, b는 프로그램 실행시, main 함수가 호출되기 전에 데이터 영역에 할당된다.

그렇기 때문에 프로그램이 종료될 때까지 메모리상에 존재한다.

(전역변수가 프로그램이 종료될 때 까지 존재하는 이유)



스택(Stack) 영역


 - 함수 호출 시 생성되는 지역 변수와 매개 변수가 저장되는 영역

 - 함수 호출이 완료되면 사라짐

 

#include <stdio.h>

void fct1(int);
void fct2(int);

int a = 10;	// 데이터 영역에 할당
int b = 20;	// 데이터 영역에 할당

int main() {

	int i = 100;	// 지역변수 i가 스택 영역에 할당

	fct1(i);
	fct2(i);

	return 0;
}

void fct1(int c) {
	int d = 30;	// 매개변수 c와 지역변수 d가 스택영역에 할당
}

void fct2(int e) {
	int f = 40;	// 매개변수 e와 지역변수 f가 스택영역에 할당
}

main함수fct1, fct2라는 함수를 추가하였다. 

a, b를 데이터 영역에 할당한 뒤에 main함수를 호출하면서 int형 변수 i는 지역변수로서 스택영역에 할당된다.

그 뒤에 fct1()이라는 함수를 호출하면서 fct1함수의 매개변수인 cd가 스택영역에 할당된다.

fct1()이라는 함수호출이 끝나면 cd는 스택영역에서 삭제되며, 

그 뒤 fct2()라는 함수를 호출하면서 매개변수 e와 지역변수 f가 스택영역에 할당된다.

스택영역은 그 이름그대로 스택의 성질을 띄고있다.


 

힙(Heap) 영역


 - 필요에 의해 동적으로 메모리를 할당 할 때 사용


지금까지 데이터영역과 스택영역을 알아보았는데, 저 두가지 영역만 있으면 코드를 문제없이 짤 수 있을것 처럼 보인다.

그럼 힙영역은 왜 필요한 것일까?


힙 영역은 왜 필요할까?

제일 첫번째 그림을 보면 힙 영역은 프로그래머가 할당한다고 되어있다. 

그럼 언제 할당을 할까? 

배열을 예를들어서 설명을 하겠다.


우리는 배열을 선언할때 상수로 선언을 한다.

int main() {

	// 정상적인 배열선언
	int arr[10];

	// 비 정상적인 배열선언
	int i = 0;
	scanf("%d", &i);
	int arr[i];

	return 0;
}

배열의 길이를 사용자가 입력한 숫자로 잡아주는 것은 비 정상적인 배열선언이다. 왜 비 정상적일까?

메모리 구조에 대해서 잘 파악하고 있다면 당연한 이야기다.


제일 첫번째 그림을 다시보자, 스택 영역에 할당될 메모리의 크기는 컴파일 타임(컴파일 하는 동안)에 결정된다고 되어있다.

정상적인 배열 선언의 경우 arr이라는 배열의 크기가 40바이트 라는것을 알 수 있다.

하지만 비 정상적인 배열선언의 경우 i의 크기가 4바이트 라는 것을 알 수 는 있으나, arr이라는 배열의 크기는 알 수 없다.


그렇다면 다음과 같이 배열을 선언할 때는 문제가 없을까?

int main() {
	
	int i = 10;
	int arr[i];

	return 0;
}

i 라는 변수가 10이기 때문에 arr이라는 배열의 크기가 10이라는 것을 알 수 있지 않을까?

결과는 아니다.


컴파일을 하는 동안 i4바이트의 크기라는 것을 알 수는 있으나, 그 값이 10으로 초기화 되었다는 사실은 무시하고 넘어간다. 값이 10으로 초기화 되었다는 사실은 실행되는 동안, 즉 런타임에 결정된다.

그렇기 때문에 컴파일러는 arr의 크기가 40바이트가 된다는 사실을 알 수 없다. 


사용자의 요구에 맞게 메모리를 할당해 주기 위해서는(런타임에 메모리 크기를 결정하고 싶을 때) 메모리 동적 할당을 통해 힙 영역에 메모리를 할당해야 한다.


힙 영역 : 할당해야 할 메모리의 크기를 프로그램이 실행되는 동안 결정해야 하는 경우(런 타임때) 유용하게 사용되는 공간


힙 영역을 사용하기 위해서는 동적할당에 대해서 공부하여야 한다.


2014/06/26 - [Programming/C언어] - [C] malloc, calloc, realloc을 이용한 메모리 동적 할당


  1. 컴돌이 2015.12.09 14:09 신고

    감사합니다!!

  2. Seph 2016.03.26 23:39 신고

    감사합니다 ^^

  3. hamji 2016.04.06 13:58 신고

    프로그램 공부하다 방문하였습니다. 글 잘 읽고 갑니다. ^^

  4. 맹훈 2016.05.01 16:44 신고

    int i=10
    int arr[i]

    이거 오류 발생안하는데요??

    • 11 2016.12.02 11:37 신고

      전역변수로 선언하면 오류 안납니다.

  5. kim 2016.05.27 21:43 신고

    친절한 설명듣고 갑니다~

  6. thanks! 2016.11.10 19:33 신고

    설명 작살나네여.... 고맙습니다

  7. sad 2016.11.24 11:11 신고

    깔끔한 설명감사합니다!

  8. 2017.05.02 23:15

    비밀댓글입니다

  9. 공부하는이 2017.05.02 23:16 신고

    비밀글 남긴거 남긴사람은 어떻게보나요?? ㅜㅜ

    • 공부하는이 2017.05.02 23:24 신고

      메모리가 한번에 잘 이해되는 글이에요
      그림도 그렇고 내용도 그렇고
      머릿속에 확 들어옵니다 ^^
      좋은글 감사합니다

  10. 지나가다가... 2017.05.31 15:49 신고

    뒷북인것 같지만..
    잘못된 정보가 인터넷에 돌고 있어서 알려드립니다.
    변수로 배열 크기를 지정하는 것은 C99부터 가능한 기능입니다.
    비정상적인 배열 선언이 아니며
    scanf 와 같은 함수로 입력받아 배열 크기 지정하는 것도 가능하다는 뜻이죠.
    어떤 기준으로 비정상적이다 라고 말씀하신지 잘 모르겠으나..
    힙영역에 선언되지 않으면서, 컴파일 타임에 배열의 크기를 알 수 없기 때문에 비정상적이다라고 보신다면... 객관적인 정보는 아닌것 같구요,
    위와 같이 가변 할당을 해주어도 사용자의 요구에 맞게 메모리를 할당 해줍니다.
    동적할당과 같이 크기를 하드코딩 하지 않으면서도 sizeof로 할당된 사이즈도 구할 수 있고, (동적할당한 포인터는 전체 사이즈를 구할 수 없기에 따로 관리해야 하죠)
    C에서 사용하는 경우 스택영역이니 메모리릭 신경쓰지 않고 사용할 수 있기 때문에 동적할당 보다 한정된 스코프에서 사용하기에는 용이하다고 봅니다.
    아래 위키 백과에서 가변 길이 배열 관련 내용을 읽어보시기 바랍니다.
    https://en.wikipedia.org/wiki/Variable-length_array

    • 흐미야 2017.08.09 17:32 신고

      지나가다가... 님이 좋은 내용 써주셨네요.
      저도 C는 오래했다고 생각했는데 새로운 내용이네요.
      말씀하신대로면 Heap 영역이 아니라 Stack 영역에 메모리가 잡히고 Life time도 지역 변수랑 동일하겠네요. 그래서 말씀하신대로 Leak에 대해서도 자유로워질 수 있겠네요. 전제 조건은 함수 호출 이전에 명시적으로 기술해야 한다고 했으니, main 함수에서는 못쓰게 되는 거겠군요. 좋은 지적 내용이십니다.
      C가 점점 최신 언어의 장점을 받아들이는 건가요? C만 해온 사람 입장으로서는 발전하는 것 같아서 좋아요.

  11. 장동규 2017.06.15 14:39 신고

    이해되었습니다! 감사합니다!

  12. 아주 지리는 구먼 2017.06.16 18:39 신고

    굿잡

  13. 아주 지리는 구먼 2017.06.16 18:54 신고

    전역변수로 하면 선언 된다고 하면(int a= 5; int b[a];)

    데이터영역(전역,static)은 동적할당 처럼 선언 할 수 있다는 건가?? 근데 결국 동적할당 처럼 사용은 못하겠지만... 선언만 야매로 쓸수있게되네?

    지역변수로 스택영역에서 사용할라면 빌드 자체가 안되는거고.. 런타임 단계에서 스택영역 애들이 아직 할당이안되서 메모리에....맞음?

    결국 그면 런타임할때 이미 데이터 영역(전역, static)들은 모두 할당이 끝난거? 라고 생각하면 됨?

    그래서 되는거고? 하지만 진정한 의미의 동적할당용 변수로는 사용못하고..(구동중에 변경 불가)

    맞음?

  14. 아주 지리는 구먼 2017.06.16 18:56 신고

    근데 위에 지나가다가.. 개소리는뭐임?

컴퓨터의 모니터 화면을 캡쳐하여 출력


CDC memDC;
CBitmap bitmap;
	
int cx, cy;	// 화면의 크기를 구하기 위해서
// 주 모니터 화면
cx = GetSystemMetrics(SM_CXSCREEN);
cy = GetSystemMetrics(SM_CYSCREEN);
// 듀얼 모니터 사용시 전체화면
//cx = GetSystemMetrics(SM_CXVIRTUALSCREEN);
//cx = GetSystemMetrics(SM_CYVIRTUALSCREEN);

CClientDC dc(this);		// dc선언해주고 (this)에 화면을 출력하기 위해선언
CClientDC ScreenDC(GetDesktopWindow());	// 스크린DC 선언
memDC.CreateCompatibleDC(&ScreenDC);	// 스크린DC와 호환되는 DC생성
bitmap.CreateCompatibleBitmap(&ScreenDC, cx, cy);	// 스크린DC와 호환되는 비트맵 생성
CBitmap* pOldBitmap = memDC.SelectObject(&bitmap);	// Bitmap 포인터를 넘겨줌
memDC.StretchBlt(0, 0, cx, cy, &ScreenDC, 0, 0, cx, cy, SRCCOPY);	// 스크린DC에 저장된화면을 memDC에 copy, bitmap에도 기록됨

// 출력
dc.BitBlt(0, 0, cx, cy, &memDC, 0, 0, SRCCOPY);	// 0,0 부터 해상도크기까지 memDC가 가리키는 참조값 0, 0부터 복사해서 출력함
	
memDC.SelectObject(pOldBitmap);	// 본래의 비트맵 복구??


위 소스를 이용하여 컴퓨터 화면을 캡쳐할 수 있다.

화면을 동영상처럼 캡쳐를 원할경우 Timer를 사용해서 1초에 약 30번 주기로 캡쳐를 하면 된다.


주석이 많기때문에 설명은 생략, 에뮬로 실행한 스마트폰 화면을 찍고싶어서 만들어봤다.

스마트폰 화면을 캡쳐해서 영상처리를 해봐야겠다.


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

[MFC] 컴퓨터 화면 캡쳐해서 출력하기  (0) 2014.06.11

안녕하세요 오랜만에 포스팅을 하네요

개인적인 사정(면접, 과제)로 정신없이 지내다 보니 조금은 소홀해졌네요 ^^..

댓글로 질문하신 분도 꽤 있으신데 제가 관련 내용을 업데이트 못 해서 죄송합니다 ^^.. 빠른 시일 내에 하도록 하겠습니다!



Genymotion 설치 방법


안드로이드 프로그래밍을 하다 보면 스마트폰에 예제 어플들이 수도 없이 깔려서 거슬린 적이 많을 것이다. 하지만 느린 에뮬레이터를 쓸 바에는 불편함을 감수하고 스마트폰을 사용하는 분들이 많을 것이라고 생각된다. 하지만 이 에뮬레이터는 빠르다.

Genymotion 설치법에 대해서 알아보자!


1. 회원가입 및 다운로드

http://www.genymotion.com 에 들어가서 회원가입을 한다. GET GENYMOTION 클릭!



유료 버전이 기능이 더 많지만 일단은 Free 버전을 써보도록 하자! DOWNLOAD 클릭!



DOWNLOAD를 클릭하면 로그인 하라는 창이 뜬다. 아직 회원가입을 안했다면 회원가입을 한다. 계정이 있다면 로그인!



Enterprise size나 Usage type은 적당히 선택하도록 한다.


가입을 완료하면 방금 적었던 이메일 주소로 인증메일이 온다. 인증메일에서 확인을 하면 바로 실행프로그램을 다운받을수 있는 창이 뜬다. (https://cloud.genymotion.com/page/launchpad/download)



자신의 컴퓨터 사양에 맞는 버전을 다운받는다.



2. 설치

다운받은 설치 프로그램을 실행하여 설치를 하도록 하자




설치를 마치고 지니모션을 실행시킨다.  

VirtualBox도 설치가 될텐데 따로 설정할 부분은 없다.

(VirtualBox는 가상운영체제로 여기서 지니모션이 실행된다.)


3. 설정

사용할 에뮬레이터의 환경설정을 한다. 지니모션 첫 실행시 다음과 같은 화면이 뜬다.

새로운 디바이스를 만들도록하자.



Yes를 누르면 로그인을 하라는 화면이 뜰 것이고 로그인을 하면 다음과 같은 화면이 뜬다.

원하는 디바이스 환경을 찾아서 Next를 누르면 설치가 진행이 된다.



설치완료후 안드로이드 에뮬레이터로 사용하기 위해 세팅을 해준다.

Settings 메뉴에서 ADB메뉴에서 Android SDK 폴더의 경로를 지정해준다.




세팅완료후 Play버튼을 눌러 실행을 한다.



4. 에러해결

실행 했을때 다음과 같은 화면이 뜨는 경우가 있을것이다. 

MS 오피스가 설치된 PC에서 대부분 발생하는 문제라고 한다.

이 문제는 키보드 입력기를 "Microsoft 입력기"로 설정하면 된다.







위 그림과 같이 설정을 변경해주면 'Microsoft 입력기'키보드가 추가된다.

선택메뉴에서 'Microsoft 입력기'로 꼭 선택해주자

확인을 누르고 다시 지니모션을 실행해보자.


이제 정상적으로 실행이 된다! 다른 문제가 있다면 현재(2014.05.22)날짜 기준으로 VirtualBox를 최신버전으로 업데이트 하지 않았을 경우 또 다른 에러가 발생한다. 


설치나 설정을 모두다 완료했는데 안된다면 재부팅을 추천한다.







지금까지 블루투스를 활성화 하고 기기 검색을 하는 단계까지 진행하였다.


이전 글 : 블루투스 활성화 글 보기블루투스 기기 검색하기 글 보기


저번 프로젝트에 이어 블루투스 연결하는 단계를 포스팅 하겠다.


검색한 기기에 연결하기 위해서는 ConnectThread와 ConnectedThread에 대해서 알고 있어야 한다.

BluetoothService.java 파일에 ConnectThread와 ConnectedThread 클래스를 내부 클래스로 삽입해준다.


1. ConnectThread


BluetoothService.java

public class BluetoothService {
	... // 이전 부분 생략

	// RFCOMM Protocol
	private static final UUID MY_UUID = 
		UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
	
	private BluetoothAdapter btAdapter;
	
	private ConnectThread mConnectThread;
	private ConnectedThread mConnectedThread;

	... // 이전 부분 생략
		
	private class ConnectThread extends Thread {
		private final BluetoothSocket mmSocket;
		private final BluetoothDevice mmDevice;

		public ConnectThread(BluetoothDevice device) {
			mmDevice = device;
			BluetoothSocket tmp = null;
			
			// 디바이스 정보를 얻어서 BluetoothSocket 생성
			try {
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                Log.e(TAG, "create() failed", e);
            }
			mmSocket = tmp;
		}

		public void run() {
			Log.i(TAG, "BEGIN mConnectThread");
			setName("ConnectThread");
			
			// 연결을 시도하기 전에는 항상 기기 검색을 중지한다.
			// 기기 검색이 계속되면 연결속도가 느려지기 때문이다.
			btAdapter.cancelDiscovery();

			// BluetoothSocket 연결 시도
			try {
				// BluetoothSocket 연결 시도에 대한 return 값은 succes 또는 exception이다.
				mmSocket.connect();
				Log.d(TAG, "Connect Success");
				
			} catch (IOException e) {
				connectionFailed();		// 연결 실패시 불러오는 메소드
				Log.d(TAG, "Connect Fail");
				
				// socket을 닫는다.
				try {
					mmSocket.close();
				} catch (IOException e2) {
					Log.e(TAG,
							"unable to close() socket during connection failure",
							e2);
				}
				// 연결중? 혹은 연결 대기상태인 메소드를 호출한다.
				BluetoothService.this.start();
				return;
			}

			// ConnectThread 클래스를 reset한다.
			synchronized (BluetoothService.this) {
				mConnectThread = null;
			}

			// ConnectThread를 시작한다.
			connected(mmSocket, mmDevice);
		}

		public void cancel() {
			try {
				mmSocket.close();
			} catch (IOException e) {
				Log.e(TAG, "close() of connect socket failed", e);
			}
		}
	}
}


여기서 UUID라는 변수가 있는데 이것은 Bluetooth 통신 프로토콜이다. 블루투스 통신을 할 때 그에 맞는 프로토콜로 통신을 해 주어야 한다. 간단한 예로 블루투스 기능이 지원되는 이어폰과의 통신을 원한다면 그에 맞는 프로토콜을 사용하여야 하고, 스마트폰 끼리의 블루투스 통신은 또 그에 맞는 프로토콜을 사용해야 한다. 

지금 위에서 사용한 프로토콜은 아마 스마트폰끼리의 블루투스 통신을 하는데 사용하는 프로토콜일 것이다.


블루투스 프로토콜 종류 보기 - [안드로이드] 블루투스 프로토콜 UUID 리스트


2. ConnectedThread


위에서(ConnectThread) 다음단계가 ConnectedThread이다. 위 단계에서는 연결을 하는 중간단계 까지라고 생각하고, 이번 단계는 연결을 완료하는 단계라고 생각하면 될 것 같다. 위와 동일하게 BluetoothService 클래스에 내부 클래스로 정의한다.


BluetoothService.java

public class BluetoothService {
	... // 이 전 부분 생략
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;

	private ConnectThread mConnectThread; // 변수명 다시
	private ConnectedThread mConnectedThread; // 변수명 다시
	
	... // 이 전 부분 생략
	
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            Log.d(TAG, "create ConnectedThread");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // BluetoothSocket의 inputstream 과 outputstream을 얻는다.
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes;

            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    // InputStream으로부터 값을 받는 읽는 부분(값을 받는다)
                    bytes = mmInStream.read(buffer);

                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    break;
                }
            }
        }

        /**
         * Write to the connected OutStream.
         * @param buffer  The bytes to write
         */
        public void write(byte[] buffer) {
            try {
            	// 값을 쓰는 부분(값을 보낸다)
                mmOutStream.write(buffer);

            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed", e);
            }
        }
    }
}



3. 마무리 작업


지금까지 잘 따라했다면 몇 가지 빨간줄이 있을 것이다. 이 빨간줄을 지워보자

기본 베이스는 처음에 말했듯이 안드로이드 기본 예제인 BluetoothChat이다.

BluetoothChat에서 다음과 같은 메소드를 모두 복사해서 BluetoothService.java에 붙여넣어 빨간줄을 없애보자


BluetoothService.java

public class BluetoothService {
	... // 이 전과 동일
	private int mState;

	// 상태를 나타내는 상태 변수
	private static final int STATE_NONE = 0; // we're doing nothing
	private static final int STATE_LISTEN = 1; // now listening for incoming connections
	private static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
	private static final int STATE_CONNECTED = 3; // now connected to a remote device

	... // 이 전과 동일

	// Bluetooth 상태 set
	private synchronized void setState(int state) {
		Log.d(TAG, "setState() " + mState + " -> " + state);
		mState = state;
	}

	// Bluetooth 상태 get
	public synchronized int getState() {
		return mState;
	}

	public synchronized void start() {
		Log.d(TAG, "start");

		// Cancel any thread attempting to make a connection
		if (mConnectThread == null) {

		} else {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}
	}

	// ConnectThread 초기화 device의 모든 연결 제거
	public synchronized void connect(BluetoothDevice device) {
		Log.d(TAG, "connect to: " + device);

		// Cancel any thread attempting to make a connection
		if (mState == STATE_CONNECTING) {
			if (mConnectThread == null) {

			} else {
				mConnectThread.cancel();
				mConnectThread = null;
			}
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		// Start the thread to connect with the given device
		mConnectThread = new ConnectThread(device);

		mConnectThread.start();
		setState(STATE_CONNECTING);
	}

	// ConnectedThread 초기화
	public synchronized void connected(BluetoothSocket socket,
			BluetoothDevice device) {
		Log.d(TAG, "connected");

		// Cancel the thread that completed the connection
		if (mConnectThread == null) {

		} else {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		// Cancel any thread currently running a connection
		if (mConnectedThread == null) {

		} else {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		// Start the thread to manage the connection and perform transmissions
		mConnectedThread = new ConnectedThread(socket);
		mConnectedThread.start();

		setState(STATE_CONNECTED);
	}

	// 모든 thread stop
	public synchronized void stop() {
		Log.d(TAG, "stop");

		if (mConnectThread != null) {
			mConnectThread.cancel();
			mConnectThread = null;
		}

		if (mConnectedThread != null) {
			mConnectedThread.cancel();
			mConnectedThread = null;
		}

		setState(STATE_NONE);
	}

	// 값을 쓰는 부분(보내는 부분)
	public void write(byte[] out) { // Create temporary object
		ConnectedThread r; // Synchronize a copy of the ConnectedThread
		synchronized (this) {
			if (mState != STATE_CONNECTED)
				return;
			r = mConnectedThread;
		} // Perform the write unsynchronized r.write(out); }
	}

	// 연결 실패했을때
	private void connectionFailed() {
		setState(STATE_LISTEN);
	}

	// 연결을 잃었을 때 
	private void connectionLost() {
		setState(STATE_LISTEN);

	}

	private class ConnectThread extends Thread {
		... 
	}

	private class ConnectedThread extends Thread {
		...
	}
}


EX_Bluetooth.zip



마지막에 설명을 쫌 대강대강 하고 넘어간 부분이 많은데, 일단은 현재까지의 프로젝트를 압축파일로 올리겠다.

이후 시간이 된다면 스마트폰 끼리의 블루투스 예제를 주석을 달아서 올리도록 하겠다. 

또한, 글쓴이는 임베디드 보드(동글)과 통신을 하였는데 이 부분에 대해서 궁금하신 분이 많다면 서둘러 예제 프로젝트를 올리도록 하겠다.

  1. 이전 댓글 더보기
  2. 함동균 2014.08.27 17:32 신고

    정말 감사합니다. 기존의 블루투스 채팅 앱에서 채팅 기능 을 빼고 블루투스 기능만 제 앱에 탑재 할려 고 하는데 블루투스에 대한 이해가 낮아서 고심하던 찰나에 이런 포스팅을 해주셔서 감사합니다. ㅋ 저는 한국해양대학교 조선과 학생입니다. 자주 방문하도록 하겠습니다.

  3. 이태경 2014.09.23 12:14 신고

    포스팅 잘 보았습니다.
    다름이 아니고, 이번에 블루투스 관련해서 프로젝트를 하는데,
    폰과 스피커를 블루투스 연결해서 전화가 왔을 때, 그 소리를 스피커로 전달하려고 하거든요
    그래서 말씀하신대로 UUID를 HSP UUID로 바꿔서 연결해봤더니, 일단 연결되었다는 의미로
    스피커에서 뚜.뚜 하는 소리는 나는데 실제로 전화가 왔을 때, 그게 스피커로 전달은 안 되더군요
    문제가 뭘까요?

    • 영상털이범 2014.09.23 13:17 신고

      스피커를 연결했을때 들리는 소리는
      미디어 사운드가 들리고
      전화가 왔을때는 다른 채널?의 사운드기 때문에 안들리는게 아닐까요?
      핸드폰의 소리 옵션에도 미디어 사운드, 통화음, 시스템사운드 등등으로 나눠져 있어서
      그 문제가 아닐까 싶습니다.. 아니면 연결이 잘 안되었거나?
      자세한건 저도 다시 알아보고 답변 달겠습니다.

  4. 이태경 2014.09.24 10:48 신고

    먼저 답변 감사합니다~!
    현재 저희 상황 다시 한번 상세히 설명 좀 드릴게요^^
    먼저 폰의 기본 블루투스 연결(환경설정에서의~)로 스피커를 연결하면, 물론 음악과 전화음 모두 스피커로 전달이 됩니다.
    헌데 올려주신 앱 통해서 UUID를 HFP, HSP로 바꾼 후 블루투스 연결을 해 보면, 스피커에서 뚜,,뚜 소리와 함께
    연결되었을 때의 소리는 나거든요(이건 HSP UUID로 설정했을 경우입니다.). 연결이 되었으니 당연히 핸드폰으로 전화가 왔을 경우, 스피커에서 소리가 나올 줄 알았는데 그게 아니더군요...아니면 HSP, HFP가 동시에 2개 다 연결이 되야하는 걸까요? 아니면 말씀해 주신대로, 소리 Source 옵션을 바꿔줘야 하는 걸까요? 가능성이 여러가지라서 어떤 방식으로 풀어야 할지...ㅎㅎ

    • 영상털이범 2014.10.01 15:46 신고

      저도 직접 해본게 아니라서 뭐라고 말씀을 드리지는 못하겠네요 ^^...
      여러가지 가능성을 열어두고
      귀찬겠지만 일일이 테스트해보시는게 제일 도움이 될 듯 싶습니다.

  5. 천태평 2014.10.20 09:00 신고

    블루투스 사용에 도움이 되었네요 ~~ UUID만 바꾸면 블루투스 스피커와 연결이 가능할까요?

  6. 2014.10.22 10:23

    비밀댓글입니다

  7. 김현우 2014.11.27 17:19 신고

    안드로이드 블루투스 통신 공부중인데요 이해가 잘 되게 정리가 되있네요!
    좋은 자료 감사합니다.
    한가지 궁금한 것이 있는데요, 현재 구현된 코드로 보면 MainActivity 내부에 BluetoothService 객체가 정의되어있잖아요? 그렇게되면 여기 MainActivity를 통해 인텐트를 통해 다른 액티비티를 불러냈을 때 더이상 블루투스 통신을 할 수 없는 것인가요?
    만약 그렇다면 서비스를 구현을 해야하는 것인지 개념이 안잡혀서 이렇게 물어봅니다!

  8. 박상균 2014.12.09 10:46 신고

    좋은글 잘 봤습니다. 설명을 정말 잘해놓으셨는데 임베디드 동글보드 연동예제도 볼수 있을까요??저도 구현중인데
    소스를 보고 하면 더 이해가 빠를꺼같아서요 ㅎㅎ

  9. futurejo2 2015.03.29 16:31 신고

    감사합니다. 많은 도움이 됫습니다.

  10. jsj 2015.04.11 00:49 신고

    안드로이드간 블루투스 통신을 하려고 하는데 포스팅한 것을 보고 그대로 했는데도
    어플이 맨처음 main.xml에 뜨는 연결 버튼을 누르면 프로그램이 죽어버립니다...
    BluetoothService.java에 enableBluetooth() 메소드 else문에서 인텐트 객체 생성하는데까지만 동작하는것같은데
    다음은 왜 안되는걸까요 ㅠㅠ

  11. magi 2015.04.20 17:18 신고

    정상적으로 실행되는 예제에다 친절한 주석까지! 깔끔하게 정리된 포스팅에 눈 호강하고 갑니다! 건승하세요 ^^

  12. yha 2015.04.23 17:22 신고

    블루투스에 대해 처음 공부중인데 많은 도움이 되었습니다. 감사합니다!

  13. hoha 2015.05.23 10:34 신고

    정말 많은 도움 받고 있습니다!!
    그런데 블루투스가 켜지는 것까지는 됬는데 블루투스가 켜진 후에는 앱이 중지된다고 하면서 종료됩니다.
    무슨 문제인지 조언해주시면 정말 감사하겠습니다ㅠㅠ

    • 거북이 2015.11.23 21:59 신고

      저도 그런상황이군요.
      현재 안드로이드 스튜디오 사용중이고
      안드 버전은 4.4로 맞춰서 사용중인데... ㄷㄷ

    • 2015.11.29 07:38 신고

      매니페스트에 블루투스 선언하셔야대용ㅋㅋ

    • bhy 2016.10.24 19:37 신고

      저도 같은 상황에서 문제가 발생햇는데 혹시 해결하셧나요

  14. hoha 2015.05.23 11:05 신고

    그리고 제가 지금 만들려고 하는 것이 pc와 앱을 블루투스로 연결하여 앱을 이용해 pc를 제어하도록 하는 것을 하는데요, 위에 포스팅하신 내용이 서버쪽도 완성이 되어있어야 블투루스 검색 리스트가 실행될 수 있는 것인지,
    제 작업에 대한 조언 부탁드려요ㅠㅠ

  15. Thanks for you 2015.05.30 19:37 신고

    안녕하세요. 블루투스 왕초보입니다.
    글 써주신 것을 완성하였을때는, 어떤 결과화면을 볼 수 있는건가요?

  16. youngman 2015.12.10 21:24 신고

    임베디드 보드와의 통신예제 혹시 올려주실수 있나요?ㅜ

  17. 양재의 2016.02.17 08:45 신고

    안녕하십니까^^ 지인의 추천으로 이 곳을 알게 된 안드로이드 초보자입니다만, 블루투스에 대해서 학습 중이던 차에 큰 도움을 받게 되어서
    감사드립니다. 아주 이해하기 쉽게 글을 써놓으셔서 다른 강좌도 공부를 할 예정입니다. 그리고 앞의 글을 보니 임베디드 보드와의 통신에 대해서 예제 프로젝트를 올리겠다고 하셨던데 보이지가 않더군요^^ 강좌를 게시한 시기가 많이 지난 것 같은데 지금이라도 혹시 게시가 가능할까요?
    간곡히 부탁 좀 드리겠습니다.^^; 수고하십시오.

  18. bhy 2016.10.24 20:02 신고

    안녕하세요~ 너무 잘봤습니다. 한가지 궁금한점이 있어서 글을 올립니다~블루투스 활성화 까지는 되지만검색하려고할때 앱이 중지되는데 문제점을 모르겟습니다 ㅠㅠ 가르쳐주세요 ㅠㅠ

  19. ㅁㄴㅇ 2016.11.10 22:45 신고

    @Override
    public void onClick(View v) {
    if(btService.getDeviceState()) {
    // 블루투스가 지원 가능한 기기일 때
    btService.enableBluetooth();
    } else {
    finish();
    }
    }

    에서 @override가 에러나는데 어떻게 해야 고쳐질까요??ㅠㅜ

  20. 상산조자룡 2018.08.01 12:26 신고

    좋은 자료 감사합니다.
    글 작성일이 한참 되서 반영이 안된 내용이 있는데

    마시멜로 버전 올라오면서부터 블루투스 스캔 사용하려면

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

    이 두개 권한이 더 필요하게 됬습니다 블루투스 쓰는데 로케이션권한이 왜 필요한건지.. 암튼 매니페스토 추가하면 되겠습니다..
    여기서 하나더. DeviceListActivity 사용시 이상하게 location 권한 요청을 안합니다..
    그래서 onCreate에서

    int MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION = 1;

    ActivityCompat.requestPermissions(this,

    new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},

    MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);

    강제 권한 요청 해주시면 스캔 잘 돌아갑니다.
    이거때매 2일동안 머리털 많이 빠졌습니당.. 저같은 고생 안하시길 바라며 글 남깁니다.

    영상털이범님 항상 감사합니다.

  21. 안드드드드 2018.11.13 14:07 신고

    블루투스 리스트가 나오고 클릭했을때 페어링을합니다 그런데 페어링하고난 뒤에 연결이 안되는데.. 왜그런건가요????ㅠㅠ 답글부탁드립니다

자바에서는 스레드를 동기화 하기 위해서 synchronized를 제공한다.

스레드는 synchronized 메소드에 들어가기 위해 lock을 얻고 메소드가 끝이나면 lock을 반환한다. 어떠한 스레드가 lock을 얻어 synchronized 메소드를 사용중이면 다른 메소드는 lock이 없으므로 synchronized에 접근할 수 없고, 다른 스레드가 lock을 반환 할 때까지 기다려야 한다.


쉽게 설명하자면 synchronized 메소드를 화장실에 비교하고 lock을 화장실 열쇠로 비교하겠다. 

화장실(synchronized)에 들어가기 위해서는 열쇠(lock)가 필요한데 열쇠(lock)은 단 1개만 존재하는 것이다. 그래서 어떤 사람이 화장실(synchronized)에 열쇠(lock)를 들고 들어가면 다른 사람은 화장실(synchronized)에 들어갈 열쇠(lock)가 없기 때문에 그 사람이 화장실(synchronized)에서 나올 때 까지 기다려야 한다고 생각하면 된다.


실제로 위와 같은 작업은 JVM에 의해 자동으로 수행되기 때문에 우리는 synchronized 메소드를 이용하기만 하면 된다.


synchronized를 이용하는 방법은 2가지가 있다.

1. synchronized method 방법

2. synchronized block 방법


1. synchronized method

메소드 앞에 synchronized 키워드를 붙여주면 간단하게 사용할 수 있다.


public synchronized 메소드명(파라미터) {
	...
}


2. synchronized block

synchronized 메소드와 기능은 유사하다. 파라미터가 공유할 객체로 들어간다.

synchronized (공유할 객체) {
	...
}


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

[JAVA] synchronized - 스레드 동기화  (0) 2014.04.05

C에는 자연로그를 구하는 log() 함수와, 밑(base)이 10인 상용 로그를 구하는 log10()함수만 있기때문에 밑이 2인 로그를 구하려면 직접 함수를 만들어야 한다. 


logB(숫자, 밑)


이라는 함수를 만들어 사용하도록 하자.

double logB(double x, double base) {
	return log(x)/log(base);
}


매크로 함수로 정의해서 사용해도 잘 된다.

#define logB(x, base) log(x)/log(base)


base에 2.0 대신 다른수를 넣어주면 다양한 밑을 가진 로그를 구할 수 있다.


원리는 다음과 같은 공식을 사용한 것이다.




예제 

#include <stdio.h>
#include <math.h>

//#define logB(x, base) log(x)/log(base)

double logB(double x, double base) {
	return log(x)/log(base);
}

int main() {
	printf("log2(8) = %f", logB(8.0,2.0));

	return 0;
}



#include <math.h> 를 사용하여 수식을 계산할때, 원주율값이 필요 한 경우가 있다.


원주율값을 잘 외우고 있는경우 상관은 없지만, 단순히 3.14라는 값을 사용하여 알고리즘 문제를 풀 때 소수점 뒷자리 문제로 답이 틀리는 경우가 종종 발생할 수 있다. 


그렇다고 원주율을 소수점 10자리 이상 외우고 다닐수는 없다.


이럴경우 math.h에서 원주율 값을 제공해주지 않을까 찾아보지만 M_PI를 사용하면 에러가 발생한다.


M_PI 상수는 C표준이 아니기때문에 기본적으로 사용할 수 없다고 한다.


math.h를 include 하기전에


#define _USE_MATH_DEFINES


를 정의해 주어야 한다.



이제 M_PI를 사용할 수 있다.

  1. cppuser 2018.07.13 14:56 신고

    외우는게 편할때가 있습니다.. 3.14159265358979... 단순히 pi하나만 필요할 때... const float으로 정의해 쓰면 편하더라구요. 어차피 리터럴인것..

큐 소스입니다.



#include <stdio.h>
#include <stdlib.h>

// 큐 크기
#define QSIZE	(5)

// 큐 선언
int queue[QSIZE];
int front, rear;	// front == 0, rear == 0

int is_full() { return rear == QSIZE; }
void put(int data)
{
	queue[rear] = data;
	rear++;
}

int is_empty() { return front == rear; }
int get()
{
	int temp = queue[front];
	front++;
	return temp;
}

// 큐 출력
void display()
{
	int i;

	system("cls"); // 콘솔창 초기화
	printf("\n");

	printf("%*s\n", (rear+1)*4, "rear");
	printf("%*s\n", (rear+1)*4, "  | ");

	for(i=0; i< QSIZE; i++)
		printf("[%2d]", queue[i]);

	printf("\n");
	printf("%*s\n", (front+1)*4, "  | ");
	printf("%*s\n", (front+1)*4, "front");
	getchar();
}
void main()
{
	int i;

	display();
	for(i=0; i<7; i++)
	{
		if(!is_full())
			put(i+1);
		display();
	}

	for(i=0; i<7; i++)
	{
		if(!is_empty())
			get();
		display();
	}
}

'Programming > 자료구조&알고리즘' 카테고리의 다른 글

[자료구조] 기본 Queue 소스  (0) 2014.03.21
[자료구조] 기본 Stack 소스  (0) 2014.03.18

스택 소스 입니다.



#include <stdio.h>
#include <stdlib.h>

// 스택 크기
#define STACK_MAX (5)

// 스택 선언
int arr[STACK_MAX];
int top = -1;

void push(int data) { arr[++top] = data; }

int pop() {	return arr[top--]; }

int is_full() { return top == STACK_MAX-1; }

int is_empty() { return top == -1; }

// 스택 출력
void display() {
	int i;

	system("cls"); // 콘솔창 초기화
	printf("\n");

	for(i = 0; i < STACK_MAX; i++) {
		if(STACK_MAX-1-i > top)
			printf("[     ]");
		else
			printf("[  %2d ]", arr[STACK_MAX-1-i]);

		if(STACK_MAX-1-i == top) printf(" <- TOP");
		printf("\n");
	}
	if(is_full()) printf("STACK IS FULL\n");
	if(is_empty()) printf("STACK IS EMPTY\n");
	getchar();
}

int main() {
	int i;

	display();
	for( i=0; i<10; i++ )
	{
		if(!is_full()) push(i+1);
		display();
	}

	for( i=0; i<10; i++ )
	{
		if(!is_empty())	printf("%d ", pop());
		display();
	}

	return 0;
}

'Programming > 자료구조&알고리즘' 카테고리의 다른 글

[자료구조] 기본 Queue 소스  (0) 2014.03.21
[자료구조] 기본 Stack 소스  (0) 2014.03.18

저번 포스팅에서는 블루투스를 활성화 하는 단계까지 진행하였다. 


이전 글 : 블루투스 활성화 글 보기 


이번에는 저번 프로젝트에 이어 기기 검색하는 부분부터 시작하겠다.


1. 기기 검색하기 - 프로젝트 파일 복사하기


기기 검색하기에 앞서 기기 검색하기 기능 실행시 보여줄 화면을 위해 안드로이드 블루투스 채팅 예제프로젝트에서 몇가지 파일을 가져오도록 하겠다. 번거로울수 있어서 아래 포스팅을 하였다. 접힌 내용을 펼쳐서 보길 바란다.

java 파일인 DeviceListActivity.java

레이아웃 xml 파일인 device_list.xml, device_name.xml

을 복사해서 붙여넣고 

[res]->[values]->[string.xml] 에 string 들을 추가해준다.(복사해서 붙여넣는다.)


.. 추가중


Manifest에 액티비티 추가(DeviceListActivity)를 해주는것도 잊지말자!


AndroidManifest.xml

        <activity android:name="ssm.bluetooth.MainActivity" 
                android:label="@string/app_name">
                    <intent-filter>
                        <action android:name="android.intent.action.MAIN">

                        <category android:name="android.intent.category.LAUNCHER">
                    </category>
                </action>
        </intent-filter>
        </activity>
        <!-- 여기서부터 추가 -->
        <activity android:name="ssm.bluetooth.DeviceListActivity" 
                android:theme="@android:style/Theme.DeviceDefault.Dialog">
        <!-- 여기까지 추가 -->


2. 기기 검색하기 - 코드 작성하기


기기 검색시 띄워줄 액티비티가 완성되었으니 이제 액티비티를 띄워줄 코드를 작성해보자

BluetoothService.java 파일에 scanDevice()라는 메소드를 생성하였다.


BluetoothService.java

public void scanDevice() {
		Log.d(TAG, "Scan Device");
		
		Intent serverIntent = new Intent(mActivity, DeviceListActivity.class);
		mActivity.startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
	}

startActivityForResult가 또 등장하였다!

저번 포스팅에서 봤던 녀석이 아닌가!

새로운 액티비티를 띄워서 처리된 결과값이 mActivity로 반환이 되는 코드이다.(mActivity는 MainActivity)


scanDevice() 메소드에서는 디바이스를 선택하는 액티비티를 띄워주고 선택하였을 경우 그 결과값을 MainActivity로 반환하고 있다. 


그렇다면 scanDevice()라는 기기를 검색을 요청하는 메소드는 어디에 추가를해야 될까?

기기의 블루투스 상태가 On 상태이고, 블루투스 연결 버튼을 눌렀을 경우,

또는 기기의 블루투스 상태가 Off상태 이었을때, 연결 버튼을 눌러 블루투스를 활성화 하였을 경우.

이 두가지 경우에 scanDevice()메소드를 호출하면 될 것이다. 

간단히 정리하자면 블루투스가 활성화 되어있는 상태에서 scanDevice()메소드를 호출하면 된다.




그럼 scanDevice()메소드를 호출하고, 기기를 선택하였을 경우 결과값을 반환 받았을때, MainActivity에서 처리해주는 소스를 작성해 보자.


MainActivity.java

public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult " + resultCode);
        
        switch (requestCode) {
        
        /** 추가된 부분 시작 **/
        case REQUEST_CONNECT_DEVICE:
            // When DeviceListActivity returns with a device to connect
            if (resultCode == Activity.RESULT_OK) {
            	btService.getDeviceInfo(data);
            }
            break;
        /** 추가된 부분 끝 **/
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
            	// Next Step
            	btService.scanDevice();
            } else {

                Log.d(TAG, "Bluetooth is not enabled");
            }
            break;
        }
	}

requestCode가 REQUEST_CONNECT_DEVICE인 경우 getDeviceInfo()메소드를 호출하도록 하였다.

getDeviceInfo()메소드는 아직 구현하지 않았다. 

이 메소드는 기기선택 액티비티에서 선택한 기기의 정보를 받아 getDeviceInfo()라는 메소드로 전달을 해주고 getDeviceInfo()메소드는 그 정보를 이용하여 블루투스연결을 시도할 예정이다.


3. 검색된 기기에 접속하기 - 1


검색된 기기에 접속하기 위해서 getDeviceInfo()메소드를 작성해보자.


BluetoothService.java

	public void getDeviceInfo(Intent data) {
		// Get the device MAC address
		String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
		// Get the BluetoothDevice object
		//BluetoothDevice device = btAdapter.getRemoteDevice(address);
		BluetoothDevice device = btAdapter.getRemoteDevice(address);
		
		Log.d(TAG, "Get Device Info \n" + "address : " + address);

		connect(device);
	}

address라는 String에는 선택한 기기의 주소가 담겨져 있고, 그 주소를 BluetoothDevice로 전달하여 connect()메소드로 전달하면 접속을 할 수 있다. connect()메소드는 아직 구현을 하지 않았다. 이제 connect()메소드만 구현하면 기기의 블루투스 연결은 가능할 것 이다.


4. 검색된 기기에 접속하기 - 2


이제부터는 많은 양의 복사 붙여넣기를 시도할 예정이다.

안드로이드 블루투스 채팅 예제의 BluetoothChatService의 connect()메소드를 붙여넣고, 이와 관련된 모든 소스를 붙여넣는다.

다음 포스팅을 보면서 진행하도록 하자.


다음 글 : 블루투스통신 연결하기 글 보기




  1. 2014.04.05 01:06

    비밀댓글입니다

    • 영상털이범 2014.04.05 03:58 신고

      지금까지는 잘 따라하셨나요? ㅋㅋ
      죄송합니다.. 사실 더 포스팅을 했어야 하는데
      그 뒤로 시간도 안나고 귀차니즘도 있어서 ..
      뒷 부분은 제가 오늘부터 포스팅 할 생각이니까 잘 봐주세요 ㅎㅎ

  2. 2014.04.05 11:36

    비밀댓글입니다

  3. 이정민 2014.05.19 15:22 신고

    android:theme="@android:style/Theme.DeviceDefault.Dialog">
    이 소스가 어떤 의미인가요??

    소스를 복사해서 붙엿는데 이 부분이 빨간줄로 나오더라구요...-_ -;;

    • 영상털이범 2014.06.26 14:04 신고

      새로 추가한 DeviceListActivity의 테마를 변경해 주는 소스입니다.
      그 부분을 제거하였을 경우 디바이스를 선택하는 화면이 전체화면으로 나타나게 됩니다.
      하지만 저 부분을 넣어주면 다이얼로그액티비티로 나타나게 됩니다.
      저 부분에서 에러가 나신다면 액티비티를 메니페스트에서 다이얼로그액티비티로 띄우는 부분을 찾아서 수정하시면 될 듯 합니다!

  4. gyun 2014.05.25 18:22 신고

    안녕하세요. 친절한 포스팅 덕분에 많이 배우고 있습니다~
    근데 블루투스 채팅 예제프로젝트는 어딨는건가요?

    • 영상털이범 2014.06.26 14:05 신고

      채팅예제 프로젝트는 이전 글에보시면
      안드로이드 예제 프로젝트를 생성하는 포스팅이 있습니다(접혀 있으니 펼쳐 보셔야 됩니다.)
      잘 못찾으시겠다면 인터넷 검색으로도 쉽게 구할 수 있습니다.
      아마 BluetoochChat 이라는 프로젝트 이름일 것입니다.

  5. 감사해용 2015.04.30 21:37 신고

    혹시 기기간 블루투스 통신 인가요?????

  6. 감사함당 2015.05.19 17:30 신고

    Manifest에 액티비티 추가(DeviceListActivity) 부분에서

    <activity android:name="ssm.bluetooth.DeviceListActivity" 부분이 빨간색으로 오류가 뜨는데
    이 소스는 무슨 의미인가요 ???

  7. 이동우 2016.03.01 00:12 신고

    기기 검색하기에 앞서 기기 검색하기 기능 실행시 보여줄 화면을 위해 안드로이드 블루투스 채팅 예제프로젝트에서 몇가지 파일을 가져오도록 하겠다. 번거로울수 있어서 아래 포스팅을 하였다. 접힌 내용을 펼쳐서 보길 바란다.
    java 파일인 DeviceListActivity.java
    레이아웃 xml 파일인 device_list.xml, device_name.xml
    을 복사해서 붙여넣고
    [res]->[values]->[string.xml] 에 string 들을 추가해준다.(복사해서 붙여넣는다.)

    이부분에서 접힌 내용을 펼쳐서 보길 바란다라고 되어있는데 접힌 내용이없고 추가중....이라는 말밖에없습니다 ㅠㅠ 어떤것을 복사붙여넣기 하라는 것인지 string들을 추가해준다도 어떤 것을 복사해서 붙여넣으라는 것인지 모르겠습니다

    • cchead 2017.02.19 13:28 신고

      DeviceListActivity.java 는
      예제소스로 공개 되어 구글링 하시면 금방나옵니다.
      bluetoothChat 예제에 다 들어있는듯

  8. String?? 2016.11.23 07:01 신고

    [res]->[values]->[string.xml] 에 string 들을 추가해준다.(복사해서 붙여넣는다.)

    이부분에서 string들을 찾을 수가 없네요... string.xml 파일은 어디서 구할 수 있을까요?

  9. 이레 2017.04.12 14:33 신고

    connect() 에서 막혀서 요녀석이어딨지? 하면서 엄청고민했었는데

    마지막을안읽었었네요 ㅋㅋ

    재밌게 따라하고있습니다 감사합니다

  10. 안드드드드 2018.11.12 16:12 신고

    블루투스 기기 검색시뜨는창을 제가직접 만들고싶은데 어떻게해야하는지좀 알려주세요ㅠㅠ

안드로이드의 블루투스 통신 예제입니다.

안드로이드의 블루투스 레퍼런스를 참고하고 싶으시다면 다음 링크로 들어가시면 됩니다.


레퍼런스 참고 -> 블루투스 API


1. 블루투스 통신 예제


> 예제 사용법 보기(클릭)


이 예제를 활용하여 수정한다면 블루투스 통신을 자유자재로 사용할 수 있다.

이제부터 이 예제를 활용하여 블루투스 통신을 하는 예제를 만들어 보겠다.


2. 새 프로젝트 생성


액티비티에 버튼을 생성하여, 버튼을 눌렀을 경우 블루투스 연결을 할 수 있도록 만들어볼 예정이다.


메인화면 레이아웃은 다음과 같이 버튼과 현재 상황을 알려주는 TextView로 만들었다.

main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/btn_connect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="연결" />


    <TextView
        android:id="@+id/txt_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_connect"
        android:text="" />

</RelativeLayout>



기본 세팅이 끝났으니 이제부터 블루투스 통신에 관련된 코드를 작성해보겠다.


3. 권한 등록


제대로된 코딩을 시작하기 전에 권한등록(permission 등록)을 먼저 하도록 하자

블루투스 기능을 사용하기 위해서는 'BLUETOOTH'와 'BLUETOOTH_ADMIN'의 권한을 등록해줘야 한다.


BLUETOOTH : 커넥션 요구, 커넥션 수락, 데이터 전송 등의 블루투스 통신을 하기 위해 필요한 권한

BLUETOOTH_ADMIN : 디바이스 검색 시작, 블루투스 설정을 조작하기 위해 필요

(BLUETOOTH_ADMIN 권한을 사용하려면 BLUETOOTH 권한도 꼭 있어야 한다.)


Manifest 파일에 블루투스 권한 등록


AndroidManifest.xml

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />


4. 새 Class 파일 생성


MainActivity에는 Button과 TextView만 배치해놓고 BluetoothService라는 Class를 만들도록 한다.

BluetoothService라는 Class에서 블루투스와 관련된 모든 작업을 처리할 예정이다. 

작업이 처리되는 과정은 핸들러를 통해서 MainActivity에 전달되어 상태값을 전달받아 TextView에 보여주거나 버튼의 상태를 변경해 주도록 할 것이다.


BluetoothService 생성자는 메인이 되는 MainActivity로 부터 Activity와 Handler 값을 받는다.


BluetoothService.java

public class BluetoothService {
	// Debugging
	private static final String TAG = "BluetoothService";
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;
	
	// Constructors
	public BluetoothService(Activity ac, Handler h) {
		mActivity = ac;
		mHandler = h;
		
		// BluetoothAdapter 얻기
		btAdapter = BluetoothAdapter.getDefaultAdapter();
	}

MainActivity.java

private BluetoothService btService = null;

private final Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
		}
		
	};

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.main);
		
		// BluetoothService 클래스 생성
		if(btService == null) {
			btService = new BluetoothService(this, mHandler);
		}
	}


5. 블루투스 활성화


블루투스를 활성화 하기 위해 BluetoothAdapter클래스를 사용해서 다음 두 단계의 작업을 순서대로 진행한다.


① 블루투스 지원 확인

② 블루투스 활성화


① 블루투스 지원 확인

블루투스 통신을 사용하기 전에 디바이스가 블루투스를 지원하는지 확인할 필요가 있다. 디바이스가 블루투스를 지원하는지 확인해보도록 하자


BluetoothService.java

public boolean getDeviceState() {
		Log.d(TAG, "Check the Bluetooth support");
		
		if(btAdapter == null) {
			Log.d(TAG, "Bluetooth is not available");
			
			return false;
			
		} else {
			Log.d(TAG, "Bluetooth is available");
			
			return true;
		}
	}

getDeviceState() 라는 메소드를 만들어서 기기의 블루투스 지원여부를 확인 한다.

BluetoothAdapter가 null일 경우 블루투스 통신을 지원하지 않는 기기이다. (하지만 이러한 기기는 요즘 없을듯..?)


② 블루투스 활성화

getDeviceState()가 true를 반환할 경우 블루투스 활성화를 요청하도록 해보자.

블루투스 활성화 요청을 위해서 enableBluetooth()라는 메소드를 만들었다. 


BluetoothService.java

public void enableBluetooth() {
		Log.i(TAG, "Check the enabled Bluetooth");
		
		
		if(btAdapter.isEnabled()) {		
			// 기기의 블루투스 상태가 On인 경우
			Log.d(TAG, "Bluetooth Enable Now");
			
			// Next Step
		} else {		
			// 기기의 블루투스 상태가 Off인 경우
			Log.d(TAG, "Bluetooth Enable Request");
			
			Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			mActivity.startActivityForResult(i, REQUEST_ENABLE_BT);
		}
	}

기기의 블루투스 상태가 On일 경우 다음 단계를 실행하면 되고, 

기기의 블루투스 상태가 Off일 경우 블루투스 활성화를 요청하는 알림창을 띄운다.


알림창에서 확인/취소를 선택할 경우 결과는 MainActivityonActivityResult()메소드로 들어온다. 

그 전에 startActivityForResult의 개념에 대해서 알고 있어야 이해하기 편한데, 이 것은 구글링을 통해서 알아보는것을 추천한다.


알림창의 확인/취소 결과는 MainActivity에 onActivityResult()메소드로 전달되는데 소스를 보면 다음과 같다.


MainActivity.java

public void onActivityResult(int requestCode, int resultCode, Intent data) {
        
        switch (requestCode) {          
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
                // 확인 눌렀을 때
                //Next Step
            } else {
                // 취소 눌렀을 때
                Log.d(TAG, "Bluetooth is not enabled");
            }
            break;
        }
}


이제 MainActivity에서 버튼을 클릭했을 때 블루투스 활성화를 위한 이벤트 처리만 해주면 된다.


MainActivity.java

@Override
	public void onClick(View v) {
		if(btService.getDeviceState()) {
			// 블루투스가 지원 가능한 기기일 때
			btService.enableBluetooth();
		} else {
			finish();
		}
	}

여기까지 블루투스를 활성화 하는 방법에 대해서 알아보았다.

블루투스가 활성화 되었을 때(resultCode가 Activity.RESULT_OK일때) 기기 검색을 통해 페어링을 시켜주면 되는데, 이 부분은 다음장에 포스팅 하도록 하겠다.


전체 소스는 다음과 같고, 프로젝트를 첨부파일로 올린다.


EX_Bluetooth(1).zip



MainActivity.java

public class MainActivity extends Activity implements OnClickListener {
	// Debugging
	private static final String TAG = "Main";
	
	// Intent request code
	private static final int REQUEST_CONNECT_DEVICE = 1;
	private static final int REQUEST_ENABLE_BT = 2;
	
	// Layout
	private Button btn_Connect;
	private TextView txt_Result;
	
	private BluetoothService btService = null;
	
	
	private final Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
		}
		
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.e(TAG, "onCreate");
		
		setContentView(R.layout.main);
		
		/** Main Layout **/
		btn_Connect = (Button) findViewById(R.id.btn_connect);
		txt_Result = (TextView) findViewById(R.id.txt_result);
		
		btn_Connect.setOnClickListener(this);
		
		// BluetoothService 클래스 생성
		if(btService == null) {
			btService = new BluetoothService(this, mHandler);
		}
	}

	@Override
	public void onClick(View v) {
		if(btService.getDeviceState()) {
			// 블루투스가 지원 가능한 기기일 때
			btService.enableBluetooth();
		} else {
			finish();
		}
	}
	
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult " + resultCode);
        
        switch (requestCode) {

        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
            	
            } else {

                Log.d(TAG, "Bluetooth is not enabled");
            }
            break;
        }
	}

}


BluetoothService.java

public class BluetoothService {
	// Debugging
	private static final String TAG = "BluetoothService";
	
	// Intent request code
	private static final int REQUEST_CONNECT_DEVICE = 1;
	private static final int REQUEST_ENABLE_BT = 2;
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;
	
	// Constructors
	public BluetoothService(Activity ac, Handler h) {
		mActivity = ac;
		mHandler = h;
		
		// BluetoothAdapter 얻기
		btAdapter = BluetoothAdapter.getDefaultAdapter();
	}
	
	/**
	 * Check the Bluetooth support
	 * @return boolean
	 */
	public boolean getDeviceState() {
		Log.i(TAG, "Check the Bluetooth support");
		
		if(btAdapter == null) {
			Log.d(TAG, "Bluetooth is not available");
			
			return false;
			
		} else {
			Log.d(TAG, "Bluetooth is available");
			
			return true;
		}
	}
	
	/**
	 * Check the enabled Bluetooth
	 */
	public void enableBluetooth() {
		Log.i(TAG, "Check the enabled Bluetooth");
		
		
		if(btAdapter.isEnabled()) {		
			// 기기의 블루투스 상태가 On인 경우
			Log.d(TAG, "Bluetooth Enable Now");
			
			// Next Step
		} else {		
			// 기기의 블루투스 상태가 Off인 경우
			Log.d(TAG, "Bluetooth Enable Request");
			
			Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			mActivity.startActivityForResult(i, REQUEST_ENABLE_BT);
		}
	}
	
}


다음 글 : 블루투스 기기 검색하기 글 보기



  1. 이전 댓글 더보기
  2. 오류 2014.07.11 15:45 신고

    btn_Connect = (Button) findViewById(R.id.btn_connect);
    txt_Result = (TextView) findViewById(R.id.txt_result);
    여기가 이렇게오류는데 왜 그럴까요 ㅠ,ㅠbtn.connect랑txt_result쪽이 오류가많이나네요
    임포트종류도 많이나와서 어떤걸할지 ㅠ,???

  3. 오류 2014.07.11 15:45 신고

    btn_Connect = (Button) findViewById(R.id.btn_connect);
    txt_Result = (TextView) findViewById(R.id.txt_result);
    여기가 이렇게오류는데 왜 그럴까요 ㅠ,ㅠbtn.connect랑txt_result쪽이 오류가많이나네요
    임포트종류도 많이나와서 어떤걸할지 ㅠ,???

  4. 오류 2014.07.11 15:54 신고

    create field 만들라 그러고 그러는데 어떻게하죵

    • 영상털이범 2014.07.14 08:54 신고

      .. 그 부분은 xml파일이랑 id값을 맞춰주어야 하는 부분인데
      이건 너무 기본적인 부분이라서 제가 직접 설명을 해드리기보다
      책을펴고 기본부터 공부를 하시고 프로젝트를 진행하시는게 맞다고 생각이 듭니다.

  5. 오류 2014.07.14 13:57 신고

    답변 감사드려요 이번에 공부좀 많이 하고 싶어서 그런데 책 추천좀 해주실수 있을까요???

    • 영상털이범 2014.07.14 14:47 신고

      시중에 안드로이드 책이 많이 있기때문에 서점에가서 한번씩 보시고 쉽게 재밌게 나온 책으로 공부하시는 것도 추천하고
      가장 기본적인 책으로는 '안드로이드 프로그래밍 1,2' 를 보시는 것을 추천해 드립니다. 두껍고 총 2권으로 되어있는데 왠만한 내용은 다 들어있기때문에 처음부터 책을 본다는 생각말고 중간에 필요한 내용을 찾아보는 사전처럼 사용하시면 좋을것 같습니다.

  6. 2014.07.31 20:27

    비밀댓글입니다

    • 영상털이범 2014.08.03 19:12 신고

      메일주소는
      dsnight@nate.com 입니다.
      제가 업무상 여유가 될 때 확인하고 도움드리도록 하겠습니다.

  7. 꿈나무 2014.08.01 15:45 신고

    블루투스 플젝을 처음하는데 포스팅이 완전탄탄해서 많은 도움이됐습니다!!

    다름이아니라 블루투스 on/off시에 enableBluetooth()메소드에서 처리를하는데

    이메소드에서 블루투스를 on을했을때 다른기기가 검색할수있는 방법을 찾고있습니다

    현재 BluetoothChat플젝에있는 ensureDiscoverable() 메소드를 참조하고있는데

    블루투스 on/off를 선택하는창 뒤에 미리 떠있더라구요 on/off선택후 기기검색허용 창이 뜨게하고싶은데

    방법이없을까요? 답변부탁드립니다!

    언제나 감사합니다^^

    • 영상털이범 2014.08.03 19:13 신고

      onActivityResult 메소드를 알아보시면
      블루투스 on/off 에서 확인을 클릭했을 경우
      받아오는 이벤트가 있습니다.
      그 부분을 찾아서 on(확인)을 클릭했을 경우
      그 다음동작에 기기검색 메소드를 실행하면 될 것 같습니다.

  8. 꿈나무 2014.08.06 16:53 신고

    지난번 답변 감사했습니다!

    이번엔 Fragment를 이용해서 블루투스를 만드려고하는데

    FragmentActivity에서 다른 Fragment로 이동후 그화면에서

    Fragment-enableBluetooth()메소드를 이용해서 기기활성화는 되는데

    scanDevice()메소드에서 Intent가 말썽입니다.

    Intent() 안에 Activity만 들어가지는지 빨간줄이 없어지질 않아서

    DeviceListActivity를 Fragment클래스로 부르는법 질문드립니다..ㅠ

    여름인데 건강 조심하세요! 감사합니다.

    • 영상털이범 2014.08.06 21:12 신고

      Activity를 상속받은 클래스에서 Intent가 가능할 것 입니다.
      Fragment 클래스로 Context를 넘겨주던지
      핸들러를 사용하여 처리하면 될 것? 같습니다. 지금 떠오르는 생각으로는..
      저 방법이 안되고 정 안되신다면 다시한번 댓글 주시면 적극적으로 해결해 드리겠습니다!

  9. 꿈나무 2014.08.08 10:27 신고

    잘해결했습니다ㅋ바보같이
    <activity
    android:name=".DeviceListActivity"
    android:theme="@android:style/Theme.DeviceDefault.Dialog"/>
    이구문을 빼놓고 구동시키니까 오류가 났었네요..

    다이어로그창이 따로 안뜨고 Activity안에서 검색레이아웃이 뜨게 만드는법 헤딩해보려고 합니다ㅎㅎ

    답글 감사합니다!^^

    • 영상털이범 2014.08.08 16:30 신고

      그 부분은
      android:theme="@android:style/Theme.DeviceDefault.Dialog"
      부분만 지워주시면 다이어로그창이 따로 안뜨고
      하나의 액티비티로 뜨게 됩니다.
      그냥 DeviceListActivity를 통쨰로 옴겨오던지 수정하시면 금방 끝날거 같습니다.

  10. wonmi 2014.08.20 15:42 신고

    이클립스 샘플파일에서 BlueToothChat이 안열립니다........finish버튼이 활성화가 안되요
    (A project with that name already exists in the workspace)라고 메세지가 뜨네요
    도와주세요 ㅠㅠ

    • 영상털이범 2014.08.20 17:23 신고

      이클립스에 같은 이름의 프로젝트가 존재해서 그렇습니다.
      BlueToothChat 이라는 같은 이름의 프로젝트가 존재하기 때문에 안열리는듯 합니다.
      원래 있던 프로젝트의 이름을 변경후에 시도해주세요.

  11. 꿈나무 2014.08.28 16:23 신고

    여름 마지막 더위에 잘지내시는지 모르겠네요ㅋ
    요즘 비콘으로 연동하는법을 찾고있는데 온갖단어로 구글링을 해도 안나오네요..ㅠㅠ
    비콘연동 해보셨는지 궁금합니다!!
    항상 감사합니다!

  12. 사과는금 2014.11.09 22:47 신고

    안드로이드 BluetoothChat 예제 를 해서
    run 해봐도 블루투스 이용 가능 하지 않다고 뜨고
    님이 올려주신 걸로 실행해보아도 이용 가능 하지 않다고 뜨고
    제가 소스보고 따라해봐도 그대론데 어떻게 해야 되나요?ㅠㅠㅠ

  13. 앱을 만들기 시간한 강군 2015.02.25 19:17 신고

    안녕하세요 안드로이드 개발자 강군입니다.
    제가 안드로이드스튜디오로 앱을 개발 중입니다.

    // BluetoothService 클래스 생성
    if(btService == null) {
    btService = new BluetoothService(this, mHandler);
    }
    이부분에 btService = new BluetoothService(this, mHandler); 에러가 발행합니다.
    에러 발생 이유를 보르겠습니다. 블루투스서비스소켓에 대한 안내글이 나오는데 이해가 잘 안되네요.

    추가로 질문이 하나 더있는데
    안드로이드 4.3미만의 버전에서는 블루투스를 이용한 통신을 하려고 합니다.
    4.3이상의 버전과 API가 다르다고 제가 들은거 같은데 위에 작성된 앱은 어떤 버전에서 사용되는 것인가요?
    Bluetooth 4.0 버전, 4.0미만 버전에 대한 통신을 하나의 앱에서 하려고 합니다.
    어떻게 작성해야 할까요?

  14. 2016.04.15 14:15 신고

    포스팅 잘봤습니다~
    따라하고 있는데,
    REQUEST_ENABLE_BT가 빨간 글씨로 뜨는데
    어떻게 해결할 수 있을까요?

  15. 2017.02.13 16:14

    비밀댓글입니다

  16. 끼룩 2017.02.13 17:00 신고

    감사합니다!!!

  17. what 2017.02.23 09:54 신고

    Buetooth 안드로이드 간에 채팅 소스 혹시 올려주실수 있나요?ㅠㅠ

  18. question 2017.03.06 11:13 신고

    이거 연결이 되면 무슨 알람같은게 뜨나요??? 연결 하기 해서 장치 선택했는데 아무 알람이 안떠서.. 된건지 안된건지..

  19. 하정 2017.03.20 00:12 신고

    Error:(21, 28) No resource found that matches the given name (at 'paddingBottom' with value '@dimen/activity_vertical_margin').
    Error:(22, 26) No resource found that matches the given name (at 'paddingLeft' with value '@dimen/activity_horizontal_margin').
    Error:(23, 27) No resource found that matches the given name (at 'paddingRight' with value '@dimen/activity_horizontal_margin').
    Error:(24, 25) No resource found that matches the given name (at 'paddingTop' with value '@dimen/activity_vertical_margin').
    C:\Users\Moi13\AndroidStudioProjects\BluetoothChat2\Application\build\intermediates\res\merged\debug\menu\main.xml
    Error:(21, 28) No resource found that matches the given name (at 'paddingBottom' with value '@dimen/activity_vertical_margin').
    Error:(22, 26) No resource found that matches the given name (at 'paddingLeft' with value '@dimen/activity_horizontal_margin').
    Error:(23, 27) No resource found that matches the given name (at 'paddingRight' with value '@dimen/activity_horizontal_margin').
    Error:(24, 25) No resource found that matches the given name (at 'paddingTop' with value '@dimen/activity_vertical_margin').


    안드로이드 스튜디오의 위 에러는 대체 어떻게 해결해야 할까요 ㅠㅠㅠ

  20. 촠봌 2018.05.26 23:43 신고

    감사합니다.
    덕분에 안되는 부분 해결했어요!!!!!!!!!!!

  21. 화랑 2018.08.06 15:06 신고

    WARNING: Configuration 'compile' is obsolete and has been replaced with 'implementation' and 'api'.

    샘플 프로젝트를 임포트하는 과정에서 위와 같은 에러 일때 전부 implementation으로 바꿔 주었는데 HC-06모듈과 연결이 안되는데 따로 더 수정해야하는것이 있을까요/????

 안드로이드 프로그래밍에서 어플만의 UI를 갖는것은 필수라고 생각한다.

 앱의 기능은 구글링을 통해서 어느정도 구현을 할 수가 있는데, 똑같은 기능을 갖고 있는 앱 중에서 UI가 이쁜 앱과 UI가 평범한 앱중에는 당연히 UI가 이쁜 앱을 쓰지 않을까??

 독특한 특색을 갖는 UI를 꾸미기 위해서 이번에는 액션바를 활용해 보겠다. (커스텀하게 액션바를 만드는 내용은 다음에 시간날때 올리도록 하겠다..)


 글쓴이의 경우 앱에 액션바를 사용하였는데 액션바에 배경이미지를 넣고 사용하기 위해 커스텀하게 구현을 하였다. 버튼역시 테마에 맞게 이미지를 넣어 구현을 하였다. 


 이때 문제가 발생하였는데 이미지를 넣은 버튼을 배치했을 때 디바이스에 따라 해상도가 모두 다르기 때문에 버튼의 크기가 제 각각 이었다. 

 액션바의 크기는 모든 디바이스에서 비율이 똑같을 것이라고 생각하고 크기를 절대값으로 조절하였더니 문제가 생긴것이다. 해상도에 따라서 액션바의 크기도 제각각이었다. 그래서 액션바의 크기를 구해서 버튼크기를 조절하려고 했다. 그러나 액션바의 크기가 0으로 나와서 크기를 알 수 가 없었다.

 그렇다.. 액션바의 크기는 0이었던 것이다.. 

 하지만 액션바의 크기를 구할 수 있는 방법이 있다!!

 액티비티가 실행될 때 onCreate 안에서 액션바의 크기를 구하면 0이 나오지만, 스레드를 이용하면 크기를 구할수 있다.

 일단 Action Bar 와 Status Bar 크기를 구하는 코드부터 살펴보자


private void getStatusBarSize() {
	Rect rectgle = new Rect();
	Window window = getWindow();
	window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
	int StatusBarHeight = rectgle.top;
	int contentViewTop = window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
	int TitleBarHeight = contentViewTop - StatusBarHeight;
	
	Log.i("getHeight", "StatusBar Height= " + StatusBarHeight + " TitleBar Height = " + TitleBarHeight);
}

위의 메소드를 onCreate 안에서 호출하지말고 스레드를 통해서 호출한다.


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

	LinearLayout layout = new LinearLayout(this);
	layout.post(new Runnable() {
		@Override
		public void run() {
			getStatusBarSize();
		}
	});
}


스레드를 통해서 getStatusBarSize를 호출하면 크기를 구할 수 있다.


여기서 또 한가지 팁은 액션바 사이즈를 구해서 액션바 사이즈를 이용해서 액션바에 들어가는 버튼이나 이미지 크기를 조절하려면 onCreate안에서 계산하면 안된다. 스레드를 통해서 크기를 구하기 때문에 onCreate가 끝나고 크기가 구해질 수 가 있기 때문에 액션바에 들어가는 버튼의 크기를 조절하고자 한다면 스레드 안에서 getStatusBarSize()를 호출하고 난 뒤에 크기를 조절해 주도록 하자!


액션바를 커스텀하게 만드는 방법은 다음에 자세히 포스팅 하도록 하겠다.


듀얼 모니터를 사용하다가 듀얼 모니터를 해제하였을 경우 창이 옆으로 넘어가서 볼 수 없는 경우가 있다. 

이런 경우 참 난감하다. 보이지가 않아서 윈도우창을 메인 모니터로 끌어 올 수 없기 때문이다.

이 때 키보드를 사용하여 창의 위치를 이동시킬수 있는데 단축기는 다음과 같다.


Alt + Space + M 을 누른뒤에 방향키 로 움직이면 된다.



기억하도록 하자!

기억이 나지 않을것 같다면 이 블로그를 즐겨찾기에 추가하도록 하자!

  1. 무니사랑 2015.05.13 11:04 신고

    굿정보 감사

  2. 김땡떙 2015.07.13 10:48 신고

    헐설마대연오빠?????

  3. ㅇㅇ 2016.06.10 10:37 신고

    대박 감사합니다. ㅋㅋㅋ

  4. dd 2017.06.17 17:09 신고

    감사합니다. 덕분에 하루종일 인터넷 뒤지면서 찾아보던 고민이 해결됐어요!!

android-ndk-r4-windows-crystax-4 이다.

NDK를 사용하고 안드로이드에 OpenCV를 올리기 위해서는 정식 NDK 버전이 아닌 crystax ndk 버전이 필요하다고 한다?? 정식 NDK로 잘 안되는 경우는 이 파일을 받아서 사용하길 바란다.


다운로드

안드로이드 앱의 폴더와 서버의 폴더 생성 시간을 비교하고 변경하여 동기화 하는 작업을 수행하는 도중 setLastModified() 메소드가 제대로 작동하지 않았다. 

폴더의 생성시간에 접근을 하는 메소드는 setLastModified()가 맞는데 몇번을 수행하여도 폴더의 시간이 변경되지 않았다.


setLastModified()의 반환형이 boolean 형임을 파악하고 Log를 계속 찍어보았다.

return 값은 계속해서 false...


실행한 디바이스는 갤럭시 탭 10.1로 안드로이드 4.0 버전이었다.


분명히 폴더시간이 바뀌어야 되는데 바뀌지 않았다. 구글링을 한 결과 setLastModified() 메소드가 일부 디바이스에서 작동하지 않는다는 답변이 있었다. 


아마도 삼성제품에 대다수 해당되는 것 같았다. 해킹이나 버그를 방지하기 위해서 접근하지 못하게 해놓았다는 답변이다. 정말 거지같다..


결국 SDcard의 폴더 생성시간은 변경할 수 없다.


폴더 생성시간을 변경을 하지는 못하고, 서버의 폴더 생성 시간과 비교를 하기 위해서는 따로 DB를 만들거나 또 다른 편법을 써야할 것 같다..


이 문제로 고민하는 분이 없길 바란다...

윈도우 7의 기본 로그온 화면은 다음과 같다.



이런 로그온 화면을 다음과 같이 사용자가 변경하고 싶은 화면으로 변경할 수가 있다.



Windows7 로그온 화면 변경하기


[시작] - [regedit] 입력후 검색결과를 실행한다. 



레지스트리 편집기 화면에서 다음의 경로로 이동한다.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\Background



OEMBackground를 더블 클릭하여 값 데이터(V)1로 변경한다.

이때 OEMBackground라는 값이 없는 경우 [마우스 오른쪽 클릭] - [새로 만들기] - [DWORD(32비트) 값]을 선택하여 OEMBackground라는 이름의 값을 추가한다.



C:\Windows\System32\oobe\info\backgrounds 폴더로 이동한다. 해당 경로가 없을 경우 폴더를 생성해준다.

(본인은 info 폴더부터 생성하였다)

그리고 로그온 배경화면으로 사용할 이미지를 backgrounds 폴더에 넣고 이름을 backgroundDefault.jpg로 변경한다.

이때 이미지 파일의 크기는 256KB미만이어야 한다.



이제 로그온 화면이 바뀌었을 것이다.


로그온 화면을 원래상태로 되돌리고 싶다면 해당 이미지 파일을 지우면 된다.

영상처리에서 RGB 색상모델을 이용하여 간단하게 색상을 검출하는 방법에 대해서 알아보겠다.


RGB 색상모델을 실제 영상처리에서 사용하는 일은 드물다 RGB 색상모델은 조명에 영향을 많이 받기 때문에 정확도가 많이 떨어진다. 그러나 간단하게 색을 인식하는 용도로 RGB 색상모델을 쓰기도 한다. 


영상처리의 기초로 간단하게 붉은색을 인식하는 예제를 알아보도록 하겠다.


소스를 먼저 보고 설명을 하도록 하겠다.


#include <opencv\highgui.h>

int main() {
	IplImage *frame;	// 원본영상
	IplImage *red;		// 색 검출 영상

	int width, height;	// 창의 넓이, 높이
	int i, j, index;
	unsigned char R, G, B;

	CvCapture* capture = cvCaptureFromCAM(0);
	frame = cvQueryFrame(capture);

	// frame 에서 width와 height 정보를 가져온다.
	width = frame->width;
	height = frame->height;
	
	cvNamedWindow("Original",0);
	cvNamedWindow("Red Tracking",0);
	cvResizeWindow("Original", width, height);
	cvResizeWindow("Red Tracking", width, height);

	// frame와 같은 크기를 갖는 3채널 영상 생성
	red = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U, 3);

	while(capture) {
		frame = cvQueryFrame(capture);

		for(i = 0; i < (frame->height); i++) {
			for(j = 0; j < (frame->widthStep); j += (frame->nChannels)) {
				index = i * frame->widthStep + j;

				R = frame->imageData[index+2];	// R영역
				G = frame->imageData[index+1];	// G영역
				B = frame->imageData[index+0];	// B영역

				// 붉은색으로 예상되는 범위 설정
				if(R > 200 && G < 200 && B < 200) {
					// 흰색으로
					red->imageData[(i*frame->widthStep)+j + 0] = 255;	
					red->imageData[(i*frame->widthStep)+j + 1] = 255;
					red->imageData[(i*frame->widthStep)+j + 2] = 255;
				}
				else {
					// 검정색으로
					red->imageData[(i*frame->widthStep)+j + 0] = 0;
					red->imageData[(i*frame->widthStep)+j + 1] = 0;
					red->imageData[(i*frame->widthStep)+j + 2] = 0;
				}
			}
		}

		cvShowImage("Original",frame);
		cvShowImage("Red Tracking",red);

		if(cvWaitKey(33) >= 27)
			break;
	}
	cvReleaseCapture(&capture);
	cvReleaseImage(&red);
	cvDestroyWindow("Original");
	cvDestroyWindow("Red Tracking");

	return 0;
}

붉은색 영역은 본인이 임의로 설정하였다. 먼저 결과부터 확인해 보겠다.


실행결과 :


추출된 범위를 흰색으로(255) 나타내고 그 외의 범위는 검정색으로(0)으로 나타내었다.


이번 포스팅은 색상 검출에 대한 내용이므로 다른 코드에 대한 설명은 나중에 하도록 하겠다.


색상 범위


색상 검출한 결과를 나타내기 위해서 red라는 3채널의 IplImage 구조체를 선언하였다. 

카메라에서 프레임을 받아오는 동안 (while문 안에서) for 2개를 이용하여 전체영상의 각 ImageData에 접근하여서 R,G,B의 크기를 판단하였다. 

R,G,B 영역의 범위는 본인이 임의로 설정하였는데, 빨간색을 검출한다고 해서 꼭 R이 255의 값을 갖고 G와 B값은 0을 갖는것이 아니다. 컴퓨터로 작업을 한 영상이라면 R=255, G=0, B=0 의 값을 갖을수는 있지만 실제 영상에서 빨간색은 R,G,B 각각의 색상모델의 조합으로 나타내진다. 또한 그 값은 조명에 의해, 주변 환경에 의해 충분히 달라질 수 있다. 그렇기 때문에 색상검출을 위해서 실제로 사용할 경우 정확도가 크게 떨어지게 된다. 이러한 단점을 해결하기 위해 다양한 색상모델이 있고 대표적으로 YCbCr 모델을 많이 사용하고 있다. YCbCr에 대해서는 나중에 알아보도록 하겠다. 


RGB영역에서 색 검출을 굳이 하고싶다면 색 검출을 위한 범위는 직접 지정하길 바란다. 위의 코드의 범위는 본인마음대로 대충 때려넣은 범위이다.


widthStep


while문 안에서 2개의 for 루프를 이용하여 영상의 넓이(width)와 높이(height)만큼의 크기의 배열에 접근하고 있다. 이때 두번째 for 루프에는 width가 아닌 widthStep이 들어가 있다. 분명 넓이는 width인데 왜 widthStep을 쓸까?


컬러영상은 다음과 같이 B, G, R 순서의 세 영역으로 이루어져 있다.


하나의 컬러 영상은 B평면(B값만 존재), G평면(G값만 존재), R평면(R값만 존재)에 존재하는 각 값들의 합으로 이루어 진다. 3개의 평면이 하나로 겹쳐지면서 컬러 영상이 만들어 지는것이다. 우리는 각 영역의 색상값을 알아내기 위해 하나의 평면을 갖는 컬러 영상을 3개의 평면으로 나누어 볼 필요가 있다. 그렇기 때문에 width 대신 widthStep을 이용하는데 widthStep은 (영상의넓이) x (채널의 갯수) 의 값을 갖고있다. 여기서는 채널이 3채널이기 때문에(width x 3)의 값을 갖고 있는 것이다. 

한번 for루프에 들어갈 경우 [index+2], [index+1], [index+0] 으로 컬러영상에서 같은 위치에 있는 RGB평면을 동시에 체크하기 때문에 for루프에서 증가값은 3이 되어야 한다. 그렇기 때문에 j값은 nChannel 만큼 증가하게 된다. 


다시한번 말하지만 RGB색상모델은 실제 영상처리에서 사용하기에 좋은 색상모델은 아니다. 




제 머리속에 있는 내용을 정리해 보았습니다! 틀린내용이 있을수도 있고 저의 머리속에 있는 내용을 글로 옮겼기 때문에 이해가 안되는 분들도 계실꺼 같습니다. 댓글을 남겨주신다면 친절히 답변해 드리겠습니다.

NDK를 사용할 시 ndk-build를 이클립스에서 편하게 할 수 있는 방법에 대해서 알아보도록 하겠다.

NDK-build를 하여 .so파일을 생성할 때 cmd창이나 cygwin 터미널을 이용하지 않고 이클립스에서 편하게 할 수 있다.


먼저 생성한 프로젝트의 [Properties] -> [Builders] -> [New] 를 선택한다.



Program 선택


이름은 NDK_Builder로 하였다. 

Location에는 Browse File System을 선택하여 ndk-build가 있는 위치를 지정해준다.

Working Directory는 현재 프로젝트의 위치를 지정해주면 되는데 Browse Workspace를 선택하여 해당 프로젝트를 선택해준다.


다음은 같은 창에서 Refresh를 선택하고 아래 그림과 같은 순서로 선택한다. 3번을 선택하면 새 창이 뜨는데 해당 프로젝트 파일의 jni 폴더를 선택해 준다.


다음은 Build Options로 넘어가겠다. 1번은 선택사항이다. 자동으로 빌드를 하는건데 컴퓨터가 느려지길 원하지 않는다면 선택을 하지 않아도 된다. 1번을 제외한 나머지 항목은 필수로 선택을 한다. 여기서는 해당 프로젝트의libs 폴더를 선택한다. 


설정이 완료 되었다. 이제 프로젝트를 빌드해보자.


콘솔창에 다음과 같이 나왔다면 빌드에 성공한것이다.


.c 혹은 .cpp파일의 내용이 변경되었을 경우는 [Project] -> [Clean] 에서 해당 프로젝트만 Clean을 해주고 다시 디버깅을 하면 된다.


이제 cmd창에 입력할 필요 없이 이클립스에서 clean -> Debug로 간단하게 ndk build를 할 수 있다.

하지만 이후에 다른 프로젝트에서 똑같은 방법으로 했을 경우 이름이 중복된다는 경고를 볼 수 있다. 그럴때는 Builder의 이름을 NDK_Builder(2) 이런식으로 바꿔주도록 하자. 왜 굳이 따로 또 만들어야 되는지는 모르겠다.. 


이 문제를 해결할 방법을 아시는분은 알려주세요~!^^



  1. 김병섭 2014.08.18 21:37 신고

    안녕하세요?
    안드로이드에서 open cv 라이브러리를 사용하기위해 ndk를 설치하고
    빌드까지 해보려고 안드로이드 프로젝트를 생성하고
    글에서 제시해 주신대로 따라가고 있었습니다.

    그런데.. 제 프로젝트에는 'jni'폴더가 따로 생성되지 않더군요..
    프로젝트를 생성할 때 다른 방법으로 생성해 주어야 하는 건가요??

  2. dpszlfk 2015.04.27 10:23 신고

    안녕하세요 글 보고 쉽게 NDK 빌드 설정했습니다. 감사합니다.

    이제 처음 시작하는 입장이라 조심스럽습니다만...

    이름 중복 문제는 NDK_Builder를 만들 때 Working Directory에 .externalToolBuilders 폴더가 생기고
    폴더 안에 '빌드이름.launch' 파일이 생기는데, 이 파일을 지우면 중복 문제는 해결이 될 것 같습니다.
    물론... 설정해논 빌드가 지워지긴 합니다...ㅎㅎ

  3. 2015.08.12 15:07

    비밀댓글입니다

+ Recent posts