서비스는 화면 없이 백그라운드에서 실행되는 앱의 구성 요소이다.
서비스도 액티비티를 만들 때 처럼 매니페스트 파일에 등록해야 한다.
서비스를 실행하려면 메인 액티비티에서 startService 메소드를 호출하여 인텐트 객체를 파라미터로 전달하고,
서비스 내에서 onStartCommand 메소드로 전달된 인텐트 객체를 처리한다.
서비스에서 액티비티로 데이터를 전달하려면 startActivity 메소드로 인텐트 객체를 전달하면 된다.
서비스는 app에서 우클릭 후 New 에서 Service를 선택해서 추가할 수 있다.
서비스가 만들어지면 Manifest.xml 파일에도 <service>
태그가 추가된다.
서비스 파일을 만들면 자동으로 생성자 메소드와 onBind 메소드가 생긴다.
여기에 서비스 수명주기를 관리하기 위해 onCreate 메소드와 onDestroy 메소드를 추가하고,
인텐드 객체를 전달받기 위해 onStartCommand 메소드를 추가한다.
activity_main.xml 파일에 버튼과 입력상자 하나를 추가한다.
버튼을 누르면 입력상자 안의 내용을 서비스로 전달하도록 만들 예정이다.
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.editText);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String name = editText.getText().toString();
Intent intent = new Intent(getApplicationContext(), MyService.class);
intent.putExtra("command", "show");
intent.putExtra("name", name);
startService(intent);
}
});
}
}
버튼을 누르면 인텐트를 만든다.
목적을 담은 command 라는 key를 가진 부가 데이터와,
입력상자에 입력된 문자를 담은 name 이라는 key를 가진 부가 데이터를 인텐트에 넣는다.
그 후 startService 메소드에 인텐트를 담아 서비스에 전달한다.
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
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 = "MyService";
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);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy() 호출됨");
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
private void processCommand(Intent intent){
String command = intent.getStringExtra("command");
String name = intent.getStringExtra("name");
Log.d(TAG, "command : " + command + ", name : " + name);
}
onCreate 메소드와 onDestroy 메소드가 호출되면 Log.d 메소드를 사용해서 로그를 출력하게 한다.
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"onCreate() 호출됨");
}
...
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy() 호출됨");
}
인텐트의 내용을 받아 인텐트의 내용을 로그로 출력하는 메소드 processCommand를 만든다.
private void processCommand(Intent intent){
String command = intent.getStringExtra("command");
String name = intent.getStringExtra("name");
Log.d(TAG, "command : " + command + ", name : " + name);
}
메인 액티비티에서 startService 메소드로 보내진 인텐트는 서비스 클래스의 onStartCommand 메소드로 전달된다.
onStartCommand로 전달된 인텐트가 null인 경우에는 Service.START_STICKY를 반환하여
서비스가 비정상적으로 종료되었음을 알려 시스템이 자동으로 재시작되도록 한다.
인텐트가 전달된 경우에는 processCommand 메소드를 사용해 로그로 인텐트 내용을 출력한다.
@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);
}
서비스에서 액티비티로 데이터를 전달하고 싶다면 서비스에서 startActivity 메소드를 사용하면된다.
MyService.java 의 processCommand 메소드의 마지막 부분에 코드를 추가한다.
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를 호출할 때는 새로운 태스크를 생성해도록 FLAG_ACTIVITY_NEW_TASK 플래그를 인텐트에 추가해야 한다.
또한 MainActivity 객체가 이미 메모리에 만들어져 있을 때 재사용하도록 FLAG_ACTIVITY_SINGLE_TOP 과 FLAG_ACTIVITY_CLEAR_TOP 플래그도 인텐트에 추가한다.
Intent showIntent = new Intent(getApplicationContext(), MainActivity.class);
showIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
인텐트 객체에는 부가 데이터를 두개 추가하였고,
startActivity 메소드로 인텐트 객체를 메인 액티비티로 전달한다.
showIntent.putExtra("command", "show");
showIntent.putExtra("name", name + " from service");
startActivity(showIntent);
메인 액티비티에서 인텐트를 받아 처리하도록 MainActivity.java 파일을 수정한다.
public class MainActivity extends AppCompatActivity {
EditText editText;
@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();
}
}
}
인텐트 객체를 받아 Toast로 내용을 출력하는 processIntent 메소드를 만든다
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 메소드를 호출하여 참조한다.
@Override
protected void onCreate(Bundle savedInstanceState) {
...
Intent passedIntent = getIntent();
processIntent(passedIntent);
}
하지만 MainActivity가 이미 메모리에 만들어져 있다면 onNewIntent 메소드가 호출된다.
@Override
protected void onNewIntent(Intent intent) {
processIntent(intent);
super.onNewIntent(intent);
}