본문 바로가기

안드로이드

Service 란?

반응형

Service 

사용자가 다른 애플리케이션으로 전환하더라도 백그라운드에서 계속해서 실행할수 있도록 하는 안드로이드 컴포넌트. 

이외에도 구성요소를 서비스에 바인딩하여 서비스와 상호작용 할수 있다.

예 ) 한 서비스에서 네트워크 처리, 파일 I/O, 음악재생 이 모든것을 백그라운드에서 수행할수있다.

 

서비스의 유형 3가지 

1. 포그라운드 

     사용자에게 보이는 작업을 수행,화면에 보여지는 작업을 포그라운드라고 한다.

     예 ) 유튜브 및 웹서핑처럼 눈으로 직접봐야 알 수 있는 작업

2.백그라운드

    사용자에게 보이지 않는 작업을 수행

    예) 어느 앱이 저장소를 압축하는 데 사용, 또는 모바일 게임하다가 중간에 나갔다와도 종료되지않고 실행되고있는것

3.바인드

   애플리케이션 구성요소가 BindService()를 호출하여 해당 서비스에 바인딩한다.

   바인딩 된 서비스는 클라이언트-서버 인터페이스를 제공하여 구성요소(예, 액티비티)가 서비스와 상호작용하게 해준다.

   바인딩 해제되면 해당 서비스 소멸.

 

   *onStartCommand()

     구성요소가 서비스를 시작하게한다.

   *onBind()

     바인드를 허용한다.

 

    => 서비스가 시작되거나, 바인드가 되었던 ,양쪽 모두이든 모든 애플리케이션 구성요소(예, 액티비티 )가 해당서비스를 사용할 수 있다. 

          이것을 우리는 Intent 로 시작할수 있다.

    

주의 할점은 ..서비스 클래스를 확장할 때는 서비스가 모든 작업을 완료할 수 있는 새 스레드를 생성하는 것이 중요합니다. 서비스는 기본적으로 애플리케이션의 기본 스레드를 사용하기 때문에 애플리케이션이 실행 중인 액티비티의 성능을 저하시킬 수 있습니다.

 

기본 사항 

 

서비스 생성하려면 Service의 하위 클래스를 생성해야한다.

서비스의 수명주기의 주요 측면을 처리하는 콜백 메서드를 몇가지 재정의 해야한다.

서비스에 바인딩할 구성요소에 대한 메커니즘을 제공해야한다,

 

재정의가 필요한 가장 중요한 콜백 메서드 

 

1.onStartCommand()

시스템이 이 메서드를 호출 하는 것은  구성요소(예.액티비티)가 서비스를 시작하도록 요청하는 경우

(*이때  상호작용하는 구성요소(예 액티비티 )에서는 startService()를 호출하는 방법을 씀.)

이 메서드 실행되면 서비스가 시작되고 무한히 실행 가능.

(*이때 구성요소에서  해당 서비스를 중단하고 싶을때는 stopSelp() 또는 stopService()를 호출함)

 

2.onBind()

  시스템은 다른 구성 요소가 해당 서비스에 바인딩되고자 하는 경우

(*이때 bindService()를 호출하는 방법을 사용합니다. )

 -바인딩 허용하지 않을 경우 :null 반환 

  -바인딩 허용할 경우 :

   이 메서드를 구현할 때는  클라이언트와 서비스가 통신을 주고 받기위해 사용할 인터페이스를 제공해야한다.  

    이때 ,Ibinder 반환

 

* 한 구성요소가 bindService()를 호출하여 서비스를 생성하는 경우에, 해당 서비스는 구성요소가 바인딩된 경우에만 서비스를 실행     한다. 서비스가 모든 클라이언트로부터 바인딩이 해제도면 시스템이 이를 소멸시킨다. 

  

 흐름적어보기 

 

 1.Activity상속받은 클래스에서 StartService()를 호출하여 서비스를 시작!

  2. Service를 상속받은 클래스의 onStartCommand()호출 발생 

  3. Service는 stopSelf()로 스스로 중단하거나, 또  activity에서 stopservice()를 호출하여 서비스를 중단할때까지

      실행중인 상태를 유지한다.

 

안드로이드 시스템이 서비스를 강제 중단 하는 것은

메모리가 부족하여 사용자 포커스를 가진 액티비티를 위해 시스템 리소스를 회복을 해야하는 경우

 

(즉, 사용자 포커스를 가진 액티비티에 바인딩 된 서비스읭 경우 종료될 가능성이 다.

        또 포그라운드에서 실행되도록 선언된경우에도 적다.)

       

그러나! 백그라운드 작업목록에서 서비스가 차지하는 위치를 낮추고,서비스가 종료될 가능성이 높다.

서비스가 시작되었다면, 시스템에 의해서 재시작을 정상적으로 할수있도록 설계해야한다.

 

시스템이 서비스를 중단했을때,리소스 다시사용할수있게 되면 서비스가 시작된다.

**다만, 개발자가 onStartCommand()에서 반환하는 값 에 따라 달라질수 있다.

 

 

onStartCommand()의 리턴 타입 3가지

 

Service는 실행 시 startService(Intent Service)를 호출하는데 onStartCommand(Intent intent, int flags, int startId)에 intent로 value를 넘겨줄 수 있다.

1.START_STICKY

 서비스가 강제 종료되었을 경우, 기존에 intent에 value 값이 설정되어 있다고 하더라도 시스템이 Service 재시작 시 intent 값을 null로 초시화시켜서 재시작한다. 

2.START_NOT_STICKY 

