USB를 연결한 후 Logcat이 바로 보이지 않을 때 해결방법

 간혹가다 안드로이드 기기를 USB에서 분리하였다가 연결한 후 Logcat을 보려 하였을 때 Logcat이 보이지 않는 경우가 종종 있습니다. 이 경우 DDMS를 키신 후 자신의 Device를 선택하는 메뉴에서 클릭을 하시면 손쉽게 해결하실 수 있습니다.




 eclipse 오른쪽 윗 부분의 아이콘을 보시면 Open Perspective라는 메시지가 보이는 아이콘이 있습니다. 해당 아이콘을 클릭하신 후


여기서 DDMS를 선택하신 후 확인 버튼을 누르시면 DDMS 선택메뉴가 추가된 것을 확인하실 수 있습니다. DDMS를 선택하시면 현재 컴퓨터에 연결된 기기의 리스트를 보실 수 있습니다. 만약 이 때 자신의 기기가 보이지 않을 경우 아래와 같이 view Menu를 클릭하신 후 'Reset adb'를 선택하시면 접속된 안드로이드 기기 리스트가 갱신되는 것을 확인하실 수 있습니다.



 이후 자신이 Logcat을 보고자 하는 기기를 선택하시면 Logcat이 정상적으로 동작하고 있는 모습을 확인하실 수 있습니다.



xml graphical layout가 정상적으로 동작하지 않을 때

 안드로이드 애플리케이션을 개발하던 도중 XML을 편집하고 graphicl layout을 확인한 순간 다음과 같은 메시지를 보게 되었습니다.



Exception raised during rendering:java.util.Locale.toLanguageTag()Ljava/lang/String;

Exception details are logged in Window > Show View > Error Log


이러한 경우 대개 2가지 경우로 보실 수 있습니다.

1.자신이 Target으로 한 버전의 SDK가 설치되어 있지 않아 Graphical Layout가 지원되지 않는 경우

2.다른 무언가에 의해 충돌이 될 경우


 거의 대부분의 경우가 1번의 경우가 많은 것으로 보입니다. 이를 해결하는 방법은 상당히 간단합니다. SDK Manager를 실행하여 자신의 프로젝트에 해당되는 프로젝트를 다운로드하여 설치하신 뒤 Eclipse를 다시 실행하시면 Graphical Layout가 정상적으로 동작하는 것을 확인해 보실 수 있습니다.



XML의 Graphical Layout이 보이지 않는 경우 해결법

 오랜만에 Linux에서 안드로이드 작업을 하려고 새로운 프로젝트를 생성한 후 작업을 하려 했더니 아래와 같은 에러가 발생하면서 아무런 화면도 뜨지 않는 현상이 발생하였습니다.



The rendering target (Android 5.0.1) is still loading.

The layout will refresh automatically once the process is finished.


 처음에는 다소 당황했었으나 차분히 원인을 분석해 본 결과 안드로이드 버전을 낮은 버전으로 설정하는 바람에 Graphical Layout가 해당 버전과 맞지 않아 위와 같은 에러가 발생하였더군요 해결 방법은 간단합니다.


1. 먼저 에러가 발생한 xml 파일을 끈 후 다시 엽니다.


2. Graphical Layout을 클릭한 후 xml 창의 왼쪽 위 부분을 자세히 보면 아래쪽 화살표 모양의 버튼이 보입니다 해당 버튼을 클릭하면 현재 설정된 버전의 번호가 보입니다 해당 버전의 번호를 클릭하면 설정할 수 있는 버전 명단이 뜨는데 그 중 자신의 프로젝트에 해당하는 버튼을 클릭합니다.



 위의 과정을 거치만 아래와 같이 Grahpical Layout가 정상적으로 출력 되는 것을 확인하실 수 있습니다.




Extract contour area using OpenCV in Android(OpcnCV에서 검출된 영역의 넓이 구하기)

※이 프로그램은 OpenCV의 예제파일인 OpenCV Sample - color-blob-detection을 수정한 자료임을 알립니다.


OpenCV의 색상 검출 프로그램을 돌려보면 원하는 영역이 아래와 같이 붉은 테두리로 나타나는 것을 보실 수 있을 것입니다.



위 그림에서 보시는 이 빨간 테두리를 '등고선(Contour)'라 부릅니다. Android OpenCV에서는 이 빨간 테두리가 쳐진 부분의 넓이를 추출하는 기능을 가지고 있습니다. 이를 적용하는 방법은 아래외 같이 하시면 되겠습니다.


ColorBlobDetectionActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ColorBlobDetectionActivity extends Activity implements OnTouchListener, CvCameraViewListener2 {
    private static final String  TAG              = "OCVSample::Activity";
 
    private boolean              mIsColorSelected = false;
    private Mat                  mRgba;
    private Scalar               mBlobColorRgba;
    private Scalar               mBlobColorHsv;
    private ColorBlobDetector    mDetector;
    private Mat                  mSpectrum;
    private Size                 SPECTRUM_SIZE;
    private Scalar               CONTOUR_COLOR;
 
    private CameraBridgeViewBase mOpenCvCameraView;
    List<MatOfPoint>             contours;
    
}

 위의 코드에서 contour 변수는 원래 onCameraFrame() 함수 내에 있는 변수입니다. 이를 다른 곳에서 사용하기 위해서는 위의 코드에서 보시는 것 처럼 전역변수로 선언하는 것이 사용하기에 편합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        mRgba = inputFrame.rgba();
 
        if (mIsColorSelected) {
            mDetector.process(mRgba);
            contours = mDetector.getContours();
            Log.e(TAG, "Contours count: " + contours.size());
            Imgproc.drawContours(mRgba, contours, -1, CONTOUR_COLOR);
 
            Mat colorLabel = mRgba.submat(4, 68, 4, 68);
            colorLabel.setTo(mBlobColorRgba);
 
            Mat spectrumLabel = mRgba.submat(4, 4 + mSpectrum.rows(), 70, 70 + mSpectrum.cols());
            mSpectrum.copyTo(spectrumLabel);
        }
        
        return mRgba;
    }

 onCameraFrame() 함수 내에 있던 contours 변수를 외부에 전역으로 선언하였으므로 이 함수 안에서는 그냥 변수에 값을 넣는 개념으로 이해하시면 되겠습니다.


 끝으로 Contour의 면적을 구하는 부분을 다음과 같이 설정하시면 Contour의 Area값을 얻으실 수 있습니다.
