Chapter 14 서비스

Ruinak·2021년 9월 5일
0

Android

목록 보기
15/15
post-thumbnail

1. 서비스

  • 서비스(service)는 일반적으로 화면 없이 동작하는 프로그램을 말합니다.
  • 다른 말로는 데몬(daemon), 백그라운드 프로세스(background process)라고도 합니다.
  • 액티비티 응용 프로그램은 화면(액티비티)이 종료되면 동작하지 않지만 서비스는 백그라운데서 실행되므로 화면과 상관없이 계속 동작합니다.
  • 서비스의 생명주기는 액티비티의 생명주기보다 단순하며 아래와 같습니다.
  • 서비스 요청이 시작되면 onCreate( ), onStartCommand( )가 동작하고 서비스가 계속됩니다.
  • 서비스 중지 요청을 받으면 onDestroy( )가 동작하여 서비스가 종료됩니다.
  • 안드로이드 서비스의 가장 일반적인 예는 음악 플레이어처럼 화면에 보이지 않아도 음악이 계속 재생되는 것입니다.
  • 그 외 필요한 서비스는 프로그래머가 직접 정의하고 실행할 수 있습니다.

실습 14-1 서비스 테스트 앱 만들기

예제 14-1 activity_main.xml 코드

<androidx.appcompat.widget.LinearLayoutCompat
    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:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btnStart"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="음악서비스 시작" />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btnStop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="음악서비스 종료" />

</androidx.appcompat.widget.LinearLayoutCompat>

예제 14-2, 3 MusicService.java 코드

public class MusicService extends Service {

