1. 서비스
2. 브로드캐스트 리시버
3. 콘텐트 프로바이더
서비스
- 일반적으로 화면없이 동작하는 프로그램을 뜻함
- 데몬(daemon), 백그라운드 프로세스라고도 함
- 서비스는 백그라운드에서 실행되므로 화면과 상관없이 계속 동작함
화면이 종료되어도 계속되는 음악 서비스 만들기
- 버튼을 클릭하면 음악이 시작되고, 화면이 종료되어도 음악이 계속 흐르는 서비스
- 또한 로그캣을 활용하여 메소드가 실행되는 순서를 확인해봄
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"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btnStart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="음악서비스 시작" />
<Button
android:id="@+id/btnStop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="음악서비스 중지" />
</LinearLayout>
Service클래스를 상속받아 구현한 MusicService클래스 생성 (Kotlin클래스)
package com.cookandroid.kotlinapp0607
import android.content.Intent
import android.media.MediaPlayer
import android.os.IBinder
class MusicService : android.app.Service(){ //Service클래스를 상속받음
lateinit var mp:MediaPlayer //mp3객체를 생성
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
/*로그를 기록하는 코드*/
android.util.Log.i("서비스 테스트", "onStartCommand()")
mp = MediaPlayer.create(this, R.raw.song1)
mp.isLooping = true
mp.start() //음악 시작
return super.onStartCommand(intent, flags, startId)
}
override fun onCreate() {
/*로그를 기록하는 코드*/
android.util.Log.i("서비스 테스트", "onCreate()")
super.onCreate()
}
override fun onDestroy() {
/*로그를 기록하는 코드*/
android.util.Log.i("서비스 테스트", "onDestory()")
mp.stop() //음악 중지시키기
super.onDestroy()
}
}
MainActivity.kt
package com.cookandroid.kotlinapp0607
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
lateinit var soundIntent:Intent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//인텐트 변수를 생성하면서 MusicService클래스를 생성자에 넘김
soundIntent = Intent(this, MusicService::class.java)
btnStart.setOnClickListener {
startService(soundIntent)
android.util.Log.i("서비스 테스트","startService()")
}
btnStart.setOnClickListener {
stopService(soundIntent)
android.util.Log.i("서비스 테스트","stopService()")
}
}
}
서비스를 사용할 수 있도록 Manifest.xml에서 </activity> 아래에 추가해준다.
<service android:name=".MusicService" />
실행결과 확인하기
logcat을 통해 로그 기록을 확인할 수 있다. [음악서비스 시작]버튼을 누른 경우, startService() -> onCreate() -> onStartCommand() 순서로 메소드가 실행됨을 확인할 수 있다.
[음악서비스 중지]버튼을 누른 경우, stopService() -> onDestroy() 순서로 실행이 된다.
실행하는 동안 앱을 완전히 종료하지 않고 홈버튼을 눌러 화면만 나가게 되면 음악은 계속 들린다.
=> '서비스'의 기능!
(그러나 다른 응용프로그램을 종료하지 않고 덮어쓰기식으로 재생이 된다. 😐)
브로드캐스트 리시버
- 안드로이드는 문자 메시지 도착, 배터리 방전, SD카드 탈부착, 네트워크 환경 변화 등이 발생하면 방송(브로드캐스트) 신호를 보내는데, 이런 신호를 받아서 처리하는 것이 브로드캐스트 리시버임
- 브로드캐스트 리시버의 대표적인 응용은 배터리 상태 확인
- 배터리 상태와 관련된 액션 ▼
액션 | 설명 |
ACTION_BATTERY_CHANGED | 배터리 상태가 변경될 때 |
ACTION_BATTERY_LOW | 배터리가 거의 방전되었을 때 |
ACTION_BATTERY_OKAY | 배터리가 방전 상태에서 정상 수준으로 돌아왔을 때 |
배터리 상태를 표시하는 앱 만들기
- 배터리 상태가 변할 때마다 충전 상태 이미지와 배터리 상태를 출력
- 명령 프롬프트에서 AVD의 배터리 상태를 강제로 변경
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"
android:orientation="vertical"
tools:context=".MainActivity2">
<ImageView
android:id="@+id/imgBattery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:srcCompat="@drawable/battery_0" />
<EditText
android:id="@+id/edtBattery"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:enabled="false"
android:inputType="textPersonName"
android:text="Name" />
</LinearLayout>
MainActivity.kt
package com.cookandroid.kotlinapp0607
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main2.*
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
title = "브로드캐스트 리시버"
}
//브로드캐스트 리시버의 정보는 intent에 포함되어있음
var br:BroadcastReceiver = object : BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
var action= intent?.action
if(action==Intent.ACTION_BATTERY_CHANGED){
/*인텐트 엑스트라에서 배터리의 잔량을 추출*/
var remain = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/*배터리 잔량 표시*/
edtBattery.setText("현재 충전량 : $remain\n")
/*잔량에 따라 배터리 이미지 변경*/
if (remain != null) {
if(remain >= 90)
imgBattery.setImageResource(R.drawable.battery_100)
else if(remain >= 70)
imgBattery.setImageResource(R.drawable.battery_80)
else if(remain >= 50)
imgBattery.setImageResource(R.drawable.battery_60)
else if(remain >= 30)
imgBattery.setImageResource(R.drawable.battery_20)
else
imgBattery.setImageResource(R.drawable.battery_0)
}
/*인텐트의 엑스트라에서 배터리의 전원 연결 상태를 추출*/
var plug = intent?.getIntExtra(BatteryManager.EXTRA_PLUGGED,0)
/*배터리 전원 연결 상태 표시*/
when(plug){
0->edtBattery.append("전원 연결 : 안됨")
BatteryManager.BATTERY_PLUGGED_AC -> edtBattery.append("전원 연결 : 어댑터 연결됨")
BatteryManager.BATTERY_PLUGGED_USB -> edtBattery.append("전원 연결 : USB 연결됨")
}
}
}
}
override fun onResume() {
super.onResume()
/*인텐트 필터를 생성*/
var iFilter = IntentFilter()
/*ACTION_BATTERY_CHANGED액션을 추가*/
iFilter.addAction(Intent.ACTION_BATTERY_CHANGED)
/*br에 등록*/
registerReceiver(br, iFilter)
}
override fun onPause() {
super.onPause()
/*등록된 br을 해제*/
unregisterReceiver(br)
}
}
실행결과 확인하기
잔여 배터리에 따라 상태가 변하는 것을 확인하기 위해 AVD의 설정에서 배터리와 전원 연결 상태를 변경할 수 있다.
콘텐트 프로바이더
- 안드로이드는 보안상 앱에서 사용하는 데이터를 외부에서 접근할 수 없음 (그냥은 안됨)
- 파일이나 데이터베이스를 외부 앱에서 사용하도록 하려면 콘텐트 프로바이더를 만들어서 외부로 제공
URI
- 콘텐트 프로바이더에서 제공하는 데이터에 접근하기 위한 주소
- "content://패키지명/경로/아이디" 형식으로 지정
안드로이드에서 통화 기록을 가져오는 예제
- AVD에서 통화 버튼을 눌러서 통화 기록을 몇 건 남겨놓음
- 통화 기록에 접근하기 위해 AndroidManifest.xml의 <application 위에 다음 코드를 추가하여 접근 권한을 줌
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
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"
android:orientation="vertical"
tools:context=".MainActivity3">
<Button
android:id="@+id/btnCall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="통화 기록 가져오기" />
<EditText
android:id="@+id/editCall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textMultiLine"
android:text="Name" />
</LinearLayout>
MainActivity.kt
package com.cookandroid.kotlinapp0607
import android.app.Activity
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.CallLog
import android.telecom.Call
import androidx.core.app.ActivityCompat
import kotlinx.android.synthetic.main.activity_main3.*
import java.text.SimpleDateFormat
import java.util.*
import java.util.jar.Manifest
class MainActivity3 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main3)
/*접근 권한 허용*/
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.READ_CALL_LOG),
Context.MODE_PRIVATE
)
/*통화기록을 띄울 editText 세팅*/
btnCall.setOnClickListener {
editCall.setText(findCallHistory()) //findCallHistory()함수 호출
}
}
fun findCallHistory(): String {
var callSet = arrayOf( //날짜, 착/발신 정보, 전화번호, 전화시간(초) 정보를 가져옴
CallLog.Calls.DATE, CallLog.Calls.TYPE,
CallLog.Calls.NUMBER, CallLog.Calls.DURATION
)
/*.query()는 sql문 쿼리와 유사하게 작동함*/
/*SELECT callSet
* FROM CallLog.Calls.CONTENT_URI
* WHERE null,null
* ORDER BY null*/
var c = contentResolver.query(CallLog.Calls.CONTENT_URI, callSet, null, null, null)
if (c != null) {
if (c.count == 0)
return "통화기록 없음"
}
var callBuff = StringBuffer()
callBuff.append("\n날짜 : 구분 : 전화번호 : 통화시간\n\n")
c!!.moveToFirst() //커서가 현재 맨 뒤에 있음 -> 처음으로 옮겨옴
do {
/*날짜(0) - 포멧처리해서 버퍼에 넣기*/
var callDate = c!!.getLong(0)
var datePattern = SimpleDateFormat("yyyy-MM-dd")
var date_str = datePattern.format(Date(callDate))
callBuff.append("$date_str:")
/*타입(1) - 착신인지 발신인지 구분해서 버퍼에 넣기*/
if (c.getInt(1) == CallLog.Calls.INCOMING_TYPE)
callBuff.append("착신 : ")
else
callBuff.append("발신 : ")
/*번호(2)*/
callBuff.append(c!!.getString(2) + ":")
/*통화시간(3)*/
callBuff.append(c!!.getString(3) + "초\n")
} while (c!!.moveToNext()) //커서를 한 칸씩 옮겨주는 것을 반복함
c.close()
return callBuff.toString()
}
}
'Android > Kotlin App' 카테고리의 다른 글
[Kotlin App] 여러 mp3파일을 재생하는 mp3 Player 만들기 (1) | 2021.06.08 |
---|---|
[Kotlin App] 파일 처리 (1) | 2021.05.11 |
[Kotlin App] DatePickerDialog / TimePickerDialog (0) | 2021.05.04 |
[Kotlin App] Action Bar와 Flagment (0) | 2021.05.04 |
[Kotlin App] TabHost (0) | 2021.05.03 |
댓글