6장 - 서비스

모래사우르스
|2025. 1. 2. 23:50

백그라운드 : 화면 뒤의 공간

서비스 : 백그라운드에서 실행되는 앱의 구성 요소 (앱의 구성요소이기 때문에 시스템에서 관리됨.)

서비스는 비정상적으로 종료되더라도 시스템이 자동으로 재실행한다. (실행된 상태를 계속 유지하기 위함.)

ex) 카카오톡 메시지는 앱이 실행되어 있지 않더라도 발신되어야 한다.

 

startService() : 서비스를 실행하는 메서드. 이 메서드 호출 시 인텐트 객체를 파라미터로 전달한다.

서비스를 시작하거나, 인텐트를 전달하는 목적으로 사용된다.

(ex. 액티비티->서비스 데이터 전달 : 인텐트 객체를 만들고 거기에 부가 데이터를 넣은 뒤 startService() 호출)

 

onStartCommand() : 전달된 인텐트 객체를 처리하는 메서드.

서비스가 startService()에 의해 메모리에 만들어진 상태에서, 시스템은 onCreate 메서드가 아닌 이 메서드를 실행한다.

 

<서비스↔액티비티 : 인텐트 양방향으로 전달하기>

1. activity_main.xml

아래와 같이 editText와 버튼 배치.

2. MainActivity.java

package com.example.chapter6_1;

public class MainActivity extends AppCompatActivity {

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

        EditText editText = findViewById(R.id.editTextText);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String name = editText.getText().toString();

                Intent intent = new Intent(getApplicationContext(), MyService.class);
                intent.putExtra("command","show");
                intent.putExtra("name", name);

                startService(intent); // myService 클래스의 onStartCommand 객체로 전달
            }
        });
        // 액티비티가 '새로' 만들어질 때 전달된 인텐트 처리하기
        Intent passedIntent = getIntent();
        processIntent(passedIntent);
    }

    @Override
    protected void onNewIntent(@NonNull Intent intent) {
        // 액티비티가 '이미' 만들어져 있을 때 전달된 인텐트 처리하기
        processIntent(intent);
        super.onNewIntent(intent);
    }

    private void processIntent(Intent intent){
        if(intent != null){
            String command = intent.getStringExtra("command");
            String name = intent.getStringExtra("name");

            Toast.makeText(this, "command : "+command+", name : "+name, Toast.LENGTH_LONG).show();
        }
    }
}

 

 

[onClick]

새로운 Intent 객체 생성 코드를 보면 파라미터로 context 객체와 MyService.class가 들어간 것을 볼 수 있다.

Intent intent = new Intent(getApplicationContext(), MyService.class);

MyService라는 서비스 파일을 추가해줄 것임.

 

putExtra(키,값)으로 인텐트 안에 2개의 부가 데이터를 넣어줌.

버튼을 누를 시 액티비티 → 서비스로 인텐트가 전달된다.

 

[재정의된 메서드]

onNewIntent() : MainActivity가 이미 메모리에 만들어져 있다면 onCreate 메서드는 호출되지 않고 이 메서드가 호출되며,

인텐트는 이 메서드의 파라미터로 전달됨.

 

[새로 작성된 메서드]

processIntent() : 인텐트로 전달받은 부가 데이터를 토스트메시지로 띄우는 메서드임.

액티비티가 새로 만들어질 때(onCreate), 이미 만들어졌을 때(onNewIntent) 둘 다 실행된다.

 

3. 서비스 파일 만들기

MyService.java

package com.example.chapter6_1;

public class MyService extends Service {
    private static final String TAG = "MyService";

    // 생성자 메서드
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Binding : 서비스가 서버 역할을 하면서 액티비티와 연결될 수 있도록 만드는 것
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"onCreate() 호출됨");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG,"onStartCommand() 호출됨");

        if(intent == null) // 서비스는 시스템에 의해 자동으로 재시작될 수 있기 때문에 인텐트가 null인 경우도 검사하는 것.
            return Service.START_STICKY; // 이 값을 반환하면 시스템이 비정상 종료되었다는 의미.
        else
            processCommand(intent);

        return super.onStartCommand(intent, flags, startId);
    }

    private void processCommand(Intent intent){
        // 인텐트에서 부가 데이터 가져오기
        String command = intent.getStringExtra("command");
        String name = intent.getStringExtra("name");

        Log.d(TAG,"command : "+command+", name : "+name);

        for(int i=0;i<5;i++){ // 5초 동안 1초에 한번씩 로그 출력.
            try{
                Thread.sleep(1000); // 1000ms = 1초
            } catch(Exception e){};
            Log.d(TAG,"Waiting "+i+" seconds.");
        }

        // 액티비티로 인텐트 넘기기
        Intent showIntent = new Intent(getApplicationContext(), MainActivity.class);
        showIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | // startActivity 메서드 호출 시 새로운 태스크 생성
                Intent.FLAG_ACTIVITY_SINGLE_TOP |
                Intent.FLAG_ACTIVITY_CLEAR_TOP);

        showIntent.putExtra("command", "show");
        showIntent.putExtra("name",name+" from service.");
        startActivity(showIntent); // MainActivity 쪽으로 인텐트 객체 전달
    }
}

 

[태그]

태그(Tag) : 로그를 구분할 수 있는 문자열로, Log.d()에서 로그 출력을 위해 첫 번째 파라미터로 전달된다.

가장 처음에

private static final String TAG = "MyService"; 라는 코드로 태그를 설정해줌.

 

프로그램을 모두 작성한 뒤, 실행시킨 다음 tag:myservice로 입력해주면 (대소문자 같게 처리하나봄)

아래와 같이 MyService라는 태그가 붙은 로그만 출력해준다.

(서비스에 전달된 인텐트의 부가 데이터를 확인하기 위해 Log.d 메서드를 사용함.)

 

[재정의된 메서드]

 

onstartCommand() : 인텐트 객체를 전달받음. intent가 null이 아니라면 processCommand 메서드를 실행한다.

 

[새로 작성된 메서드]

processCommand() : 인텐트를 사용하여 액티비티로부터 부가 데이터를 가져온 다음, 다시 액티비티로 넘긴다.

 

중간에 for문이 있는데 왜 있는건진 잘 모르겠음. 초당 로그를 왜 출력하는지? 근데 실행할 때 좀 걸리는 거 보니까.. 로딩을 이런 식으로 처리한다 이걸 알려주기 위해 존재하는 듯?

 

마지막에 startActivity(showIntent);로 액티비티 쪽으로 인텐트 객체를 전달한다.

 

 

4. 실행화면

 

(처음에 앱을 실행할 때도 토스트메시지가 띄워지는데 이게 맞는건가 잘 모르겠음)

 

단어를 입력하면

액티비티 → 서비스 (키 : "name", 값 : "arson" 전달)

서비스 → 액티비티 (키 : "name", 값 : "arson from service" 전달)