1
2
3
4
5
6
7
8
if(contours != null){
        double d;
        d=0;
        for(int i=0; i<contours.size();i++){
            d += Imgproc.contourArea(contours.get(i));
        }
            
}

 프로그램을 처음 구동할 때 contours의 값이 null일 경우가 있으므로 if문을 통해 null일 경우 예외처리를 해줍니다.

그 다음으로 contours 내의 모든 등고선의 값을 d에 저장을 하시면 화면에 표시되는 등고선(Contour)의 면적값들을 구하실 수 있습니다.


 위에서 보시는 바와 같이 contours는 List 변수로 내부에는 화면에 표시된 등고선의 수 만큼의 Mat 값들이 있는 것을 확인하실 수 있습니다. 이러한 contours를 반복문을 통해서 각 하나의 contour값을 확인하는 과정을 거칩니다.


 Imageproc.contourArea() 함수는 해당 Contour의 값을 구하는 기능을 가지고 있습니다. 이를 통하여 해당 contour의 넓이를 구해 모든 값을 더하게 되면 화면에 표시된 붉은 영역의 총 면적을 구할 수 있게 됩니다.




Color detection using Android openCV(안드로이드 OpenCV로 특정 색깔 인식)

※이 프로그램은 OpenCV의 예제파일인 OpenCV Sample - color-blob-detection을 수정한 자료임을 알립니다.


1.먼저 자신이 찾고자 하는 색깔의 Hsv를 알아내야 합니다. 만약 자신이 찾는 색깔의 Hsv를 모르는 경우 해당 색깔의 RGB를 Hsv로 변환하는 사이트를 이용합니다.

http://www.rapidtables.com/convert/color/rgb-to-hsv.htm




위의 RGB 색상 변환으로 나오는 HSV의 값을 아래와 같이 입력하시면 되겠습니다.

new Scalar(235, 75.2, 45.9, 0.0);


위에서 처리한 값을 이제 아래의 onCameraViewStarted() 함수에 입력해주시면 프로그램이 실행하자마자 해당 색상을 검출하는 것을 확인하실 수 있습니다.


ColorBlobDetectionActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void onCameraViewStarted(int width, int height) {
        mRgba = new Mat(height, width, CvType.CV_8UC4);
        mDetector = new ColorBlobDetector();
        mSpectrum = new Mat();
        mBlobColorRgba = new Scalar(255);
        SPECTRUM_SIZE = new Size(200, 64);
        CONTOUR_COLOR = new Scalar(255,0,0,255);
        
        
        mBlobColorHsv = new Scalar(235, 75.2, 45.9, 0.0);
        mDetector.setHsvColor(mBlobColorHsv);
        mIsColorSelected = true;
        
    }


  • 노태규 2016.03.12 18:10 ADDR 수정/삭제 답글

    안드로읻용 opencv 자료 주로 어디서 찾으시나요 찾기 힘드네요

  • 뉴비의 눈물 2018.06.15 11:07 ADDR 수정/삭제 답글

    안드로이드 카메라가 아닌, 이 소스를 이클립스 (JAVA) 노트북에 적용할려면 어떻게 해야할까용........

    • Justin T. 2018.06.15 11:09 신고 수정/삭제

      이 소스코드는 안드로이드에서 사용되는 소스입니다.
      혹시 노트북의 운영체제가 안드로이드이신가요

Use front camera with OpenCV 2.4.9 for android(안드로이드 OpenCV에서 전면카메라 적용 방법)

 굳게 마음을 먹고 OpenCV를 사용하여 프로그래밍을 해보려고 하는데 SurfaceView를 통해 전면 카메라를 사용하는 방법과 다른 방법을 쓰는 것 같아 찾아보니 쉽지가 않더군요.


그래서 직접 OpenCV 라이브러리를 찾아보면서 적용방법을 찾아본 결과 다음과 같은 결과를 얻을 수 있었습니다.

안드로이드용 OpenCV에서 아래의 클래스가 카메라를 담당하는 것으로 보였습니다.


private CameraBridgeViewBase mOpenCvCameraView;

위 클래스를 이것 저것 적용하다보니 사용법을 찾아낼 수 있었습니다.

