반응형

: 서비스를 사용하지 않고 별도의 스레드를 생성해 백그라운드 작업을 하면 어떤 문제가 발생할까? 스레드로 백그라운드 작업을 하다가 앱에서 back 키를 눌러 액티비티를 모두 종료할 시 앱 프로세스의 우선순위가 낮아져 LMK(low memory killer)가 프로세스를 강제로 종료할 수 있다. 이 때 스레드 또한 종료되게 된다. 그렇다면 서비스는 그냥 스레드와 다를까? 먼저, 프로세스 우선순위에 대해 알아보자. 


- 프로세스 우선순위

1. 포그라운드 프로세스 : 사용자와 상호작용하는 액티비티나 그런 액티비티에 바인딩된 서비스, onCreate/ onStart/ onStartCommand/ onDestory 를 실행 중인 서비스, onReceive를 실행하는 브로드캐스트 리시버에 해당한다. 


2. 가시(visible) 프로세스 : 포그라운드 컴포넌트를 가지지 않지만 사용자가 보는 화면에 아직 영향이 있는 프로세스이다. 액티비티의 경우 onPause()까지 실행됬지만 가시 상태인 것이다. 이 액티비티에 바인딩된 서비스도 가시 프로세스이다.


3. 서비스 프로세스 : startService로 실행했지만 1,2 번 상태가 아닌 서비스가 실행 중인 프로세스이다.


4. 백그라운드 프로세스 : 액티비티가 종료되지 않았지만 사용자에게 보이지 않고 활성화된 컴포넌트가 없는 프로세스(홈 키를 눌러 onStop()까지 불린 태스크)


5. 빈 프로세스 : 사용자가 백 키로 액티비티를 모두 종료하고 활성화된 컴포넌트가 없는 프로세스이다. 우선순위가 낮아서 리소스가 부족하면 가장 먼저 종료된다.


=> 맨 위에서 말한 것을 보면 액티비티가 모두 종료되었으니 빈 프로세스가 된다. 하지만 서비스를 사용한다면 onStartCommand가 리턴된 후 서비스는 서비스 프로세스가 된다. 따라서 백그라운드 작업이 종료될 위험에서 안전하다.


: 서비스를 사용할 때 주의할 점은 서비스의 생명주기 메서드는 메인 스레드에서 실행된다는 점이다. 생명주기 메서드 내에서 오랜 시간동안 작업하면 안 된다. 또한 서비스는 앱에서 1개의 인스턴스밖에 생기지 않는다. 별도로 싱글톤으로 구현하지 않아도 되고 서비스 안에서 사용되는 변수도 별도의 싱글톤 패턴으로 만들 필요 없다. 다음은 서비스의 생명주기이다.

서비스는 스타티드 서비스와 바운드 서비스가 있다. 서로 결합해서 사용할 수도 있다. 스타티드&바운드 서비스를 사용해야 하는 예는 다음과 같다. 음악 재생 액티비티에서 액티비티를 종료해도 음악이 나와야 한다. 이는 스타티드 서비스이다. 홈 화면에서 음악 재생 액티비티로 가면 현재 듣고 있는 음악에 대한 정보가 나와야 한다. 이는 바운드 서비스이다. 또 다른 경우 네트워크로 파일을 다운로드 받고 있다면 액티비티를 종료해도 다운은 계속 되야 한다. 이는 스타티드 서비스이다. 다시 액티비티로 돌아왔을 때 다운 진행 상황을 보여줘야 한다면 이는 바운드 서비스이다. 이렇게 바운드 서비스와 스타티드 서비스를 결합해야 되기도 하다.


- 스타티드 서비스

: 스타티드 서비스는 startService로 시작한다. 단, startService를 호출한다고 바로 시작하지 않고 메인 루퍼의 메시지 큐에서 차례대로 작업이 처리된다. startService를 처음 호출했을 때는 onCreate()->onStartCommand() 가 콜백되고 그 이후에 startService를 호출하면 onStartCommand만 호출된다. 서비스에서 작업 진행 상황을 액티비티에 전달해야 할 때가 있다. 이 때 ResultReceiver 브로드 캐스트를 사용한다. 

