반응형

Message, Handler, Looper를 이용해 메인 스레드에서 다른 스레드로 메세지(데이터)를 보내자.

 

레이아웃에 에디트텍스트, 버튼, 텍스트뷰를 적당히 배치한다.

 

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
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.handler.sendMessage(message);
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        Handler handler;
 
        @Override
        public void run() {
            handler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(@NonNull Message msg) {
                    textView.setText(msg.obj + " from My Thread.");
                    // 메인 스레드의 메인 루퍼를 사용하기 때문에
                    // textView()에 바로 접근 가능하다.
                }
            };
        }
    }
}
 

 

메인 루퍼를 이용하는 소스를 작성하고 빌드한다.

 

에디트텍스트에 메세지를 입력하고 버튼을 터치하면 텍스트뷰에 표시된다.

 

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
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.handler.sendMessage(message);
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        Handler handler;
 
        @Override
        public void run() {
            Looper.prepare();
 
            handler = new Handler(Looper.myLooper()) {
                // 메인 스레드가 아닌 새로 만든 이 스레드의 루퍼 사용
                @Override
                public void handleMessage(@NonNull Message msg) {
                    String message = msg.obj.toString();
                    // 새로 만든 이 스레드로 넘어온 메세지 데이터를 미리 처리.
                    // 아래 setText()에서 msg.obj를 사용하면 null 값이 대입된다.
 
                    textView.post(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText(message + " from My Thread");
                        }
                    });
                }
            };
 
            Looper.loop();
        }
    }
}
 

 

메인 루퍼가 아닌 새로 만든 스레드의 루퍼를 생성하고 메세지를 처리하는 소스. 결과는 동일하다.

 

 

(루퍼 종료용)버튼을 하나 더 추가한다.

 

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
91
92
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.myHandler.sendMessage(message);
            }
        });
 
        Button button2 = findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myThread.myHandler.StopLooper();
                // 새로 만든 스레드의 루퍼 종료.
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        MyHandler myHandler;
 
        @Override
        public void run() {
            Looper.prepare();
 
            myHandler = new MyHandler();
 
            Looper.loop();
        }
 
        class MyHandler extends Handler {
            MyHandler() {
                super(Looper.myLooper());
            }
 
            @Override
            public void handleMessage(@NonNull Message msg) {
                String message = msg.obj.toString();
                // 새로 만든 이 스레드로 넘어온 메세지 데이터를 미리 처리.
                // 아래 setText()에서 msg.obj를 사용하면 null 값이 대입된다.
 
                textView.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText(message + " from My Thread");
                    }
                });
            }
 
            void StopLooper() {
                myHandler.getLooper().quitSafely();
                // 새로 만든 스레드의 루퍼 종료.
            }
        }
    }
}
 

 

새 스레드의 루퍼를 종료하는 함수가 포함된 파생 Handler 클래스를 사용하는 소스.

Looper.XXX() 호출 시 MyThread 클래스 내부라도 어디서 루퍼를 참조 하느냐에 따라 메인 루퍼가 참조 될 수도 있고 새 스레드의 루퍼가 참조 될 수 있다. (MyThread.Run()에서만 새 스레드의 루퍼가 참조된다)

 

스레드 루퍼 종료 버튼 터치 후 다시 스레드로 보내기 버튼을 터치하면 아래와 같은 경고 발생.

 

새 스레드의 루퍼가 종료(별 다른 작업이 없다면 동시에 새 스레드도 종료) 된 후 핸들링할 메세지를 보내기 때문에 경고가 발생한다. (예외 처리 필요)

 

※ 참고

Looper

 

반응형
Posted by J-sean
:
반응형

안드로이드 UI는 메인 스레드나 UI 스레드 외 다른 스레드에서 조작 할 수 없다. 다른 스레드에서 UI 조작 시 별다른 에러 없이 Virtual Device에서 잘 작동 되더라도 실제 기기에서는 종료되어 버린다. 아니면 테스트 시 Logcat에 아래와 같은 에러가 표시된다.

 

CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

 

텍스트뷰와 버튼을 적당히 배치한다.

 

다른 스레드에서 텍스트뷰에 접근하는 코드를 작성한다.

 

위 예제의 경우 AVD에서는 별 이상없이 동작 하지만 실제 기기에서 버튼을 터치하면 바로 종료되어 버린다. 메인스레드나 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
package com.example.myapplication;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    TextView textView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MyThread thread = new MyThread();
                thread.start();
            }
        });
    }
 
    class MyThread extends Thread {
        @Override
        public void run() {
            textView.post(new Runnable() {
                @Override
                public void run() {
                    textView.setText("My Thread.");
                }
            });
        }
    }
}
 

 