아래의 코드와 같이 onCreate()에서 굵게 표시한 부분을 코드에 삽입하면 OpenCV가 전면카메라를 사용하는 것을 확인하실 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
 
        setContentView(R.layout.color_blob_detection_surface_view);
 
        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.color_blob_detection_activity_surface_view);
        mOpenCvCameraView.disableView();
        mOpenCvCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_FRONT);
        mOpenCvCameraView.setCvCameraViewListener(this);
    }



  • 공학도 2015.05.26 23:57 ADDR 수정/삭제 답글

    잘봤습니다. 다름아니라 저 굵게 표시된 코드 원리좀 설명듣고싶은데 알수있을까요

  • Justin T. 2015.05.27 00:08 신고 ADDR 수정/삭제 답글

    CameraBridgeViewBase는 OpenCV 내에서 카메라와 관련된 기능들을 구현하는데 사용되는 것으로 알고 있습니다.
    그래서 이를 통해 카메라의 전면부를 사용할 수 있도록 하는 기능을 구현한 겁니다.

    그런데 이상하게도 맨 처음부터 다음과 같이 프로그램을 설정하면 전면카메라가 설정이 되지 않더라고요.

    mOpenCvCameraView.setCameraIndex(CameraBridgeViewBase.CAMERA_ID_FRONT);

    이는 아마도
    mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.color_blob_detection_activity_surface_view);
    요 부분이 구현되기 시작할 때부터 카메라가 구동이 되기 시작하는데
    카메라가 구동되는 동안에는 사용하는 카메라를 변경하는게 안된다고 알고 있습니다.
    그래서 일단 mOpenCvCameraView를 불러온 후 disable을 하여서 카메라 기능을 잠시 끈 후 재설정을 해주는 것입니다.
    이렇게 설정만 하면 나중에 다시 카메라가 저절로 enable 되면서 구현이 되는 것으로 이해해주시면 되겠습니다.

블루투스를 통해 이미지를 바이트로 전송하기

 대부분의 안드로이드 폰에는 블루투스가 기본적으로 내장되어 있습니다. 블루투스 기능을 활용하면 굳이 인터넷이나 기지국을 거치지 않고도 안드로이드 기기간에 파일 전송이 가능하지요.

 이번 포스팅에서는 간단하게 다른 안드로이드 폰으로 사진을 전송하는 방법에 대해 알아보겠습니다.


 시작하기에 앞서 아래에 있는 블루투스 채팅 프로그램 프로젝트를 다운로드하여 Import 합니다.

 

BluetoothChat (1).zip





 위의 예제는 안드로이드 폰 상호간에 간단한 문자 채팅을 할 수 있는 프로그램입니다. 이 프로젝트에 이미지를 전송할 수 있는 기능을 넣어보도록 하겠습니다.


먼저 AndroidManifest.xml 파일에 다음과 같은 퍼미션을 추가 해줍니다.

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


다음으로 메인 레이아웃인 main.xml에 그림을 띄울 ImageView와 그림을 전송하기 위한 Button을 다음과 같이 추가합니다.

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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <ListView android:id="@+id/in"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:stackFromBottom="true"
        android:transcriptMode="alwaysScroll"
        android:layout_weight="1"
    />
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
 
        <ImageView
            android:id="@+id/getimage"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:src="@drawable/app_icon"
            android:scaleType="centerInside"
            />
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
 
            <EditText
                android:id="@+id/edit_text_out"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                android:layout_weight="1" >
 
                <requestFocus />
            </EditText>
 
            <Button
                android:id="@+id/button_send"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/send" />
 
            <Button
                android:id="@+id/button_image"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/send_image" />
        </LinearLayout>
 
    </LinearLayout>
 
</LinearLayout>
 



다음으로 BluetoothChat.java를 수정해 보도록 하겠습니다.

먼저 추가된 Button과 ImageView에 대한 함수를 생성합니다.
1
2
    private Button mSendImage;
    private ImageView iv;


다음으로 setupChat() 함수 내에 다음과 같이 추가합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    private void setupChat() {
        
        ............
    
        iv = (ImageView)findViewById(R.id.getimage);
        mSendImage = (Button) findViewById(R.id.button_image);
        mSendImage.setOnClickListener(new OnClickListener(){
 
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_PICK,
                                               android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                startActivityForResult(intent,10);
            }
            
        });
    }


위에서 보는 것과 같이 이미지 갤러리를 Intent를 통해 불러오는 것을 볼 수 있습니다.

Intent를 통해 선택된 사진은 onActivityResult() 함수를 통해 얻을 수 있습니다.


onActivityResult() 함수에 다음과 같이 값을 추가합니다.

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
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        
        ............
        
        switch (requestCode) {
            
            ............
            
            case 10:
            if(resultCode==RESULT_OK && data != null){
                try {
                    Uri selectedImage = data.getData();
                Bitmap bp = Images.Media.getBitmap(this.getContentResolver(), selectedImage);
                    
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bp.compress(CompressFormat.JPEG, 80, baos);
                byte[] imageByte = baos.toByteArray();
                
                iv.setImageBitmap(BitmapFactory.decodeByteArray(imageByte, 0, imageByte.length));
 
                if(mChatService.getState() == BluetoothChatService.STATE_CONNECTED){                        
                    int len;
                    final int size = 512;
                    byte[] sendByte = new byte[size];
                    ByteArrayInputStream bais = new ByteArrayInputStream(imageByte);
                    mConversationArrayAdapter.add("이미지 전송을 시작합니다.");
    
                        
                    sendByte[0] = 6;
                    sendByte[1] = 26;
                    sendByte[2] = 18;
                    mChatService.write(sendByte);
                    while( (len=bais.read(sendByte)) != -1){                            
                        if(len<512){
                            byte[] EOF = new byte[len];
                            for(int eof=0 ; eof<EOF.length; eof++){
                                EOF[eof] = sendByte[eof];
                            }
                            mChatService.write(EOF);
                            
                        }else{
                            mChatService.write(sendByte);
                        }
                    }
                    
                    sendByte[0] = 26;
                    sendByte[1] = 6;
                    sendByte[2] = 18;
                    mChatService.write(sendByte);
                        
                    mConversationArrayAdapter.add("이미지 전송이 완료되었습니다!");
                    mConversationArrayAdapter.add("Image Size : " + imageByte.length);
                }
                    
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
           }
           break;
     }
    }



