서비스( Service ) - 백그라운드 프로세스


백그라운드에서 작동하는 프로세스를 Service라고 부른다. Service와 Activity간에는 상호 데이터 교환이 가능하다.

이때, Service와 Activity간 데이터 교환을 위해선 Intent 객체가 사용된다. (Intent가 안드로이드 표준 DTO 객체 같은데 ?)

안드로이드 앱에서 Service를 등록하려면 Service 클래스를 상속받는 클래스를 만들면 된다.

 

메인 Activity에서 Service로 데이터를 보낸 후, 다시 Service에서 메인 Activity로 데이터를 응답하는 예제를 만들어보자.

public class MainActivity extends AppCompatActivity {

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

        final EditText editText = (EditText) findViewById(R.id.editText);
        Button button = (Button) findViewById(R.id.button);

        button.setOnClickListener((v) -> {
            Intent intent = new Intent(getApplicationContext(), MyService.class);
            intent.putExtra("command", "show");
            intent.putExtra("name", editText.getText().toString());
            startService(intent); // Service에 데이터를 전달한다.
        });

        Intent passedIntent = getIntent(); // Service에서 보낸 Intent 객체를 전달받음
        processIntent(passedIntent);
    }

    @Override
    protected void onNewIntent(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();
        }
    }
}

 

메인 Activity 클래스에서 startService() 메서드를 사용해 Service에 데이터를 전달한다.

Activity에서 데이터가 넘어오면 Service에선 onStartCommand() 메서드가 Intent 객체를 전달받는다.

public class MyService extends Service {

    private static final String TAG = "MyService";

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

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

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

        if (intent == 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) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) { };

            Log.d(TAG, "Waiting " + i + " seconds.");
        }

        Intent showIntent = new Intent(getApplicationContext(), MainActivity.class);
        showIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                            Intent.FLAG_ACTIVITY_SINGLE_TOP |
                            Intent.FLAG_ACTIVITY_CLEAR_TOP);
        showIntent.putExtra("command", "show");
        showIntent.putExtra("name", name + " from service.");
        startActivity(showIntent); // Service에서 Activity로 데이터를 전달
    }

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

 

위 Service 코드에서 Activity에 넘기기 위해 Intent 객체를 만들고, addFlags() 메서드를 이용해 플래그를 설정했는데,

그 이유는 Service는 백그라운드에서 항상 돌아가지만, Activity 객체는 메모리에 있을 수도 있고 없을 수도 있다.

이 말은, Service에서 Activity로 데이터를 전달하는데 Activity 객체가 메모리에 올라가 있지 않다면 새로 객체를 생성해야된다는 말.

Activity 객체가 메모리에 없으면 생성해 올려주고, 메모리에 있으면 그걸 재사용한다는걸 알려주기 위해 플래그 설정이 필요한 것.

자, 그러면 이때 Activity가 메모리에 없다면 생성해준다고 했으니 Activity의 onCreate() 메서드가 호출되고,

그렇지 않다면 onNewIntent() 메서드를 통해 Service가 전달한 Intent 객체를 전달받을 수 있다.

 

위 Service 코드에서 넘어온 Intent 객체가 null이면 Service.START_STICKY 상수 값을 반환하는데,

onStartCommand() 메서드가 Service.START_STICKY 상수를 반환하면 시스템은 Service가 비정상 종료되었다고 판단,

Service를 재시작시킨다.

로그


위의 코드에서 Log 클래스를 통해 로그 값을 출력했는데, 이는 Logcat 창에 로그를 찍기 위함.

로그 출력을 위해선 첫 번째 파라미터로 로그를 구분할 수 있는 문자열을 전달해야 한다. 이걸 태그(TAG)라 부름.

 

출력된 로그 모습