    private MediaPlayer mediaPlayer;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        Toast("음악서비스 준비중");
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        Toast("음악 중지");
        mediaPlayer.stop();
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast("음악 재생");
        mediaPlayer = MediaPlayer.create(this, R.raw.darkside);
        mediaPlayer.setLooping(true);
        mediaPlayer.start();
        return super.onStartCommand(intent, flags, startId);
    }
    
    public void Toast(String msg){
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

예제 14-4 MainActivity.java 코드

public class MainActivity extends AppCompatActivity {

    private Intent intent;
    private Button btnStart, btnStop;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("서비스 테스트 예제");

        init();
        initLr();
    }

    public void init(){
        intent = new Intent(this, MusicService.class);
        btnStart = findViewById(R.id.btnStart);
        btnStop = findViewById(R.id.btnStop);
    }

    public void initLr(){
        btnStart.setOnClickListener(v -> {
            startService(intent);
            Toast("음악서비스 시작 요청");
        });

        btnStop.setOnClickListener(v -> {
            stopService(intent);
            Toast("음악 서비스 중지 요청");
        });
    }

    public void Toast(String msg){
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

예제 14-5 AndroidManifest.xml

<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cos.project14_1">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Project14_1">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name="MusicService" />
    </application>

</manifest>

2. 브로드캐스트 리시버

  • 브로드캐스트 리시버(Broadcast Receiver, BR)를 활용하면 안드로이드에서 발생하는 많은 이벤트(문자 메시지, 배터리 방전, 날짜 변경 등)를 감지하고 이를 처리하는 앱을 작성할 수 있습니다.
  • 안드로이드는 문자 메시지 도착, 배터리 방전, SD 카드 탈부착, 네트워크 환경 변화 등이 발생하면 방송 신호를 보내는데, 이러한 방송 신호를 받아서 처리하는 것이 바로 브로드캐스트 리시버입니다.
  • 브로드캐스트 리시버의 대표적인 응용은 배터리 상태 확인입니다.
  • 안드로이드는 배터리 상태가 변할 때마다 이를 방송하는데, 배터리 상태를 확인하기 위한 관련 액션은 아래 표와 같습니다.
  • 위의 액션 중에서 ACTION_BATTERY_CHANGED만 잘 확인하면 됩니다.

실습 14-2 배터리 상태 체크 앱 만들기

  • 텔넷이나 푸티를 사용해서 확인 해야하므로 추후 진행보겠습니다.

3. 콘텐트 프로바이더

  • 안드로이드는 보안상 앱에서 사용하는 데이터를 외부에서 접근할 수 없습니다.
  • 예를 들면 간단 일기장에서 생성한 일기 파일이나 SQLite의 그룹 이름 데이터는 자신의 앱에서만 사용할 수 있고 다른 앱에서는 사용할 수 없습니다.
  • 이러한 파일이나 데이터베이스를 외부 앱에서 사용하게 하려면 콘텐트 프로바이더(Content Provider, CP)를 만들어서 외부에 제공해야 합니다.
  • 콘텐트 프로바이더와 관련이 깊은 것은 URI(Uniform Resource Identifier)인데, URI는 콘텐트 프로바이더에서 제공하는 데이터에 접근하기 위한 주소라고 생각하면 됩니다.
  • URI는 'content://패키지명/경로/아이디' 형식으로 지정할 수 있습니다.

3-1 안드로이드 제공 콘텐트 프로바이더

  • 안드로이드에서는 자체적으로 몇 갖기 콘텐트 프로바이더를 제공하고 있습니다.
  • 연락처 전화번호, 통화 기록, 주소록, 브라우저의 북마크 등을 예로 들 수 있습니다.
  • 아래 그림은 안드로이드 자체에서 제공하는 콘텐트 프로바이더를 사용하는 방법을 보여줍니다.
  • 예로 통화 기록은 URI를 Contacts.Phones.CONTENT_URI로 제공합니다.
  • 사용자는 getContentResolver( ) 메서드를 이용하여 ContentResolver 클래스를 얻은 후 통화 기록에 접근할 수 있습니다.
  • 아래는 주요 콘텐트 프로바이더와 URI를 정리한 표입니다.
  • AVD에서 통화 버튼을 눌러 통화 기록을 몇 건 남겨놓습니다.
  • 안드로이드에서 통화 기록에 접근하려면 AndroidManifest.xml의 <application 위에 아래 코드를 추가하여 접근 권한을 줘야 합니다.
<uses-permission android:name="android.permission.READ_CALL_LOG" />

예제 14-10 activity_main.xml

<androidx.appcompat.widget.LinearLayoutCompat
    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:orientation="vertical"
    android:padding="10dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnCall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="통화 기록 가져오기" />

    <EditText
        android:id="@+id/etCall"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="100"
        android:gravity="top"/>

</androidx.appcompat.widget.LinearLayoutCompat>

예제 14-11

public class MainActivity extends AppCompatActivity {

    private Button btnCall;
    private EditText etCall;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        init();
        initLr();
    }

    public void init(){
        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.READ_CALL_LOG}, MODE_PRIVATE);
        btnCall = findViewById(R.id.btnCall);
        etCall = findViewById(R.id.etCall);
    }

    public void initLr(){
        // 버튼을 클릭하면 getCallHistory( )를 호출하고 그 결과를 에디트텍스트에 출력
        btnCall.setOnClickListener(v -> {
            etCall.setText(getCallHistory());
        });
    }

    // 통화 기록의 내용을 검색하고 통화 기록을 문자열로 만들어 반환하는 함수
    public String getCallHistory() {
        // 통화기록 중에서 통화 날짜, 발신, 또는 착신 여부, 전화번호, 통화 시간에 대한 문자열 배열을 준비
        String[] callSet = new String[] {CallLog.Calls.DATE, CallLog.Calls.TYPE,
                CallLog.Calls.NUMBER, CallLog.Calls.DURATION};

        // 통화 기록에 대해 callSet 에서 설정한 내용을 조회
        Cursor c = getContentResolver().query(CallLog.Calls.CONTENT_URI, callSet, null, null, null);

        // 통화 기록이 단 한 건도 없다면 종료
        if (c == null) {
            return "통화기록 없음";
        }

        // 통화 기록의 문자열을 저장할 StringBuffer 변수를 준비
        StringBuffer callBuff = new StringBuffer();

        callBuff.append("\n 날짜 : 구분 : 전화번호 : 통화시간 \n\n");
        // 통화 기록 앱의 처음 행으로 이동
        c.moveToFirst();

        // 통화 기록의 마지막까지 반복
        do {
            // 첫 필드(0번)를 가져와서 날짜 형식으로 버퍼에 저장
            long callDate = c.getLong(0);
            SimpleDateFormat datePattern = new SimpleDateFormat("yyyy-MM-dd");
            String date_str = datePattern.format(new 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();
    }
}

profile
Nil Desperandum <절대 절망하지 마라>

0개의 댓글