@Override
protected void onCreate(Bundle savedInstanceState){
	
	....
	
	Intent intent = new Intent(this, MyService.class);
	intent.putExtra(Constant.EXTRA_RECEIVER, resultReceiver);
	startService(intent);
	
}

private Handler handler = new Handler();

private ResultReceiver resultReceiver = new ResultReceiver(handler){
	@Override
	protected void onReceiveResult(int resultCode, Bundle resultData){
		if(resultCode == Constant.SYNC_COMPLETED){
			.....
		}
	}
}

class MyService extends Service{
	@Override
	public int onStartCommand(Intent intent, int flags, int startId){
		
		....
		
		final ResultReceiver receiver = intent.getParcelableExtra(Constant.EXTRA_RECEIVER);
		recevier.send(Constant.SYNC_COMPLETED, null);
		stopSelf();
	}
}

만약 디바이스의 메모리가 부족할 경우 안드로이드 시스템은 서비스를 강제 종료시킬 수 있다. 이 때 서비스는 재시작할 수 있는 데 이는 onStartCommand의 리턴 값에 따라 동작 방식이 달라진다. 리턴 값은 다음과 같다.


- START_NOT_STICKY : 강제 종료되면 재시작하지 않는다. 예로 화면에 최신 뉴스를 보여줄 때다.


- START_STICKY : 강제 종료되면 다시 onStartCommand를 호출하되 Intent가 null로 전달된다. onStartCommand 메서드의 기본 리턴 값이다. intent가 null로 전달되기 때문에 내부 변수를 사용하는 서비스에 적합하다. 예로 SNS 앱에서 새로운 메시지가 몇 개 왔는 지 정기적으로 확인한다면 적절하다. 


- START_REDELIVER_INTENT : 강제 종료되면 재시작하되 Intent가 전달된다. 예로 특정 상점의 상품 목록을 가져온 후 DB에 저장하는 경우가 있다.


재시작 동작 방식은 중요하지만 그에 못지 않게 서비스 종료도 중요하다. 서비스 종료를 제대로 하지 않으면 작업이 종료되었는 데 재시작 할 수도 있고 메모리를 잡아먹게 된다. 서비스 종료는 stopService() 보다는 stopSelf() 메서드로 하는 걸 권장한다. 두 메서드의 동작 방식은 동일하지만 stopSelf()는 Service 내에서 호출하는 것이다. 서비스의 작업이 끝나면 바로 stopSelf를 호출해 종료하는 게 좋다. 이 때 서비스는 onDestroy()까지 실행된다. 서비스에서 여러 개의 스레드가 실행될 때 주의할 게 멤버 변수를 최소한으로 사용해야 한다. 여러 스레드가 잘못 공유할 수 있기 때문이다. 또한 여러 군데에서 startService를 실행한다면 모든 작업이 끝났을 때 서비스를 종료해야 한다. 이를 위해서 종료할 때 매 작업마다 stopSelf()를 호출하지 말고 stopSelfResult(int startId)를 호출하는게 좋다. stopSelfResult는 startId가 가장 최근에 시작된 것이라면 그 때 서비스를 종료한다. startId는 onStartCommand() 메서드의 인자이다.


서비스의 멀티스레딩이 필요없을 경우 순차적으로 실행하는 IntentService를 사용하자. IntentService는 onStartCommand 메서드의 기본 리턴 값이 START_NOT_STICKY이다. 리턴 값을 변경하려면 setIntentRedelivery(true)를 호출하면 된다. IntentService에서 Toast를 사용할 때 주의해야 한다. Toast는 내부적으로 Handler을 생성해 루퍼가 필요하다. 일반 Service에서는 Toast를 사용하려면 Looper.prepare을 해야 한다. IntentService는 내부적으로 Looper가 있기 때문에 바로 Toast 사용이 가능하다. 하지만 Toast는 화면에 Toast를 보여주는 작업과 일정 시간 후 화면에서 Toast를 제거하는 작업을 해서 루퍼의 메시지 큐에 2개의 메시지가 들어간다. 만약 IntentService가 Toast를 보여주는 작업만 메시지 큐에서 처리하고 Destroy 되 Looper가 제거된다면 Toast 제거 작업은 실행이 안 되 Toast가 화면에 계속 띄어지게 된다. 따라서 Looper.getMainLooper를 사용해 핸들러를 생성 후 Toast를 사용하는 게 좋다. 


