Thread
< Smart phone의 자원의 제약 / 네트워크의 불안정성>
** Type
1. Thread Class
2. Runnable Interface
3. Callable Interface
Main Thread : UI 갱신 가능 > UI Thread
> Daemon O : Deamon 이 아닌 스레드가 없으면 자동 종료됨
> Daemon X : 작업이 종료 되거나 Interrupted Exception이 발생해야 한다.
그 이외 : 화면 갱신 가능
핸들러는 화면 갱신 하는 것이 아닌, 작업을 메인 쓰레드에게 요청!
서버에서 데이터 받아서 화면 갱신을 원하면, Thread 통해 데이터를 다 받으면 Handler에게 메시지를 보냄
> Handler 가 Main thread 에게 요청을 보내도록 만들어야한다
> 이 때 요청을 전송 할 때
1. Send Message : 순서대로 처리 > 바로 바로 처리 원할 때 사용
2. Post Message : 다른 작업이 없을 때 처리 > 급하지 않을 때 사용
현재 Deprecated : Async task > 비동기, Thread 가 아니지만 Thread 형태로..
순서대로 되지 않음
- 상호배제
- Semaphore 공유 자원
- 생산자 소비자
Java util concurrent > 1.7 부터 사용
메인 스레드에서 동기적으로 화면 갱신하는 코드를 작성하면 모아서 한꺼번에 나중에 처리합니다.
> 거의 모든 GUI 시스템이 동일
// 1초 씩 쉬면서 1-10 까지 TextView 에 출력
var i = 0
while(i<10){
i = i + 1
Thread.sleep(1000)
resultView.setText("i:${i}")
Log.e("i", "${i}")
}
Thread 생성
1. Thread 클래스로부터 상속 받는 방법
1) Thread 클래스로부터 상속 받는 클래스를 생성하고 run 이라는 메소드에 스레드로 수행할 내용을 작성
2) 인스턴스를 만들어서 Start() 를 호출해주면 됩니다.
2. Runnable 인터페이스를 구현하는 방법
1) Runnable 인터페이스를 구현한 클래스를 생성하고 run 이라는 메소드에 스레드로 수행할 내용을 작성
2) 인스턴스를 생성
3) Thread 클래스의 인스턴스를 생성할 때 Runnable 인스턴스를 대입하고 start()를 호출
Handler
Thread 간에 Message나 Runnable 인스턴스를 통해서 Message를 주고 받는 장치
하나의 스레드와 관련을 맺어서 Main Thread 에게 메시지를 요청해서 작업을 수행하도록 해주는 클래스
1. Handler 를 생성
Handler(Looper.getMainLooper())
2. 핸들러를 호출 - 순서대로 수행되도록 호출
Handler 인스턴스.sendMessage(Message)
Handler 인스턴스.sendMessageAtFrontOfQueue(Message)
Handler 인스턴스.sendMessageAtTime(Message, Long) - Long 에 해당하는 시간에 작업
Handler 인스턴스.sendMessageDelayed(Message, Long) - Long 에 해당하는 시간 만큼 Delay
Handler 인스턴스.sendEmptyMessage(what:Int): 번호만 전달해서 호출
3. Message 클래스
Handler 의 handleMessage 메소드에 전달할 데이터 클래스
what : Int -> 구분하기 위한 정수
arg1 : Int -> 데이터
arg2 : Int -> 데이터
obj : Any -> 데이터
replyTo : Message -> 응답을 받을 객체를 지정
4. 핸들러가 메시지를 받았을 때 어떤 동작을 수행하도록 하고자 하면 handleMessage(msg:Message) 를 overriding 해야 합니다.
이 메소드에서 데이터를 받아서 출력하는 역할을 수행합니다.
주기적으로 UI 갱신 하기 위해
1. 핸들러가 있어야 한다
// inner 를 붙인 이유는 외부 클래스에 선언된 멤버를 사용하기 위해서 입니다.
inner class ThreadEx:Thread(){
override fun run(){
var i = 0
while(i<10){
i = i + 1
Thread.sleep(1000)
//resultView.text = "i=${i}"
Log.e("i", "${i}")
}
}
}
// 메시지 생성
val msg = Message()
//val msg.what = i // 정수는 what, arg1, arg2, obj 모두 사용 가능
msg.obj = "$i={i}"
// 핸들러에게 메시지 전송
handler.sendMessage(msg)
TelephonyManager
전화기의 상태변화를 감지하기 위한 클래스
ConnectivityManager
인터넷 연결 상태 변화를 감지하기 위한 클래스
WiFiManager 클래스도 제공
activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="@+id/resultView"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="다운로드"
android:id="@+id/download"
android:textSize="30sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="@+id/display"
android:textSize="30sp"/>
</LinearLayout>
ActivityMain.kt
package kr.co.tjoeun.lotto
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
// inner 를 붙인 이유는 외부 클래스에 선언된 멤버를 사용하기 위해서 입니다.
inner class ThreadEx:Thread(){
override fun run(){
var i = 0
while(i<10){
i = i + 1
Thread.sleep(1000)
//resultView.text = "i=${i}"
Log.e("i", "${i}")
}
}
}
inner class RunnableImpl : Runnable{
override fun run() {
var i = 0
while(i<10){
i = i + 1
Thread.sleep(1000)
//resultView.text = "i=${i}"
// 메시지 생성
val msg = Message()
//val msg.what = i // 정수는 what, arg1, arg2, obj 모두 사용 가능
msg.what = 1 // 다른 스레드와 구분하기 위해서 what 에 어떤 값을 대입
msg.obj = "$i={i}"
// 핸들러에게 메시지 전송
handler.sendMessage(msg)
//Log.e("i", "${i}")
}
}
}
// 핸들러 생성
// kotlin 에서 anonymous 만들기
val handler : Handler = object : Handler(Looper.getMainLooper()){
// 핸들러에게 메시지가 전송 되면 호출 되는 메소드
override fun handleMessage(msg : Message) {
// 전달된 데이터를 읽어서 출력
if(msg.what == 1) {
val str = msg.obj as String
resultView.setText(str)
}
}
}
inner class DownloadThread : Thread(){
override fun run(){
// 다운로드 받는 코드
Thread.sleep(15000)
val msg = Message()
msg.obj = "다운로드 완료"
downloadHandler.sendMessage(msg)
}
}
val downloadHandler = object : Handler(Looper.getMainLooper()){
override fun handleMessage(msg:Message) {
// 다운로드가 끝났을 때 수행할 내용
val data = msg.obj as String
resultView.text = data
}
}
var value = 0
// 핸들러가 다시 핸들러를 호출하는 구조
var handlerCall : Handler = object:Handler(Looper.getMainLooper()){
override fun handleMessage(msg : Message) {
value = value + 1
resultView.text = "i=${value}"
Thread.sleep(1000)
if(value < 100){
this.sendEmptyMessage(0)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
download.setOnClickListener {
resultView.text = "다운로드 시작"
DownloadThread().start()
handlerCall.sendEmptyMessage(0)
}
// 1초 씩 쉬면서 1-10 까지 TextView 에 출력
/*
var i = 0
while(i<10){
i = i + 1
Thread.sleep(1000)
resultView.setText("i:${i}")
Log.e("i", "${i}")
}
// Thread 로 부터 상속 받은 경우
ThreadEx().start()
// Runnable 을 구현한 경우
var r = RunnableImpl()
val th = Thread(r)
th.start()
*/
}
}
서버와의 통신 방법
1. Socket 을 직접 생성하는 Low Level 통신
=> Web Server 나 WebSoket Server 보다는 효율이 좋음
=> Server 구축이 어려움
2. Web Server 와 통신
=> Server 구축이 일반적으로 Socket 보다는 쉽다.
=> 실시간 단문 메시지를 주고받는 통신에는 부적합
3. WebSocket Server 와 통신
=> Socket 과 Web Server 의 중간 정도 효율
4. 센서(Bluetooth, NFC, Beacon...)를 이용한 통신
스마트 폰은 외부 데이터 베이스 직접 연결이 안됩니다.
구글의 Firebase 를 하면 별도의 서버 구축없이 서버의 역할을 수행하도록 할 수 있습니다.
아마존 서비스도 많이 이용합니다.
=> node 나 java 로 많이 구현을 합니다.
Web Server 와의 통신
1. URL 클래스를 이용해서 접속할 웹서버의 URL 을 생성
=> 파라미터에 한글이 있으면 URLEncoder.encode 를 이용해서 인코딩을 해야합니다.
2. URL 클래스의 openConnection을 호출해서 HttpURLConnection 을 가져옵니다.
openConnection의 리턴타입이 URLConnection 이므로 강제 형 변환 해야합니다.
3. 가져온 HttpURLConnection을 가지고 옵션을 설정
=> Method(GET, POST)
=> Header 설정
=> POST 방식인 경우 파라미터를 별도의 방법으로 설정
=> 파일이 있는 경우 파일 업로드를 구현
4. HttpURLConnection 의 getInputStream 과 getOutputStream 이라는 메소드를 이용해서 InputStream(메시지 전송이나 업로드) 과 OutputStream(Download)를 제공
=> 받아온 스트림을 이용해서 바이트 스트림(파일)이나 문자스트림(텍스트)을 생성해서 통신
5. 가져온 데이터를 파싱해서 사용
=> csv(구분 문자로 구분된 텍스트), html, xml(태그형태로 만든 데이터 포맷), json(자바 스크립트 데이터 표현법으로 만든 데이터 포맷 - 스마트 폰에서는 필수)
스마트폰 <-> 스마트폰
스마트폰 <-> 컴퓨터
위의 모델은 통신이 불가능
스마트폰 <-> 운영체제 서버 (구글이나 애플) <-> 애플리케이션 서버
운영체제 서버에서는 스마트폰과 애플리케이션을 함쳐서 특정 스마트폰의 애플리케이션으로 구분합니다.
구글과 애플이 보내는 메시지 포맷이 json입니다.
그리고 광고도 json 형태로 데이터가 전송 됩니다.
구글의 서비스를 FCM (Fire Cloud Message) 이라고 합니다.
애플의 서비스를 APNS 라고 합니다.
6. 인터넷 권한이 설정 되어야 합니다.
7. http 가 아닌 컴퓨터에 접속할 때는 설정을 추가
=> AndroidManifest.xml 파일의 application 태그에
android:usesCleartextTraffic = "true" 를 추가해야 합니다.
8. 네트워크에 접속하는 코드는 스레드에 작성되어야 하고 네트워크에 접속해서 데이터를 가져오는 것은 화면에 표시를 해주는 것이 좋습니다.
ProgressBar 를 화면에 출력하는 것이 좋습니다.되도록이면 deamon thread 로 만드는 것이 좋습니다.
html 가져오기, 이미지를 다운로드 -> 로컬에 저장 html 파싱, xml 파싱, json 파싱, POST 방식으로 파라미터를 전송, 파일 업로드
'안드로이드' 카테고리의 다른 글
안드로이드 : 영화 소개 페이지 웹 크롤링 (0) | 2020.11.05 |
---|---|
안드로이드 : 웹 서버 연결 (0) | 2020.11.04 |
안드로이드 : 액티비티와 인텐트 (0) | 2020.11.02 |
안드로이드 (0) | 2020.10.29 |
안드로이드 : 목록 대화 상자 (0) | 2020.10.28 |