어느 플랫폼에서건 앱을 작성하게 되면,
앱의 라이프사이클에 대해 고민을 하게 됩니다.
앱 프로세스가 생성(fork & exec)된 직후에 수행해야하는 루틴,
종료 직전에 처리해야하는 루틴,
앱이 일시정지할 때 혹은 다시 시작할 때 필요한 루틴 등은,
앱 라이프 사이클을 구상할때 필히 고려해야하는 요소입니다.
안녕하세요, Tizen 개발자 윤진입니다.
타이젠 2.3 버전 기준으로 앱 라이프사이클은 "application"이라는 framework에서 관리합니다.
"application"의 소스를 살펴보려면,
아래처럼 command를 입력하여 소스를 clone 받을 수 있습니다.
간단하게 웹으로 보고 싶다면 여기를 누르면 됩니다.
앱은 main()에서 앱 라이프사이클을 관리해주는 ui_app_main()을 불러줘야 합니다.
ui_app_main()은 아래와 같이 선언되어있습니다.
/** * @brief Runs the application's main loop until ui_app_exit() is called. * * @details This function is the main entry point of the Tizen application. * The app_create_cb() callback function is called to initialize the application before the main loop of application starts up. * After the app_create_cb() callback function returns true, the main loop starts up and the app_control_cb() callback function is subsequently called. * If the app_create_cb() callback function returns false, the main loop doesn't start up and app_terminate_cb() callback function is called. * This main loop supports event handling for the Ecore Main Loop. * * @since_tizen 2.3 * @param[in] argc The argument count * @param[in] argv The argument vector * @param[in] callback The set of callback functions to handle application lifecycle events * @param[in] user_data The user data to be passed to the callback functions * * @return 0 on success, otherwise a negative error value * @retval #APP_ERROR_NONE Successful * @retval #APP_ERROR_INVALID_PARAMETER Invalid parameter * @retval #APP_ERROR_INVALID_CONTEXT The application is illegally launched, not launched by the launch system * @retval #APP_ERROR_ALREADY_RUNNING The main loop already starts * * @see app_create_cb() * @see app_terminate_cb() * @see app_pause_cb() * @see app_resume_cb() * @see app_control_cb() * @see ui_app_exit() * @see #ui_app_lifecycle_callback_s */ int ui_app_main(int argc, char **argv, ui_app_lifecycle_callback_s *callback, void *user_data);
ui_app_main()에 들어가는 세 번째 인자 ui_app_lifecycle_callback_s는 라이프사이클을 위한 콜백을 등록해주는 부분입니다.
ui_app_lifecycle_callback_s는 아래와 같은 구조체입니다.
typedef struct { app_create_cb create; /**< This callback function is called at the start of the application. */ app_terminate_cb terminate; /**< This callback function is called once after the main loop of the application exits. */ app_pause_cb pause; /**< This callback function is called each time the application is completely obscured by another application and becomes invisible to the user. */ app_resume_cb resume; /**< This callback function is called each time the application becomes visible to the user. */ app_control_cb app_control; /**< This callback function is called when another application sends the launch request to the application. */ } ui_app_lifecycle_callback_s;
위의 구조체를 하나 만들어준 후,
create와 terminate,
pause와 resume,
그리고 app_control 콜백을 만들어 구조체에 채워넣어주어야 합니다.
typedef bool (*app_create_cb) (void *user_data); typedef void (*app_terminate_cb) (void *user_data); typedef void (*app_pause_cb) (void *user_data); typedef void (*app_resume_cb) (void *user_data); typedef void (*app_control_cb) (app_control_h app_control, void *user_data);
app_create_cb은 앱이 fork-exec된 이후 mainloop에 진입하기 직전에 불립니다.
이후 앱이 terminate되어 메모리에서 제거된 후 다시 런칭되기 전까지 불리지 않습니다.
앱이 런칭된 직후 프로세스를 초기화하는 루틴은 이곳에서 수행됩니다.
static int __before_loop(struct ui_priv *ui, int *argc, char ***argv) { int r; if (argc == NULL || argv == NULL) { _ERR("argc/argv is NULL"); errno = EINVAL; return -1; } g_type_init(); elm_init(*argc, *argv); r = appcore_init(ui->name, &efl_ops, *argc, *argv); _retv_if(r == -1, -1); LOG(LOG_DEBUG, "LAUNCH", "[%s:Platform:appcore_init:done]", ui->name); if (ui->ops && ui->ops->create) { r = ui->ops->create(ui->ops->data); if (r == -1) { _ERR("create() return error"); appcore_exit(); errno = ECANCELED; return -1; } LOG(LOG_DEBUG, "LAUNCH", "[%s:Application:create:done]", ui->name); } ui->state = AS_CREATED; __add_climsg_cb(ui); return 0; }
위의 코드는 __before_loop() 함수에서 사용자가 등록한 create()를 불러주는 부분입니다.(git://review.tizen.org/framework/appfw/app-core)
함수명에서 알 수 있듯,
mainloop에 진입하기 전에 create()을 불러주는 것을 확인할 수 있습니다.
create()를 실행한 직후 바로 mainloop에 진입합니다.
mainloop에서는 앱이 create()에서 수행한 루틴을 해석하여 첫번째 프레임을 렌더링하겠죠.
app_terminate_cb은 app_create_cb의 반대개념으로,
앱이 종료될 때 한 번 불립니다.
이 콜백에서 메모리를 정리하고,
현 프로세스에서 설정한 값들이나 사용자 데이터를 저장합니다.
app_pause_cb은 앱이 더 이상 화면에서 보이지 않게 된 순간 호출됩니다.
pause에서는 앱이 점유하고 있던 메모리를 내려놓거나,
pause되기 전까지 사용자가 수행한 결과물을 정리할 수 있습니다.
app_resume_cb은 화면에서 사라진 앱이 다시 화면에 보이게 되면 불립니다.
홈/메뉴를 통해 앱 A를 런칭했다가,
홈키나 파워키로 앱 B가 앱 A 위로 올라간 후,
다시 앱 A에 진입하게 되면 앱 A의 resume 콜백이 불리게 됩니다.
resume에서는 앱이 pause때 일시정지해두었던 기능을 다시 수행할 수 있도록 설정하면 됩니다.
app_control_cb은 위에서 설명한 create/terminate/pause/resume과는 달리 app_control_h을 인자로 받습니다.
app_control_cb은 create나 resume와는 다르게,
앱을 런칭하는 쪽에서 특정 정보를 app_control_h에 채워서 줄 수 있습니다.
때때로 앱은 런칭상황에 따라 다른 뷰를 보여주거나 다른 동작을 해야할 필요가 있겠죠.
예를 들어, 홈/메뉴를 통해 앱을 실행할 때와 스토어에서 앱을 실행할 때 서로 다른 뷰를 보여줄 필요가 있을 수도 있습니다.
이때 홈/메뉴는 홈/메뉴에서 앱을 런칭한다는 정보를 실어보내고,
스토어는 스토어에서 앱을 런칭한다는 정보를 실어보낸다면,
앱은 각각의 상황에 맞춰 뷰를 구성할 수 있습니다.
샘플앱을 런칭시켜 어떤 콜백이 불리는지 살펴보면 흥미로운 사실을 알 수 있습니다.
홈/메뉴를 통해 앱을 런칭시키면,
단지 app_create_cb만 불리지 않습니다.
총 3개의 콜백이 차례대로 불립니다.
app_create_cb은 애초에 설명한대로 앱을 런칭하여 mainloop에 진입하기 전에 불리게 됩니다.
하지만, app_control_cb은 왜 불리는 것일까요?
app_control은 런칭을 요청한 caller 앱에서 callee 앱으로 요청합니다.
callee 앱은 mainloop에 진입한 직후에 app_control_cb()을 처리하게 되죠.
static int __app_launch_local(bundle *b)
{
if (!aul_is_initialized())
return AUL_R_ENOINIT;
if (b == NULL) {
_E("bundle for APP_START is NULL");
}
if (g_idle_add(__app_start_internal, b) > 0)
return AUL_R_OK;
else
return AUL_R_ERROR;
}
위의 코드는 caller 앱에서 g_idle_add()를 등록하여 callee 앱에서 등록한 app_control_cb을 불러주도록 설정하는 부분입니다.
저 코드로 앱이 idle 상태에 진입한 직후에 app_control_cb의 루틴을 타게 되는 것입니다.
(git://review.tizen.org/framework/appfw/aul-1)
app_control_cb 내에서 윈도우를 elm_win_activate()를 이용하여 최상단으로 올리면,
드디어 앱이 사용자에게 노출이 됩니다.
앱이 일부의 영역이라도 보이게 되면, app_resume_cb()이 불리게 됩니다.
resume()은 앱이 보이게 되면 불리는 콜백이기 때문에,
초기런칭루틴에서도 예외없이 불릴 수밖에 없습니다.
따라서 앱 런칭 시점에는 세 개의 콜백이 약간의 시간차를 두고 연달아 불리게 되죠.
Application Life Cycle Management
이상 타이젠의 앱 라이프사이클을 간단하게 설명하였습니다.
앱 라이프사이클을 잘 이용하면,
런칭속도 개선에 힘을 보탤 수 있습니다.
오늘은 여기서 마무리할까 합니다.
30분이면 포스팅할 수 있을거란 애초의 기대에서 벗어나
3시간째 글을 쓰고 있네요...
아무래도 내일 제 시간이 일어날 수 있을지 걱정이 됩니다.
끝_
'IT > Tizen' 카테고리의 다른 글
[Tizen] 타이젠에 대한 문의는 여기서 하자! (0) | 2015.06.04 |
---|---|
[Tizen] 타이젠 앱에서 SQLite를 이렇게 사용해볼까요? (2) | 2015.06.02 |
[Tizen] 타이젠 앱 DB는 언제 어디서 초기화하는게 좋을까요? (1) | 2015.06.02 |
[Tizen] 타이젠 앱에 디버그 로그 심어보기 (0) | 2015.05.30 |
[Tizen] 타이젠 플랫폼이 선택한 DB는 SQLite (0) | 2015.05.28 |
[Tizen] 타이젠 SDK와 Git repository 연동하기 (0) | 2015.05.25 |
[Tizen] 타이젠 SDK 네이티브 앱 디렉토리 구조 (0) | 2015.05.25 |
[Tizen] 타이젠 스토어 182개국 오픈 중 3개국 유료판매가능 (0) | 2015.05.25 |
[Tizen] 타이젠 SDK와 Z1 연결이 되나요? 인증은 필수! (0) | 2015.05.25 |
[Tizen] 타이젠 Z1에 개발자모드 메뉴가 숨겨져 있는 까닭은? (2) | 2015.05.23 |