서비스가 중복되서 실행되는 걸 원치 않을 수 있다. 예로 메모 앱에서 서버와 지속적으로 동기화할 때 비슷한 시간에 여러 번 동기화 되는 걸 원치 않을 수 있다. 즉, startService를 호출할 때마다 매번 스레드가 생성되지 않고 이미 시작되었다면 나머지는 스킵해야 할 수 있다. 이를 구현하기 위해 onCreate()에서 스레드를 생성하여 구현(onCreate는 한번만 호출된다)할 수 있지만 서비스의 일반적인 형태가 아니다. onStartCommand()에서 스레드를 생성해서 이를 구현하는 방법은 다음과 같다. 

private ExecutorService exec = Executors.newSingleThreadExecutor();
private boolean isRunning = false;

@Override
public int onStartCommand(Intent intent, int flags, int startId){
	if(isRunning)
		return START_NOT_STICKY;
	isRunning = true;
	exec.submit(new Runnable(){
		......
               stopSelf();
	});
	return START_STICKY;
}

@Override
public void onDestroy(){
	isRunning = false;
}

isRunning 변수를 통해 스킵 시에는 START_NOT_STICKY를 리턴해 재시작되지 않도록 했다. Executors 팩토리 메서드를 사용했는데 ThreadPoolExecutor을 직접 사용하면 exec 변수를 다음과 같이 할 수 있다.

private exec = new ThreadPoolExecutor(1,1, 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy();

DiscardPolicy를 통해 요청이 추가로 들어온다면 해당 요청을 버린다. 따라서 isRunning 변수를 사용할 필요 없다.



- 바운드 서비스

: 바운드 서비스는 서비스에 정의된 메서드를 다른 컴포넌트에서 호출할 수 있도록 한다.

public abstract boolean bindService(Intent service, ServiceConnection conn, int falgs)

service 매개변수는 대상 서비스를 가리킨다. conn은 서비스와 연결되거나 끊길 때의 콜백이다. flags는 보통 Context.BIND_AUTO_CREATE가 들어간다. startService를 실행해서 서비스를 실행하지 않았다면 BIND_AUTO_CREATE 옵션을 넣어야 한다. 스타티드 & 바운드 서비스가 아닌 이상 startService를 호출할 일이 없으므로 해당 옵션은 필수이다. 해당 옵션을 주고 stopService를 호출하면 서비스는 종료되지 않는다. 바운딩된 곳마다 unbindService를 실행해야 서비스가 종료된다. 해당 옵션이 없으면 stopService로 바로 종료된다. 또한 메모리 문제로 강제 종료됬을 시 해당 옵션이 있다면 재연결된다. 바운드 서비스는 로컬 바인딩과 리모트 바인딩으로 나뉜다. 리모트 바인딩은 다른 프로세스에서 접근할 때 사용하고 로컬 바인딩은 로컬 프로세스에서만 접근 가능하다.


여러 클라이언트들이 bindService()를 호출했다면 모든 클라이언트가 unbindService()를 호출해야 서비스가 정리된다. bindService와 unbindService는 액티비티의 onStart와 onStop에서 호출하는 걸 권장한다. onResume/onPause에서 이들 메서드들을 호출하면 해당 생명주기들이 자주 콜백되기 때문에 서비스를 종료하고 생성하는 작업을 많이 해 비용이 많이 든다. 


바인드 서비스에서 백그라운드 작업 시 결과를 돌려주는 방법으로 다음과 같이 4가지가 있다.

1. 백그라운드 작업이 끝났을 때 sendBroadcast()를 사용한다.

2. ResultReceiver을 사용한다.

3. Messenger를 사용해 양방향 통신을 한다.

4. 바인더 콜백을 이용한다. 이 방법은 안드로이드 프레임워크에서 많이 사용되는 방법이다. 아래는 바인더 콜백을 이용한 예이다.

public class MainService extends Service {
//서비스에서 선언.
 
//서비스 바인더 내부 클래스 선언
 public class MainServiceBinder extends Binder {
        MainService getService() {
            return MainService.this; //현재 서비스를 반환.
        }
    }
 
private final IBinder mBinder = new MainServiceBinder();
 
 @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        //throw new UnsupportedOperationException("Not yet implemented");
        return mBinder;
    }
 
//콜백 인터페이스 선언
public interface ICallback {
        public void recvData(); //액티비티에서 선언한 콜백 함수.
    }
 
private ICallback mCallback;
 
//액티비티에서 콜백 함수를 등록하기 위함.
public void registerCallback(ICallback cb) {
        mCallback = cb;
    }
 
//액티비티에서 서비스 함수를 호출하기 위한 함수 생성
public void myServiceFunc(){
    //서비스에서 처리할 내용
    }
 
 
//서비스에서 액티비티 함수 호출은..
     
    mCallback.recvData();
}
public class MainActivity extends Activity {
 
//액티비티에서 선언.
private MainService mService; //서비스 클래스
 
//서비스 커넥션 선언.
private ServiceConnection mConnection = new ServiceConnection() {
        // Called when the connection with the service is established
        public void onServiceConnected(ComponentName className, IBinder service) {
            MainService.MainServiceBinder binder = (MainService.MainServiceBinder) service;
            mService = binder.getService(); //서비스 받아옴
            mService.registerCallback(mCallback); //콜백 등록
        }
 
        // Called when the connection with the service disconnects unexpectedly
        public void onServiceDisconnected(ComponentName className) {
            mService = null;
        }
    };
 
//서비스에서 아래의 콜백 함수를 호출하며, 콜백 함수에서는 액티비티에서 처리할 내용 입력
private MainService.ICallback mCallback = new MainService.ICallback() {
        public void recvData() {
 
    //처리할 일들..
 
        }
    };
 
//서비스 시작.
public void startServiceMethod(View v){
        Intent Service = new Intent(this, MainService.class);
        bindService(Service, mConnection, Context.BIND_AUTO_CREATE);
    }
 
//액티비티에서 서비스 함수 호출
 