flag를 리턴해주면, 강제로 종료 된 서비스가 재시작하지 않는다. 따라서, 시스템에 의해 강제 종료되어도 괜찮은 작업에 사용한다. 

3.START_REDELIVER_INTENT

START_STICKY와 마찬가지로 Service가 종료되어도 시스템이 다시 Service를 재시작시켜주지만 intent 값을 그대로 유지시켜 준다. startService() 호출 시 intent value 값을 사용한 경우라면, 이 flag를 사용해서 리턴 값을 설정해주면 된다. 

 

 

 

 

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private static final String TAG="maind";

    EditText editText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"onCreate");
        setContentView(R.layout.activity_main);
        editText=findViewById(R.id.editTextTextPersonName);
        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);
            }
        });
        //액티비티가 새로 만들어질때 전달된 인텐트 처리하기
        Intent passdIntent=getIntent();
        proccesIntent(passdIntent);
    }
// 메인 액티비티가 이미 메모리에 만들어져있다면 oncreate()메서드 호출 하지 않고
  //onNewIntent()가 호출된다.
    @Override
    protected void onNewIntent(Intent intent) {
        Log.d(TAG,"onNewIntent");
        proccesIntent(intent);
        super.onNewIntent(intent);
    }
    private void proccesIntent(Intent intent)
    {
        Log.d(TAG,"proccesIntent");
        if(intent !=null)
        {
            String command=intent.getStringExtra("command");
            String name=intent.getStringExtra("name");
            Toast.makeText(this,"command"+command+"name"+name,Toast.LENGTH_LONG).show();
        }
    }
}

 

 

package com.example.myapplication;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

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 void onDestroy() {
        super.onDestroy();
    }

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

        if(intent==null){
            return Service.START_STICKY;
        }else
            processCommand(intent);
        Log.d(TAG,"끝");
        return super.onStartCommand(intent, flags, startId);
    }

    @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);
        for(int i=0;i<5;i++)
        {
            try{
                Thread.sleep(1000);
            }catch (Exception e){}
            Log.d(TAG,"Watting"+i+"seconds.");
        }
        //서비스와 액티비티 동신가능 , 즉 컴포넌트간의 공유가능 
        Intent showIntent= new Intent(getApplicationContext(),MainActivity.class);
        //서비스에서 StartActivity()메서드를 호출하는 경우 새로운 테스크를 생성하도록 플래그를 인텐트에 추가해줘야한다(FLAG_ACTIVITY_NEW_TASK)
        //서비스는 화면이 없기때문에 화면이 없는 서비스에서 화면이 있는 액티비티를 띄우려면 필요하다
        //메인 액티비티 객체가 이미 메모리에 만들어져있으면 재사용하도록 두개 더 추가 ( FLAG_ACTIVITY_SINGLE_TOP,FLAG_ACTIVITY_CLEAR_TOP)
        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 Servie");
        startActivity(showIntent);
    }
}

 

[정리]

1. stop 명령어를 주지 않는한 서비스는 죽지않는다.
2. 그래서 앱 시작 ->소비스 시작(쓰레드 포문 다섯번)-> 앱종료후 ->다시 켯을때 서비스 oncreate실행안됨. 

     사실상, 서비스 한번 시작했다하면 on create ->onstartcommand()

      앱껏다켜도 서비스 보내보면  바로 onStartCommand 실행

      즉 processCommad의 쓰레드는 끝낫을지라도, 기본 서비스컴포넌트의 쓰레드는 백그라운드 앱이  실행되고 있는것임.
3. 메모리부족해서 죽은걸 강제로 죽은경우라고 말하는데 

     안드로이드에서 시스템이 서비스를 다시 호출하게 되어있음.

      onStartCommand의 intent==null인경우가 그경우인데 리턴값에 따라 서비스를 재시작할지 안할지를 결정할수있다.

 

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

위 예제에서는 startService() 를 통해 서비스가 시작되고 이러한 서비스는 한번 시작되면 백그라운드에서 무한정 실행될수 있습니다.  심지어 서비스를 시작한 액티비티가 소멸되어도 마찬가지 입니다.  그러나 이렇게 시작한 서비스는 호출한 쪽에 어떠한 결과를 반환할수 없습니다.  이를 Unbound Service 라고도 합니다.

아래는 클라이언트-서버 관계처럼 통신가능한 서비스바인딩 에 대한설명 이다 .

https://bitsoul.tistory.com/149

 

안드로이드 서비스 바인딩 예제 (Bound Service)

안드로이드 서비스 바인딩 예제  (Service Bind) 일전의 포스팅에서 안드로이드 4대 컴포넌트 중 하나인 서비스 (Service) 에 대한 예제를 다루었습니다. 안드로이드: 서비스 Service 예제   위 예제에서

bitsoul.tistory.com

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

https://developer.android.com/guide/components/services?hl=ko#java

 

서비스 개요  |  Android 개발자  |  Android Developers

Service는 백그라운드에서 오래 실행되는 작업을 수행할 수 있는 애플리케이션 구성 요소이며 사용자 인터페이스를 제공하지 않습니다. 다른 애플리케이션 구성 요소가 서비스를 시작할 수 있으

developer.android.com

 

 

 

 

 

 

 

 

 

반응형

'안드로이드' 카테고리의 다른 글

GattSever  (0) 2021.06.22
Burn-in ?  (0) 2021.06.22
Powermanager  (0) 2021.05.25
안드로이드 생명주기  (0) 2021.03.03
리사이클러뷰 리스트에 sqlite 값 뿌려주기  (0) 2021.03.02