마지막으로 Handler를 통해 전송된 사진의 값을 수정하는 부분을 다음과 같이 수정해 줍니다.


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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
....
    boolean imageTransfer = false;
    boolean imageTransferW = false;
    ByteArrayOutputStream ReceiveImage;
 
    // The Handler that gets information back from the BluetoothChatService
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_STATE_CHANGE:
                if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
                switch (msg.arg1) {
                case BluetoothChatService.STATE_CONNECTED:
                    mTitle.setText(R.string.title_connected_to);
                    mTitle.append(mConnectedDeviceName);
                    mConversationArrayAdapter.clear();
                    break;
                case BluetoothChatService.STATE_CONNECTING:
                    mTitle.setText(R.string.title_connecting);
                    break;
                case BluetoothChatService.STATE_LISTEN:
                case BluetoothChatService.STATE_NONE:
                    mTitle.setText(R.string.title_not_connected);
                    break;
                }
                break;
            case MESSAGE_WRITE:
                byte[] writeBuf = (byte[]) msg.obj;
                String writeMessage = new String(writeBuf);
                mConversationArrayAdapter.add(writeBuf.length+" "+(int)writeBuf[0]
                                                +" "+(int)writeBuf[1]);
                mConversationArrayAdapter.add("나 :  " + writeMessage);
                
                break;
            case MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                if(msg.arg1 > 2 && readBuf[0]==6 && readBuf[1]==26 && readBuf[2== 18){
                    imageTransfer = true;
                    ReceiveImage = new ByteArrayOutputStream();
                    mConversationArrayAdapter.add("Image Transfer Start!");
                    break;
                }
                
                if(msg.arg1 > 2 && readBuf[0]==26 && readBuf[1]==6 && readBuf[2== 18){
                    imageTransfer = false;
                    mConversationArrayAdapter.add("Image Transfer End!");
                    mConversationArrayAdapter.add("Image Size : " + ReceiveImage.size());
                    byte[] getImage = ReceiveImage.toByteArray();
                                        
                    iv.setImageBitmap(BitmapFactory.decodeByteArray(getImage, 0, getImage.length));
                    
                    break;
                }
                
                if(imageTransfer){
                    try {
                        ReceiveImage.write(readBuf);
                        if(readBuf.length==512)
                        mConversationArrayAdapter.add("Size : " + readBuf.length +", "
                                                    +(short)readBuf[0]+", " + (short)readBuf[511]);
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }else{
                    String readMessage = new String(readBuf, 0, msg.arg1);
                    mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);
                }
                break;
            case MESSAGE_DEVICE_NAME:
                // save the connected device's name
                mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to "
                               + mConnectedDeviceName, Toast.LENGTH_SHORT).show();
                break;
            case MESSAGE_TOAST:
                Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
                               Toast.LENGTH_SHORT).show();
                break;
                
            case 6:
                //Toast.makeText(getApplicationContext(), "WTF", Toast.LENGTH_SHORT).show();
                
                break;
            }
        }
    };