위와 같이 소스를 수정하고 빌드한다.

 

문제없이 잘 실행된다.

 

 

1
2
3
4
5
6
7
8
9
10
11
class MyThread extends Thread {
    @Override
    public void run() {
        textView.postDelayed(new Runnable() {
            @Override
            public void run() {
                textView.setText("My Thread");
            }
        }, 5000);
    }
}
 

 

.post()가 아닌 .postDelayed()를 사용하면 Thread.sleep()를 사용하지 않고 일정시간 후 실행할 수 있다. 위 예제는 버튼 터치 5초 후 텍스트를 바꾼다.

 

다른 스레드에서 UI 스레드에 접근 하는 방법은 여러가지가 있다. 아래 링크를 참고하자.

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

프로세스 및 스레드 개요

 

반응형
Posted by J-sean
:
반응형

구글 안드로이드 맵 SDK를 사용해 보자.

 

Google Play services가 설치되어 있지 않다면 설치한다.

 

play-services-maps를 추가한다.

 

AndroidManifest.xml에 API KEY와 GMS(Google Mobile Services) 버전 확인하는 메타 데이터를 추가한다.

 

레이아웃에 fragment를 추가한다.

 

 

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
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
 
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
 
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }
 
    @Override
    public void onMapReady(@NonNull GoogleMap googleMap) {
        LatLng curPosition = new LatLng(37.3850143127.1234308);
 
        googleMap.addMarker(new MarkerOptions().position(curPosition).title("Marker"));
        googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(curPosition, 16));
    }
}
 

 

소스를 입력하고 빌드한다.

 

지정한 위치에 마커가 표시되고 카메라가 이동한다.

※ 참고

Android용 Maps SDK 개요

 

반응형
Posted by J-sean
:
반응형

안드로이드 앱을 위한 리눅스(우분투) 데이터베이스(MySQL/MariaDB) 서버를 만들어 보자.

 

app/res/xml/network_security_config.xml 파일을 작성한다.

 

AndroidManifest.xml 에 인터넷 권한과 networkSecurityConfig를 추가한다.

 

리니어 레이아웃에 에디트텍스트, 버튼, 텍스트뷰를 적당히 배치한다.

 

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
91
92
93
94
package com.example.myapplication;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText1;
    EditText editText2;
    TextView textView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText1 = findViewById(R.id.editText1);
        editText2 = findViewById(R.id.editText2);
        textView = findViewById(R.id.textView);
 
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String id = editText1.getText().toString();
                String name = editText2.getText().toString();
 
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        RequestData(id, name);
                    }
                }).start();
            }
        });
    }
 
    public void RequestData(String id, String name) {
        try {
            BufferedReader bufferedReader;
            StringBuilder stringBuilder = new StringBuilder();
 
            String urlString = "http://192.168.171.200/data_check.php";
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            if (conn != null) {
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                conn.setRequestMethod("POST");
                conn.setDoInput(true);
                conn.setDoOutput(true);
                conn.connect();
 
                String parameter = "id=" + id + "&name=" + name;
                OutputStream outputStream = conn.getOutputStream();
                outputStream.write(parameter.getBytes(StandardCharsets.UTF_8));
                outputStream.flush();
                outputStream.close();
 
                int responseCode = conn.getResponseCode();
 
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                } else {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
                }
 
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line + "\n");
                }
 
                textView.setText(stringBuilder);
 
                bufferedReader.close();
                conn.disconnect();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 

 

서버에 데이터를 요청하고 표시하는 소스를 작성한다. (MainActivity.java)

 

 