    mService.myServiceFunc();
}


반응형
,
반응형

안드로이드 어플리케이션을 구성하는 네 가지 기본 요소에는 ActivityServiceBroadcast ReceiverContent Provider가 있다.  인텐트(Intent)란 이러한 어플리케이션 구성요소(컴포넌트) 간에 작업 수행을 위한 정보를 전달하는 역할을 한다.  인텐트를 가장 손쉽게 사용한 예로는 액티비티간의 화면전환을 들 수 있다.  즉 인텐트는 컴포넌트A가 컴포넌트B를 호출할 때 필요한 정보를 담고 있으며, 이 정보에는 호출되는 컴포넌트B의 이름이 명시적으로 표시되기도 하고, 속성들이 암시적으로 표시되기도 한다.  또한 호출된 컴포넌트B가 호출한 컴포넌트A로 어떠한 결과를 전달할 때도 인텐트가 사용된다. 어떠한 컴포넌트를 호출하냐에 따라 사용되는 대표적인 메소드로는 다음과 같은 것들이 있다.

  • startActivity() -새로운 액티비티를 화면에 띄울 때
  • startService(), bindService() – 서비스와 관련
  • broadcastIntent() – 브로드캐스팅을 수행할 때

인텐트는 명시적 인텐트(Explicit Intent)와 암시적 인텐트(Implicit Intent)로 구분할 수 있는데 인텐트에 클래스 객체나 컴포넌트 이름을 지정하여 호출될 대상을 확실히 알 수 있는 경우에는 명시적 인텐트, 호출될 대상의 속성들을 지정했지만 호출될 대상이 달라질 수 있는 경우에는 암시적 인텐트라고 한다.

  1. 명시적 인텐트

다음은 MainActivity에서 button을 눌렀을 때 SecondActivity로 화면전환하는 코드이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*MainActivity*/
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
 
 
public class MainActivity extends Activity implements View.OnClickListener{
Button button;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 button = (Button)findViewById(R.id.button);
 button.setOnClickListener(this);
 }
 
 @Override
 public void onClick(View v) {
 Intent intent = new Intent(getApplicationContext(),SecondActivity.class);
 startActivity(intent);
 }
}

위 코드에서 살펴 볼 코드는 다음과 같다.

1
2
Intent intent = new Intent(getApplicationContext(),SecondActivity.class);
startActivity(intent);

새로운 인텐트 객체를 생성하면서 getApplicationContext()와 SecondActivity.class를 파라마터로 넘긴다. getApplicationContext()는 현재 액티비티 정보가 담겨있는 것이라고 생각하면 될 듯 하다. SecondActivity는 호출할 컴포넌트이다. 그리고 startActivity()에 인텐트 객체인 intent를 파라미터로 넘겨주면 새로운 화면인 SecondActivity가 실행된다. startActivity()는 새로 띄우는 액티비티로부터 받는 응답을 처리할 필요가 없을 때 간단하게 사용된다. 이에 반해 startActivityForResult()의 경우 새로 띄운 액티비티로 받는 응답을 처리할 경우에 사용된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*MainActivity*/
Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
startActivityForResult(intent,REQUEST_CODE);
 
...
 
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode==REQUEST_CODE){
        if(resultCode==RESULT_OK){
            String key = intent.getExtras().getString("key");
            Toast.makeText(getApplicationContext(),key,Toast.LENGTH_LONG).show();
        }
    }
}

startActivityForResult()는 인텐트 객체인 intent뿐만 아니라 정수형태의 코드 값을 파라미터로 전달하는데, 새로 띄웠던 여러 액티비티 중에 어떤 것으로부터 온 응답인지 구분하기위해 사용된다. 이 코드 값은 임의로 선언할 수 있지만 어플리케이션에 들어갈 액티비티가 여러 개 있을 수 있으므로 서로 중복되지 않는 값으로 설정해야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*SecondActivity*/
...
public class SecondActivity extends View.OnClickListener{
Button button;
 
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    button = (Button)findViewById(R.id.button);
    button.setOnClickListener(this);
  }
@Override
public void onClick(View v){
    Intent result = new Intent();
    result.putExtra("key",value);
    setResult(RESULT_OK,result);
    finish();
  }
}

새로 띄워진 SecondActivity에서 result라는 인텐트 객체를 생성하고, putExtra()를 통해 부가 데이터를 넣고 있다. putExtra() 메소드를 이용할 때는 key와 value 값을 함께 넣어줘야 나중에 value값을 확인하고자 할 경우 key값을 가지고 데이터를 가지고 올 수 있다. 그리고 setResult()메소드를 통해 MainActivity로 응답을 보내고 있다. 그러면 MainActivity에서는 onActivityResult()함수로 그 응답을 처리하는 역할을 한다.

2. 암시적 인텐트

1
2
3
4
5
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://m.naver.com"));
startActivity(intent);
....
Intent intent = new Intent(Intent.ACTION_VIEW,Uri.parse("tel:010-0000-0000"));
startActivity(intent);

위와 같이 암시적 인텐트는 보통 액션(Action)과 데이터(data)라는 속성으로 구성되어있다. 이 두가지 속성말고도 Category, Type, Component, Extras 라는 속성을 가진다. Component라는 속성을 지정할 경우 컴포넌트 클래스 이름을 명시적으로 지정하게 되는데 이 경우가 명시적 인텐트에 속하게 된다. 결국 암시적 인텐트는 Component 속성을 제외한 나머지 속성들로 구성되며, 이러한 속성들에 부합하는 컴포넌트가 실행된다.(호출할 대상들이 달라질 수 있음)

1
2
3
4
5
...
public void onClick(View v){
  Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://m.naver.com"));
  startActivity(intent);
}

위 코드를 실행했을 때의 화면이다.
intent
이와 같이 암시적 인텐트는 그 속성에 부합하는 컴포넌트가 여러개 있을 때 선택할 수 있도록 해준다.