....
cs


  • khb 2015.03.25 17:06 ADDR 수정/삭제 답글

    사진 전송시 수신 휴대폰에서 여러개의 패킷으로 나누어서 받아 바이트값을 이미지 값으로 바꾸지 못합니다..ㅠㅠ

    • Justin T. 2015.03.25 21:07 신고 수정/삭제

      사용 환경에 따라 정상적으로 동작하지 않는 경우가 있습니다.
      이 경우 전송 방식을 Serializable을 사용해보셨으면 합니다.

  • 2015.05.11 17:44 ADDR 수정/삭제 답글

    비밀댓글입니다

    • Justin T. 2015.05.11 23:09 신고 수정/삭제

      코드를 확인해 보니 Handler 부분이 시작되기 전에 imageTransfer 등의 전역변수가 미리 선언이 되어있지 않아 오류가 발생하는 것이었습니다.
      코드 부분에 전역변수 부분도 추가하였으니 확인 부탁드려요

  • 2소 2015.05.14 16:00 ADDR 수정/삭제 답글

    iv = (ImageView)findViewById(R.id.getimage);
    mSendImage = (Button) findViewById(R.id.button_image);
    이부분에서 에러가 뜨는데 에러 이유는 "getimage cannot be resolved or is not a field" 이렇게 뜹니다.
    혹시 죄송한데 조언좀 구할수 있을까요?

    • Justin T. 2015.05.15 00:52 신고 수정/삭제

      아래 링크에서 해답을 찾아보셨으면 합니다.
      http://hyojjeong.tistory.com/entry/cannot-be-resolved-or-is-not-a-field-%EC%97%90%EB%9F%AC-%EB%B0%9C%EC%83%9D-%EC%8B%9C-%EB%8C%80%EC%B2%98-%EB%B0%A9%EB%B2%95

      http://ckbcorp.tistory.com/649

  • dd 2015.05.25 20:06 ADDR 수정/삭제 답글

    안녕하십니까 다름이 아니라, 어플을 실행해본결과 수신받는 핸드폰에 이미지가 안뜨더라구여..이거는 어떤 문제 때문에 그런건지 혹시 알수있을까요?

    • Justin T. 2015.05.26 12:23 신고 수정/삭제

      위 소스코드의 가장 큰 문제점이라 할 수 있겠습니다.
      소스코드 자체만으로는 데이터를 완벽히 보내는 것 처럼 보입니다만
      중간에 나누어서 보내는 과정에서 이미지와 관계없는 값이 섞여서 전송되다보니
      수신측에서 이미지가 깨져버리는 경우가 생깁니다.

      이는 직렬화라는 것을 통해 해결할 수 있다고 들었습니다.
      관련 내용은 찾아보시면 쉽게 아실 수 있을 것입니다.

  • 2015.05.25 21:54 ADDR 수정/삭제 답글

    비밀댓글입니다

    • Justin T. 2015.05.26 12:25 신고 수정/삭제

      수신측에 데이터를 전송할 때 Buffer의 앞 3개 부분의 값을 확인해서 전송의 시작과 종료 여부를 파악하기 위해 쓰인 부분입니다.

    • ㅎㅎㅎㅎㅎ 2015.05.27 23:40 수정/삭제

      수신 부분에서 직렬화라는 말씀이 case MESSAGE_READ: 이부분에서 직렬화 부분을 추가 하면된다는 말씀이시간요?
      그럼 if문은 따로 수정해야될 부분이 없는가요..?

    • Justin T. 2015.05.28 10:49 신고 수정/삭제

      위 소스코드와 직렬화인 Serializable은 별개의 개념입니다.
      이 부분은 JAVA 쪽 지식이니 관련자료를 따로 찾아 공부하셔야 될 겁니다.
      질문해주신 소스코드의 부분은 제가 임의로 전송이 시작됨을 표시한 내용이고 Serializable을 활용하면 코드를 좀더 깔끔하게 만드실 수 있을 겁니다.

  • 냥냥 2015.10.08 01:09 ADDR 수정/삭제 답글

    안녕하세요.
    맨 처음 채팅 예제를 import 해서 실행시키는 것 까지는 됩니다만
    그 후에 블루투스를 켜서 다른 기기와 접속하는 방법을 잘 모르겠습니다.

    다른 사용자와 접속하기 위해 기기목록을 불러오는 버튼이 폰 화면에 안보이더라구요.

    • Justin T. 2015.10.08 00:50 신고 수정/삭제

      위 예제는 안드로이드 기기의 물리버튼인 메뉴를 눌렀을 때 주변 기기를 탐색하는 기능이 탑재되어 있습니다.
      그러나 최근에 나오는 안드로이드 기기에는 물리버튼이 없어 이를 사용하지 못하는 상황으로 알고 있습니다.
      최신 버전의 Activity 예제를 실행하신 후 위 소스코드를 새로 입력하시면 상단 메뉴가 보일 것으로 보입니다.

  • 냥냥 2015.10.08 01:10 ADDR 수정/삭제 답글

    친절한 답변 정말 감사드립니다.

    또 궁금한 것이 있는데, onActivityResult() 함수에 이미지 전송에 관련된 값을 추가할 때
    포스팅 하신 코드 기준 13번째 줄 에서 Images 와
    16번째 줄에서 CompressFormat 이 붉게 표시되어 에러가 뜨는데 원인을 아시는지요?

    • Justin T. 2015.10.08 01:29 신고 수정/삭제

      Bitmap이 import되어있는지요?
      위 소스코드는 정상적으로 동작되던 당시의 소스코드를 그대로 올린 것입니다. 검색을 통해 원인을 찾아보셨으면 합니다.

  • 냥냥 2015.10.09 15:56 ADDR 수정/삭제 답글

    actionbar로 option menu를 끌어오려고
    manifest파일에서 minsdkversion을 11로 설정할 경우 작동이 안되네요.
    최신 폰에서 이 어플을 사용할 수 있는 방법이 없는것 같습니다.

  • 조두현 2015.11.09 16:32 ADDR 수정/삭제 답글

    혹시 이미지 데이터를 핸드폰으로 수신하는 기능을 추가하는 예제는 없을까요...?

    • Justin T. 2015.11.09 16:49 신고 수정/삭제

      본 예제는 이미지와 같은 데이터 발신 및 수신 기능이 포함되어 있습니다. 궁금하신 부분이 있으시다면 부담없이 문의해주세요

  • gg 2015.11.12 17:28 ADDR 수정/삭제 답글

    소스 예제에
    main.xml에 마지막 부분 @string/send_image 에러와
    public void handleMessage(Message msg) 쪽 mTitle 에러 나는 부분이 있어서 안됬는데 에러를 다 해결하고 나서
    실행시킬때 어플이 바로 중지되네요..
    해결 방안이 있을까요...

    • Justin T. 2015.11.12 08:48 신고 수정/삭제

      죄송합니다. 비밀글에는 답변을 해드리지 않습니다

    • gg 2015.11.12 17:56 수정/삭제

      비밀글 풀엇습니다 ㅠ

    • Justin T. 2015.11.12 19:48 신고 수정/삭제

      직관적으로 보았을 때 프로그래밍 환경에 문제로 보입니다.
      위의 예제는 최신 버전의 Android Studio와 Kitkat에서 동작이 확인되었습니다.
      그리고 에러가 발생하는 부분의 logcat를 google에서 검색해보시면 답을 금방 찾아보실 수 있습니다.

  • gg 2015.11.13 01:26 ADDR 수정/삭제 답글

    이미지 전송하는데 까지 성공하였습니다 ^^ 감사합니다
    궁금한것이 있는데 이미지를 전송한 데이터는 따로 저장을 어떻게 해줘야되나요?

    • Justin T. 2015.11.13 01:35 신고 수정/삭제

      아래 포스팅이 도움이 되셨으면 합니다.
      http://elecs.tistory.com/149

    • gg 2015.11.13 04:13 수정/삭제

      이미지를 전송하였는데 바이트 값으로 오는 것 같습니다...
      제가 잘못한것인가요..?
      혹시 정상적으로 작동한것이라면 바이트값으로 오는 데이터를 이미지로 받으려면 소스를 어떻게 수정해야될까요..

    • Justin T. 2015.11.14 07:37 신고 수정/삭제

      이미지가 바이트 값으로 오는 것이 맞습니다.
      다만, 위 예제의 가장 안좋은 점이 전송 도중에 이미지와 관계없는 바이트 값이 중간에 끼어들 경우 이미지가 뜨지 않는 경우가 발생합니다.
      이는 Serializable 방식을 활용한 Object socket 방식을 활용하실 경우 byte값 손실을 최소화 할 수 있습니다. 이 방법은 아래 블로그를 참고하셨으면 합니다.

      http://elecs.tistory.com/148

  • GG 2015.11.16 21:19 ADDR 수정/삭제 답글

    아두이노를 통해서 안드로이드폰으로 이미지파일을 전송시켰습니다. 따로 깨지거나 하는건 없는데
    휴대폰에서 데이터를 받을 때 바이트값만 받고 이미지는 뜨지않는데
    바이트 값이 제대로 다 받아진다면 이미지가 자동으로 저장되는건가요?

    • Justin T. 2015.11.17 02:25 신고 수정/삭제

      아두이노에서의 이미지 포맷 양식을 확인해 보셔야 할 듯 합니다.
      안드로이드에서는 RAW(무압축), JPG, PNG 이미지 포맷을 지원합니다.

  • onejsh 2015.11.17 00:49 ADDR 수정/삭제 답글

    이미지를 선택하게되면 애플리케이션이 예상치않게 중지되었습니다 하면서 앱이 종료되는데
    왜그런건가요?? 단말기 2개다 오류나는데 블루투스 연결다한상태에서요

    • Justin T. 2015.11.17 09:59 신고 수정/삭제

      위 소스코드를 Jellybean과 kitkat에서 수행한 결과 정상적으로 동작합니다.
      로그캣을 확인하신 후 구글에서 답을 찾아보셨으면 합니다.

  • GG 2015.11.19 20:32 ADDR 수정/삭제 답글

    휴대폰끼리 이미지 전송을 하여 저장하는 것까지는 성공했는데
    전송된 이미지가 1KB까지만 저장되어 사진이 제대로 나오지 않습니다.
    해결할 방법이 있을까요

    • Justin T. 2015.11.19 20:38 신고 수정/삭제

      위에서 말씀드렸던 Serializable 방식의 Object Socket 방식을 사용해 보셨는지요?

    • GG 2015.11.20 01:58 수정/삭제

      자꾸 질문하는데 열심히 답변주셔서 감사합니다 ^^
      파일크기 문제는 해결하였습니다.
      다른문제인 데이터 값이 뒤죽박죽으로 섞여서 전달되는 문제가 생겼습니다..
      하나해결할때마다 새로운 문제가 생기네요 ㅠㅠ

  • ㅅㅎㅎ 2016.11.23 17:30 ADDR 수정/삭제 답글

    매우 감사합니다.

  • 공대생의 좀비 2018.05.24 20:53 ADDR 수정/삭제 답글

    안드로이드를 공부하는 학생입니다! 이번에 이것을 참고해서 블루투스를 이용해서 아두이노와 안드로이드를 연결해서 이미지를 전송받는 것을 App으로 만들고있는데 이것을 참고하는 과정에서 Handler를 수정하라는 제일 마지막 부분이 이해가 되지 않는데 괜찮으시면 답변 부탁드려도 될까요?

  • good 2018.09.11 23:19 ADDR 수정/삭제 답글

    안드로이드를 공부하고 있는 학생입니다 안드로이드 스튜디오 프로그램을 짜고 있습니다 제가 마지막 레이아웃에 블루투스 통신으로 bmp 파일을 pc 바탕화면에 전송 하고 싶은데 마지막 레이아웃 전에는 ROI라는 파일로 bmp파일을 만들었는데 블루 투스 통신에서 막혀있는데 조언좀 구할수있을까요?