리눅스(우분투) 서버 데이터베이스에 위와 같은 테이블과 데이터를 생성한다.

 

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
<?php
    $mysql_host = "localhost";
    $mysql_user = "root";
    $mysql_password = "1234";
    $mysql_db = "member_db";
 
    $conn = mysqli_connect($mysql_host$mysql_user$mysql_password$mysql_db);
 
    if (!$conn) {
        die("Database Connect Error: " . mysqli_connect_error());
    }
 
    $android = strpos($_SERVER['HTTP_USER_AGENT'], "Android");
 
    if ((($_SERVER['REQUEST_METHOD'== 'POST'&& isset($_POST['submit'])) || $android)
    {
        $post_id = trim($_POST['id']);
        $post_name = trim($_POST['name']);
 
        $sql = "SELECT * FROM member WHERE id='$post_id' AND name='$post_name'";
        $result = mysqli_query($conn$sql);
 
        if (mysqli_num_rows($result== 1 ) {
            $member = mysqli_fetch_assoc($result);
            echo "ID: " . $member['id'] . " Name: " . $member['name'] . " Age: " . $member['age'];
        } else {
            echo "No Data.";
            mysqli_close($conn);
            exit;
        }
    } else {
        echo "No Android Device Detected.";
    }
 
    mysqli_close($conn);
?>
 

 

아파치 서버 디렉토리에 안드로이드 앱 요청을 처리할 PHP 파일을 작성한다. (/var/www/html/data_check.php)

 

앱을 실행하고 정보를 요청하면 Age 데이터가 표시된다.

 

잘못된 정보 요청.

 

 

 

안드로이드가 아닌 다른 운영체제로 서버에 접속.

※ 참고

SELinux 유효성 검사

 

반응형
Posted by J-sean
:
반응형

네이버 클라우드 플랫폼의 리버스 지오코딩을 사용해 보자.

 

2022.02.05 - [Android] - Naver Geocoding For Android 안드로이드 네이버 지오코딩

위 링크를 참고해 인터넷 권한과 network_security_config 옵션을 설정한다.

 

리니어 레이아웃에 텍스트뷰와 버튼을 적당히 배치한다.

 

JSON 타입의 주소 정보를 처리하기 위해 관련 클래스를 작성한다.

주소 정보 관련 클래스는 아래 파일을 사용하자.

myapplication.zip
0.00MB

 

JSON 데이터를 처리하기 위해 gson을 추가한다.

 

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
91
92
package com.example.myapplication;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
 
import com.google.gson.Gson;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
 
public class MainActivity extends AppCompatActivity {
 
    TextView textView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        textView = findViewById(R.id.textView);
 
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        requestGeocode();
                    }
                }).start();
            }
        });
    }
 
    public void requestGeocode() {
        try {
            BufferedReader bufferedReader;
            StringBuilder stringBuilder = new StringBuilder();
            String coord = "127.1234308,37.3850143";
            String query = "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc?request=coordsToaddr&coords="
                    + coord + "&sourcecrs=epsg:4326&output=json&orders=roadaddr&output=xml";
            URL url = new URL(query);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            if (conn != null) {
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                conn.setRequestMethod("GET");
                conn.setRequestProperty("X-NCP-APIGW-API-KEY-ID""Client ID");
                conn.setRequestProperty("X-NCP-APIGW-API-KEY""Client Secret");
                conn.setDoInput(true);
 
                int responseCode = conn.getResponseCode();
 
                if (responseCode == 200) {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                } else {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
                }
 
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line + "\n");
                }
                //textView.setText(stringBuilder);
 
                Gson gson = new Gson();
                Address address = gson.fromJson(String.valueOf(stringBuilder), Address.class);
 
                String finalAddress = null;
                finalAddress = address.results[0].region.area1.name;
                finalAddress += address.results[0].region.area2.name;
                finalAddress += address.results[0].region.area3.name;
                finalAddress += address.results[0].region.area4.name;
                finalAddress += address.results[0].land.addition0.value;
 
                textView.setText(finalAddress);
 
                bufferedReader.close();
                conn.disconnect();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 

 

소스를 입력하고 빌드한다. 주소로 변환할 GPS 좌표는 (127.1234308, 37.3850143) 이다.

 

 

앱을 실행하고 버튼을 터치하면 GPS 좌표가 주소로 변환되어 표시된다.

아래와 같은 양식의 curl 명령으로도 같은 JSON 데이터를 얻을 수 있다.

curl "https://naveropenapi.apigw.ntruss.com/map-reversegeocode/v2/gc?request=coordsToaddr&coords=127.1234308,37.3850143&sourcecrs=epsg:4326&output=json&orders=roadaddr&output=xml" -H "X-NCP-APIGW-API-KEY-ID: Client ID" -H "X-NCP-APIGW-API-KEY: Client Secret" -v

-v, --verbose
Makes curl verbose during the operation. Useful for debugging and seeing what's going on "under the hood". A line starting with '>' means "header data" sent by curl, '<' means "header data" received by curl that is hidden in normal cases, and a line starting with '*' means additional info provided by curl.

If you only want HTTP headers in the output, --include might be the option you are looking for.
If you think this option still does not give you enough details, consider using --trace or --trace-ascii instead.
This option is global and does not need to be specified for each use of -:, --next.
Use --silent to make curl really quiet.
Example:
 curl --verbose https://example.com

 

반응형
Posted by J-sean
:
반응형

네이버 클라우드 플랫폼의 지오코딩을 사용해 보자.

 

xml 리소스 폴더를 만들고 network_security_config.xml 파일을 생성한다.

 

AndroidManifest.xml에 인터넷 권한과 networkSecurityConfig 옵션을 추가한다.

 

리니어 레이아웃에 텍스트뷰와 버튼을 적당히 배치한다.

 

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
91
package com.example.myapplication;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
 
public class MainActivity extends AppCompatActivity {
 
    TextView textView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        textView = findViewById(R.id.textView);
 
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        requestGeocode();
                    }
                }).start();
            }
        });
    }
 
    public void requestGeocode() {
        try {
            BufferedReader bufferedReader;
            StringBuilder stringBuilder = new StringBuilder();
            String addr = "분당구 성남대로 601";
            String query = "https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query=" + URLEncoder.encode(addr, "UTF-8");
            URL url = new URL(query);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            if (conn != null) {
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                conn.setRequestMethod("GET");
                conn.setRequestProperty("X-NCP-APIGW-API-KEY-ID""Client ID");
                conn.setRequestProperty("X-NCP-APIGW-API-KEY""Client Secret");
                conn.setDoInput(true);
 
                int responseCode = conn.getResponseCode();
 
                if (responseCode == 200) {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                } else {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
                }
 
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line + "\n");
                }
                //textView.setText(stringBuilder);
 
                int indexFirst;
                int indexLast;
 
                indexFirst = stringBuilder.indexOf("\"x\":\"");
                indexLast = stringBuilder.indexOf("\",\"y\":");
                String x = stringBuilder.substring(indexFirst + 5, indexLast);
 
                indexFirst = stringBuilder.indexOf("\"y\":\"");
                indexLast = stringBuilder.indexOf("\",\"distance\":");
                String y = stringBuilder.substring(indexFirst + 5, indexLast);
 
                textView.setText("X: " + x + ", " + "Y: " + y);
 
                bufferedReader.close();
                conn.disconnect();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 

 

MainActivity.java에 위 소스를 입력하고 빌드한다. '분당구 성남대로 601'의 주소 정보를 여러가지 데이터가 포함된 JSON 포맷으로 가져와 간단한 문자열 조작으로 GPS 좌표만 추출한다.

 

 

앱을 실행시키고 버튼을 터치하면 '분당구 성남대로 601'의 GPS 좌표가 표시된다.

 

구글 맵에서 GPS 좌표를 확인해 보자. 서현역이다.

 

아래와 같은 양식의 curl 명령으로도 동일한 JSON 데이터를 얻을 수 있다.

curl -G "https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query=%EB%B6%84%EB%8B%B9%EA%B5%AC%20%EC%84%B1%EB%82%A8%EB%8C%80%EB%A1%9C%20601" -H "X-NCP-APIGW-API-KEY-ID: Client ID" -H "X-NCP-APIGW-API-KEY: Client Secret"

※ query에는 주소를 url encoding한 값을 대입한다.

분당구 성남대로 601 = %EB%B6%84%EB%8B%B9%EA%B5%AC%20%EC%84%B1%EB%82%A8%EB%8C%80%EB%A1%9C%20601

 

아래와 같은 양식으로 웹브라우저 주소창에 입력해도 동일한 JSON 데이터를 얻을 수 있다.

https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode?query=분당구 성남대로 601&X-NCP-APIGW-API-KEY-ID=[Client ID]&X-NCP-APIGW-API-KEY=[Client Secret]

 

 

반응형
Posted by J-sean
:
반응형

2022.02.05 - [Android] - Naver Mobile Dynamic Map For Android 안드로이드 네이버 모바일 다이나믹 맵 1

 

지도에 마커와 중심점을 추가해 보자.

 

onMapReady()에 마커와 중심점을 정의한다.

 

빌드하고 실행하면 지정된 위치에 마커와 중심점이 표시된다.

 

반응형
Posted by J-sean
:
반응형

네이버 클라우드 플랫폼에서 제공하는 지도 서비스를 이용해 안드로이드 앱을 만들어 보자.

 

Empty Activity를 선택하고 프로젝트를 만든다.

 

settings.gradle에 저장소를 추가한다.

 

build.gradle에 네이버 지도 SDK 의존성을 추가한다.

 

Sync Now를 클릭한다.

 

 

AndroidManifest.xml에 클라이언트 ID를 지정한다.

 

앱 레이아웃에 MapFragment를 추가한다.

 

빌드하고 실행하면 기본 위치의 지도가 표시된다.

 

MainActivity.java의 onCreate()를 수정하고 onMapReady()를 추가한다.

 

 

빌드하고 실행하면 원하는 위치의 지도가 표시된다.

 

반응형
Posted by J-sean
: