[Android] 서비스(Service)
모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다.
서비스(Service)
백그라운드에서 실행되는 앱의 구성요소/프로세스
*백그라운드 : 화면 뒤의 공간
👆 서비스도 액티비티와 같이 앱의 4대 구성요소(액티비티, 서비스, 브로드캐스트 수신자, 내용 제공자) 중 하나!
→ 시스템에서 관리하기 때문에 액티비티처럼 서비스도 Manifest 파일에 꼭 등록해야 함!
서비스의 실행 원리와 역할
[역할] 단말이 항상 실행되어 있는 상태로 다른 단말과 데이터를 주고 받거나 필요한 기능을 백그라운드에서 실행
→ 실행된 상태를 계속 유지하기 위해 서비스가 비정상적으로 종료되더라도 시스템이 자동으로 재실행함!
👆 서비스를 시작하기 위해 액티비티에서 startServcie() 메서드를 호출
- startService() 메서드를 호출할 때는 인텐트 객체를 파라미터로 전달
- 이 인텐트 객체는 어떤 서비스를 실행할 것인지에 대한 정보를 담고 있음
- 시스템은 서비스를 시작시킨 후 인텐트 객체를 서비스에 전달함
💡 서비스가 실행 중인 경우, 실행 이후 startService()를 여러 번 호출해도 서비스는 이미 메모리에 있음
→ 서비스를 시작하는 목적 이외에 인텐트를 전달하는 목적으로도 자주 사용됨!
→ 이미 서비스가 메모리에 만들어져 있는 상태라면, 시스템은 onCreate()가 아닌 onStartCommand() 메서드를 실행함
*onStartCommand() 메서드 : 서비스로 전달된 인텐트 객체를 처리하는 메서드
[예제]
MainActivity.java
public class MainActivity extends AppCompatActivity {
EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = findViewById(R.id.service_et);
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.java
public class MyService extends Service {
private static final String TAG = "MyService2";
public MyService() {
}
@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");
}
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
- 인텐트에 2개의 부가 데이터(Extra data)를 넣고 서비스에 전달함
- startService() 메서드에 담은 인텐트 객체는 MyService 클래스의 onStartCommand() 메서드로 전달됨
인텐트의 부가 데이터 출력하기
위의 예제에서 추가한 3개의 메서드 중 onStartCommand() 메서드가 인텐트 객체를 전달받음!
😮 이때 onStartCommand() 메서드는 서비스 내에서 아주 중요한 역할을 함!
👆 서비스는 시스템에 의해 자동으로 다시 시작될 수 있기 때문에 onStartCommand() 메서드로 전달되는 인텐트 객체가 null인 경우를 검사함
- null인 경우, onStartCommand() 메서드는 Service.START_STICKY를 반환
- 이 값을 반환한 경우, 서비스가 비정상 종료되었다는 의미 → 시스템이 자동으로 재시작함
- 자동으로 재시작하지 않도록 하려면 다른 상수를 사용
MyService.java
private void processCommand(Intent intent) {
...
// 인텐트 전달
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);
}
- 서비스에서 startActivity() 메서드를 호출할 때는 새로운 태스크(Task)를 생성하도록 FLAG_ACTIVITY_NEW_TASK 플래그를 인텐트에 추가
- 서비스는 화면이 없기 때문에 서비스에서 화면이 있는 액티비티를 띄우려면 새로운 태스크를 만들어야 함
- MainActivity 객체가 이미 메모리에 만들어져 있을 때 재사용하도록 FLAG_ACTIVITY_SINGLE_TOP, FLAG_ACTIVITY_CLEAR_TOP 플래그도 인텐트에 추가함
MainActivity.java
public class MainActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
Intent passedIntent = getIntent();
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_SHORT).show();
}
}
}
- MainActivity가 메모리에 만들어져 있지 않은 상태에서 처음 만들어진다면 onCreate() 메서드 안에서 getIntent()를 호출
- 이미 MainActivity가 메모리에 만들어져 있다면 onCreate() 메서드는 호출되지 않고 onNewIntent() 메서드를 호출
- 예제에서는 onNewIntent() 메서드가 호출되었을 때 processIntent() 메서드를 호출하도록 함
- 인텐트로 전달받은 데이터는 토스트 메세지로 5초 뒤에 나타남
💡 서비스에서 액티비티로 데이터를 전달하는 방법은
- Service 클래스의 startService() 메서드 호출하기 - stopService() 메서드로 종료할 수 있음
- IntentService 클래스 사용하기
- 백그라운드에서 실행되는 것은 같지만 한 번 실행되고 나면 끝나는 작업을 수행
- onHandleIntent 메서드가 있으며 이 함수는 onStartCommand() 메서드로 전달된 인텐트를 전달받으면서 실행
- 이 함수의 실행이 끝나면 서비스는 자동 종료됨
* 인텐트(Intent)와 부가데이터(Extra data), 플래그(Flag) 관련 내용은 아래 포스팅 참고!