[NDK] openCV jni 소스 헤더파일이 include 되지 않을 때 해결방법

 안드로이드로 openCV를 해보기 위해 NDK를 설치한 후 직접 소스를 수정해보기 위해 소스파일을 연 순간 다음과 같은 장면이 펼쳐졌습니다.



 분명 직접 빌드까지 하였던 프로젝트임에도 이렇게 헤더파일이 잡히지 않아 당황하였습니다. 혹시 프로젝트를 빌드에 성공하지 못하셨던 분이라면 아래의 블로그를 참조해 주시길 바랍니다.


 http://adppark.tistory.com/303



 그럼 프로젝트의 헤더파일이 연결될 수 있도록 해보겠습니다.


1. 자신이 원하는 jni 프로젝트 폴더를 오른쪽 클릭 -> Properties -> C/C++ General -> Paths and Symbols를 선택합니다.



 2. 다음과 같은 화면이 나오면 Includes 탭 내의 Languages 내에서 C/C++ 둘중 Include directories가 있는 경우 모두 추가해줍니다.

오른쪽의 Add를 선택합니다.



3. File System...를 선택하신 후



4. "자신의 안드로이드 openCV 버전/sdk/native/jni/include"를 선택하신 후 확인 버튼을 누른 후



5. OK버튼을 눌러 Include directories에 추가합니다.



 6. 추가한 Include 디렉토리를 상단 두번째에 위치시킵니다. 오른쪽의 Move Up를 클릭하여 이동이 가능합니다.



 8. jni 폴더 내에 있는 Android.mk 파일을 연 후 include 부분에 자신의 OpenCV의 절대 경로로 수정합니다.



 9. 시작버튼을 눌러 '고급 시스템 설정'을 검색하여 나오는 항목을 클릭합니다.



10. 다음과 같이 시스템 설정 메뉴가 나오는 것을 보실 수 있습니다. 아래 환경변수(N)을 클릭합니다.