3. 인텐트 필터

안드로이드 어플리케이션을 만들 때 메니페스트라는 파일이 있고, 파일 내용을 보면 Intent-filter라는 것이 있다.  예를 들어 컴포넌트 A가 컴포넌트 B로 인텐트를 보냈다고 하자. 명시적 인텐트라면 컴포넌트B는 바로 인텐트를 받았겠지만 암시적 인텐트라면 컴포넌트B는 이 인텐트가 자신의 속성과 부합하는지 확인을 해야할 것이다. 이러한 확인작업을 위해 Intent-filter라는 것이 존재한다. 인텐트 필터도 암시적 인텐트의 속성처럼 Action, data, category 필터라는 것을 가지고 있다. 이 필터들을 가지고 암시적 인텐트의 속성과 비교를 하게된다.

반응형
,
반응형

1. Activity 생명주기


먼저, Activity에 대해서 정리해볼텐데요,

아래 이미지를 보시면서 전체 흐름에 대해서 보고, 

하나씩 정리해보도록 하겠습니다.



1-1. OnCreate ()


액티비티가 Launch되고 나서, 최초에 실행될 때, 호출 됩니다.

액티비티에 필요한 리소스들을 여기서 초기화 해주는데요.

Button이라든가 EditText를 예로 들 수 있겠습니다.



1-2. OnStart ()


유저에게 액티비티가 보이도록 해주는데요.

이 단계는 매우 빠르게 끝나고, onResume() 상태로 들어갑니다.


보통 BroadcastReceiver는 이 단계에서 등록을 하는것이 좋습니다.



1-3. OnResume () 와 OnPause()


제 생각에, 안드로이드 개발에 있어서 꼭 알아야 하는, 매우 중요한 단계 두가지입니다.

OnResume 단계에서, Activity는 foreground로 나와서, 유저와 인터랙션을 하게 되는데요.


이 때, 유저에게 전화가 오거나, 다른 액티비티로 이동하면, OnResume()에 있던 activity는,

OnPause()를 호출하게 됩니다.

그러다가 유저가 다시 해당 activity로 돌아오면, 이번에는 다시

OnResume()을 호출합니다.


즉, Foreground로 나오면, OnResume을 호출하고, 다른 액티비티가 Background로 나오게 되면, OnPause를 호출하는 것인데요.

OnResume에서는, Camera리소스 같이 화면이 포커스를 가지고 있는 경우에만 사용할 리소스들을 초기화 해주고,

OnPause에서, 해제해 주어야 합니다.


생각보다 OnResume과 OnPause에서 리소스를 초기화하고, 해제해주어야 하는 경우가 많습니다.

BroadcastReceiver를 동적으로 생성했거나, SensorManager로 GPS를 사용하거나 하는 경우에는,

OnPause시에 리소스를 해제해 주면, 배터리소모를 막을 수 있기도 합니다.


한가지 주의할 점은, 

OnPause에서  데이터를 저장하거나, 네트워크를 호출하고, 데이터베이스를 실행하면 않된다는 것입니다.

안드로이드 가이드에서는, onPause가 위의 과업들을 수행할 만큼의 충분한 시간을 가지지 못한다고 합니다.

이런 무거운 과업들은 onStop에서 행하라고 하는 군요.



1-4. OnStop()


OnPause에서의 시간은 매우 짧습니다. 유저에게 다른 Activity가 보이고, 더이상 해당 Activity가 화면에서 보이지 않게되면,

OnStop이 호출됩니다. 

이 단계에서는, Activity Object가 메모리에만 남아있고, window manager에서는 attach되어있지 않아서, 

데이터가 있다면 다시 복원할 수 있는 상태입니다.


OnStop에서 유저가 다시 해당 액티비티로 돌아오게 되면,

OnRestart()를 거쳐서, OnStart()가 호출되게 됩니다.

하지만, 유저가 Activity를 finish()하거나(안드로이드 하단의 세모 버튼), 시스템이 메모리 부족으로 해당 액티비티를 finish()시킨다면,

onDestroy()를 호출하고 사라집니다.