11. 새로 만들기(W)를 선택하신 후



12. 자신의 Android NDK의 환경변수를 설정합니다.

변수 이름은 'NDKROOT'로 설정하시고

변수값에는 자신의 NDK가 있는 위치를 입력하신 후 확인버튼을 누릅니다.



 위와 같은 과정을 거치시면 다음과 같이 헤더파일이 정상적으로 동작하는 것을 확인하실 수 있습니다.




<Project name> does not specify a android.test.InstrumentationTestRunner instrumentation or does not declare uses-library android.test.runner in its AndroidManifest.xml

 블로그에 올라와 있는 안드로이드 프로젝트 파일을 받은 후 이를 실행하기 위해 실행을 하려 했더니 다음과 같은 에러가 뜨면서 프로그램이 실행되지 않는 상황을 맟게 되었습니다.


 "<Project name> does not specify a android.test.InstrumentationTestRunner instrumentation or does not declare uses-library android.test.runner in its AndroidManifest.xml"



이렇게 나오는 경우 다음과 같이 진행해 주시면 되겠습니다.



해당 프로젝트 폴더 위에 마우스 우클릭 -> Run As(또는 Debug As) -> 1 Android Application


다음과 같이 프로그램을 실행하면 정상적으로 동작되는 것을 확인하실 수 있습니다.


만약 위와 같은 방법으로 실행이 되지 않으신 분들은

Run As -> Run Configurations... 를 선택하신 후

동작 시킬 프로그램을 선택하신 후 수동으로 프로그램을 실행할 타겟을 선택합니다.

MediaPlayer, VideoView 재생이 끝났을 때 이벤트 처리하기 (setOnCompletionListener)

public void setOnCompletionListener (MediaPlayer.OnCompletionListener listener)


 안드로이드를 통해 동영상이나 mp3 파일 등을 재생한 후 해당 미디어를 모두 재생이 되었을 때 설정하는 이벤트입니다. 사운드를 재생하는 MediaPlayer라던가 동영상을 재생하는 VideoView에서 사용할 수 있습니다.


 함수의 인자로 OnCompletionListener()가 사용됩니다. 이 함수는 인터페이스로 new 로 함수를 새로 선언함으로서 적용할 수 있습니다.


 아래는 MediaPlayer에 setOnCompletionListener 함수를 적용한 예제입니다. 음악이 모두 재생된 후 이벤트가 정상적으로 발생되는 것을 확인하실 수 있습니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
MediaPlayer mp;
mp = MediaPlayer.create(this, R.raw.melody);
mp.start();
mp.setOnCompletionListener(new OnCompletionListener(){
 
        @Override
        public void onCompletion(MediaPlayer mp) {
            // TODO Auto-generated method stub
            l1.setBackgroundColor(Color.TRANSPARENT);
            l2.setBackgroundColor(Color.TRANSPARENT);
            l3.setBackgroundColor(Color.TRANSPARENT);
            pb1.setVisibility(View.INVISIBLE);
            pb2.setVisibility(View.INVISIBLE);
            pb3.setVisibility(View.INVISIBLE);
            tv4.setText("");
            tag = 0;
        }
        
    });


안드로이드 SDK 업데이트 후 이클립스에서 실행이 안될 때(Android ADT 재설치)

 모처럼 안드로이드 SDK Manager를 실행시켜 업그레이드를 시켜준 후 이클립스를 실행시켰더니 다음과 같이 빨간 줄들이 난무하면서 실행이 되지를 않는군요.


 지금까지 열심히 만들어왔던 애플리케이션 프로젝트가 이렇게 갑작스럽게 실행조차 되지 않는다면 상당히 당황스러울 것이라 생각합니다. 보통 일반적으로는 이클립스를 업데이트 하는 것 만으로 해결이 되는 경우가 많습니다.


이클립스를 업데이트 하는 방법은 다음과 같습니다.

Menu->Help->Check for Updates


 그런데 종종 이렇게 이클립스를 업데이트 시켰음에도 불구하고 위의 화면과 같이 계속 먹통을 일으키는 경우가 발생합니다. 이 경우 이클립스에 설치되었던 Android ADT를 완전히 삭제한 후 다시 설치해야 합니다.



1. 메뉴에서 Help->Install New Software... 를 실행합니다.


2. 다음과 같은 화면이 나왔을 경우 오른쪽 윗 부분의 Add를 클릭합니다.



3. Name은 자신이 원하는 대로 작성하셔도 됩니다.

Location에는 다음과 같은 주소를 입력합니다.

https://dl-ssl.google.com/android/eclipse/

입력을 마친 후 OK 버튼을 클릭합니다.



4. 정상적으로 수행되었을 경우 위와 같이 Developer Tools 메뉴가 나타납니다.

확인후 위의 그림과 같이 빨간색으로 표시한 'already installed'를 클릭합니다.



5. 현재 이클립스에 설치된 소프트웨어들의 목록이 나타납니다.

Id 부분에서 안드로이드와 관련된 소프트웨어를 모두 선택하신 후 Uninstall을 클릭합니다.



6. 목록들을 다시 한 번 확인한 후 Finish를 눌러 모두 삭제해줍니다.



7. Yes를 누른 후 이클립스를 다시 실행합니다.



8. 다시 이클립스를 실행한 후 Help->Install New Software를 클릭한 후 위에서 수행하였던 Add버튼을 누른 후 안드로이드 ADT를 다시 설치합니다.


설치를 완료하면 아래와 같이 프로그램이 정상적으로 수행되고 있음을 확인하실 수 있습니다.




숨겨진 Activity 혹은 Fragment의 Thread를 종료시키는 방법

 요즘 나오는 안드로이드 앱의 구성을 보면 여러개의 Fragment를 사용하여 Action Activity나 PagerView Activity를 활용하는 경우가 많습니다. 위의 Activity를 적절히 사용하면 몇 줄 안되는 코드로 화려한 화면 연출을 구성할 수 있기 때문이겠죠.

 그러나 안드로이드 개발 초보자들의 경우 위의 구성을 한 Activity와 Fragment의 동작 원리를 잘 이해하지 못할 경우 앱이 원하는 대로 동작을 하지 않는 경우가 수두룩 합니다. 특히 Fragment 내의 Thread를 활용하던 중 갑자기 앱이 죽어버리는 광경을 많이 보셨을 것이라 생각합니다.


 


 위에 보시는 화면은 Fragment를 활용하여 Tab과  Pager를 적용한 어플리케이션입니다. 보시는 바와 같이 4개의 탭이 존재하고 각 탭마다 고유의 Fragment를 가지고 있습니다.

 위의 화면으로 보기에는 첫 번째 'Simple'의 Fragment만이 동작을 하는 것 처럼 보이지만 사실은 보이는 탭을 중심으로 왼쪽과 오른쪽의 Fragment 또한 동작을 하고 있는 상황입니다. Pager 형식으로 화면으로 오른쪽에서 왼쪽으로 드래그를 하면 Contacts의 Fragment 화면으로 넘어가게 되는데요. 이는 'Simple'의 Fragment가 떠 있는 상황에도 'Contacts'의 Fragment가 미리 로딩이 되어 자신의 Fragment로 넘어가기를 기다리기 때문에 바로 뜰 수 있는 것이지요.


 Fragment의 이러한 점은 상당히 합리적인 듯 하지만 만약 Fragment에서 메인 핸들러를 활용하여 Fragment의 UI를 계속 바꾸어 주고 있는 상황이면 어떻게 될까요? 해당 Fragment가 다른 Fragment로 전환되는 순간 자신의 Fragment에 돌리던 Thread가 종료되지 않고 계속 동작을 하다가 Fragment의 UI를 수정하는 작업에 접근하게 되고 이로인해 숨겨진 Fragment의 UI를 건드릴 경우 앱이 죽어버리는 일이 벌이집니다!


 그렇다면 해당 Thread를 어떻게 처리하면 좋을지의 방안을 생각해보면 다음과 같은 해결책을 세울 수 있습니다.


 1. Fragment 화면이 넘어가게 될 때 Thread에 Interrupt를 걸어준다.

 - 위의 방안대로 수행하게 되면 Fragment가 숨겨짐과 동시에 Thread에 인터럽트가 걸리면서 Thread가 종료됨을 볼 수 있습니다. 코드는 다음과 같이 작성해 주시면 되겠습니다.


1
2
3
4
public void onStop(){
        super.onStop();
        U.interrupt();
    }


 하지만 분명 이렇게 Thread에 Interrupt를 걸어 종료시켰음에도 불구하고 Fragment의 UI를 건드려 앱이 죽어버립니다. 이는 UI 핸들러의 Message가 Queue 방식의 구조로 되어 있어 Queue에 UI와 관련된 작업의 Message가 남아 있을 경우 Thread가 종료되었음에도 UI의 동작에 접근하게 되는 상황이 발생합니다. 이에 대한 대처 방식으로 다음과 같은 방안을 다시 마련할 수 있습니다.


 2. Flag 방식을 사용하여 UI를 건드리는 Handler의 Message를 막는다.

 - 위의 코드에서 Flag만 추가해주시면 됩니다. 


1
2
3
4
5
public void onStop(){
        super.onStop();
        state = "DeActive";
        U.interrupt();
    }


 다음은 Fragment의 Thread를 종료시키고 UI와 관련된 작업도 중단시키는 방식에 대해 다룬 코드입니다. 이렇게 설계한다면 숨겨진 Fragment의 UI와 간섭할 일을 최소한으로 줄이실 수 있을 것입니다.



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
71
public class FragmentTab3 extends Fragment {
    UIThread U;
 UIHandler u;
    String state;
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
 
        u = new UIHandler();
 
        View rootView = inflater.inflate(R.layout.fragmenttab3, container,
                false);
        
        state = "Active";
        U = new UIThread();
        U.start();
 
        return rootView;
    }
    
    private class UIThread extends Thread{
        Message msg;
        boolean loop = true;
 
        public void run() {
            try {
                while (loop) {
                    Thread.sleep(100);
 
                    if(Thread.interrupted()){ //인터럽트가 들어오면 루프를 탈출합니다.
                        loop = false;
                        break;
                    }
                    
                    msg = u.obtainMessage();
                    msg.arg1 = 1;
 
                    u.sendMessage(msg);
                }
 
            } catch (InterruptedException e) {//sleep 상태에서 인터럽트가 들어오면 exception 발생
                // TODO Auto-generated catch block
                loop = false;
            }
 
        }
    }
 
    private class UIHandler extends Handler {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.arg1) {
            case 1:
                if(state.equals("DeActive")) //Fragment가 숨겨진 상태일 때
                    break;
                //Fragment의 UI를 변경하는 작업을 수행합니다.
            }
        }
    }
    
    public void onStop(){
        super.onStop();
        state = "DeActive";
        U.interrupt();
    }
    
    public void onResume(){
        super.onResume();
        state = "Active";
    }


  • 감사 2016.05.04 18:35 ADDR 수정/삭제 답글

    정말 며칠을 고민했는데 올려주신 글 덕분에 거의 해결했습니다.
    초짜의 길은 험난하네요^^
    정말 정말 감사합니다.