참고로, 시스템은 OnStop()상태에 들어서면, onSaveInstanceState()이라는 메소드를 호출하는데요.

이 단계에서 key-value형태로 값을 저장할 수 있고, 이 값은 OnCreate()에서 복원할 수 있습니다.

이 부분에 관해서는 다른 글로 정리해보겠습니다~.



1-5. OnDestroy() 


OnStop()단계에서, 시스템이 메모리가 부족하여 시스템이 액티비티를 finish 하거나,

유저가 액티비티를 finish하는 경우, 혹은

프로그램적으로 activity를 finish()시키는 경우 OnDestroy가 호출됩니다.


한가지 알아둬야 할 점은, 안드로이드에서 세로화면에서 가로화면으로 전환시키는 것은,

내부적으로는 현재의 Activity를 OnDestroy()를 호출하고, 다시  OnCreate()를 호출한다는 것입니다.



2. Fragment 생명주기


아래 표에 Fragment의 생명주기를 나열해놓았는데요.

뭔가 많아 보이지요?

Fragment자체가 Activity안에서 존재하게 되므로, Activity의 존재와도 연관된 생명주기들을 추가적으로 가지고 있기 때문인데요.

하나하나 정리해 보겠습니다.


2-1. OnAttach ()


Fragment가 Activity에 attach될 때 호출됩니다.

코드적으로 재미있는 것은, 호출되면서 인자로 context가 주어진다는 점입니다.

이 context를 가지고, 부모 액티비티에 lisnter interface를 implement했다면,

형변환을 해서, 아래와 같이 가져올 수 있습니다.




2-2. OnCreate()


액티비티와 마찬가지로 초기화해야하는 리소스들을 여기서 초기화 해줍니다.

프래그먼트를 생성하면서 넘겨준 값들이 있다면, 여기서 변수에 넣어주면 됩니다.


하지만, 주의해야 할 점은, 여기서 UI 초기화는 할 수 없다는 점입니다.

UI초기화는 다음단계에서 해줍니다.




2-3. OnCreateView()


Layout을 inflate하는 곳인입니다.  view객체를 얻을 수 있으므로,

여기서 View와 관련되서, 버튼이나 EditText, TextView등을 초기화 할 수 있습니다.



2-4. OnActivityCreated()


Fragement에서의 OnCreateView를 마치고,

Activity에서 onCreate()가 호출되고 나서 호출되는 메소드입니다.

Activity와 Fragment의 뷰가 모두 생성된 상태로,

View를 변경하는 작업이 가능한 단계입니다.



2-5. OnStart()


Activity에서의 역할과 비슷합니다.

유저에게 Fragment가 보이도록 해줍니다.

Activity는 이단계에서는 started상태입니다.


2-6. OnResume()


이부분도 Activity와 비슷한데요.

유저에게 Fragment가 보여지고, 유저와 상호작용이 가능하게 되는 부분입니다.



2-6. OnPause(), OnStop()


OnPause에서 OnStop으로 가는 단계도, 액티비티의 생명주기와 유사한데요.

프래그먼트의 부모 액티비티가 아닌, 다른 액티비티가 foreground로 나오게 되면,

OnPause()를 콜하구요 Backstack으로 들어가게 됩니다.


다른 액티비티가 화면을 완전히 가리게 되면, 

onStop()을 호출하게 됩니다.

onStop에서 프래그먼트는 액티비티의 다른 데이터들과 마찬가지로,

유저가 다시 해당 액티비티를 호출하면 다시 복원될수 있는 상태입니다.



2-6. OnDestroyView()


메소드의 이름에서도 알수있겟지만, 

프래그먼트와 관련된 View가 제거되는 단계입니다.


2-6. OnDestroy()


프래그먼트는 생성될 때, onCreate-> onCreateView로,

초기화할 리소스들과, 초기화할 뷰를 순서대로 호출했는데요.

Destroy할 때는, 반대로 OnDestroyView에서 View를 제거하고,

OnDestroy()를 호출합니다.



2-6. OnDetach()


프래그먼트가 액티비티로부터 해제되어질때 호출됩니다.



반응형
,