Upload
masami-yabushita
View
3.764
Download
3
Embed Size (px)
DESCRIPTION
Citation preview
バッテリー監視の為にバックグラウンドタスクについて調べたらなく
なってたから作ってみた話 のはずだった
株式会社
グローバルサイバーグループ
藪下 正美
自己紹介
• 名前 藪下 正美
• 会社 株式会社グローバルサイバーグループ
• どんな人?
– @aoi_nagatsuki
–プログラミング言語とかスマホとか好き
弊社紹介
• 株式会社グローバルサイバーグループ
–やる気と人間性を大切にする総合開発企業
–とかはどうでもよくて
• ブログやってます
–GCG研究所で検索!
–ネタ募集中!
–空いてる時に調べてブログに書くのでなんか聞いてね!
今日のアジェンダ
• バッテリーアプリを作ろうと思った経緯
– バックグラウンドアプリを作ってみた
– 最近の環境では動かないと思っていたバックグラウンドアプリが動いたので調べてみる事にした
• バックグラウンドアプリの仕組み
– バックグラウンドサービスマネージャ
– manifestからいろいろ読み込む
– frameの保存
– frameって何者
今日のアジェンダ
• Gaiaだけだと怒られるのでイベントリスナを掘り下げてみる – addEventListner
–グローバルなwindowオブジェクトを見てみる
– nsEventListenerManager
–各関連イベントは誰がどう投げているのか • mozbrowseropenwindowから掘り下げる
• イベントのディスパッチの流れ
• イベントディスパッチまとめ
バッテリーアプリを作ろうと思った経緯
• 手前味噌ながらここの記事にあるようにある日デバッグビルドを試してみた。
– [Firefox OS][FxOS][Gecko]デバッグ情報付きのビルド | GCG研究所
– http://www.gcg.bz/labo_blog/?p=504
• デバッグビルドしたsoを端末にプッシュして動かしているととても電池が減った。
• 電池の減り具合をモニタしたくなった。
バックグラウンドアプリを作ってみた
• バッテリー監視のためまた手前味噌ながらバックグラウンドアプリを作ってみた。 – バッテリー監視の為にバックグラウンドタスクについて調べたらなくなってたから泣く泣くタイマーAPIを使ってみた話 のはずだった
– http://www.slideshare.net/aoitan/api-28631339
• この時事前情報として最近のFxOSではバックグラウンドアプリは作れないと聞いていた – なので続き物として今回バックグラウンドタスクを作ってみるはずだった
• でもやってみると使えてしまった。。。
最近の環境では動かないと思っていたバックグラウンドアプリが動いたので調べてみる事にした
• 使えてしまったものは仕方ないので予定を変更してバックグラウンドアプリがどう動くのか見てみた
• というのがこれまでのあらすじ
バックグラウンドアプリの仕組み
• 本題に入って
• バックグラウンドアプリの仕組みは意外と簡単
バックグラウンドサービスマネージャ
• systemアプリが起動するときにbackground_service.jsのBackgroundServiceManagerがwidnowオブジェクトにいくつかイベントを登録する – ${B2G}/gaia/apps/system/js/background_serv
ice.js
–
window.addEventListener('mozbrowseropenwindow', function bsm_winopen(evt) { window.addEventListener('mozbrowserclose', function bsm_winclose(evt) { window.addEventListener('mozbrowsererror', function bsm_winclose(evt) { window.addEventListener('applicationinstall', function bsm_oninstall(evt) { window.addEventListener('applicationuninstall', function bsm_oninstall(evt) {
manifestからいろいろ読み込む
• applicationinstallのリスナ内で
• ```javascript:backgorund_service.js • var app = evt.detail.application; • ```
• なappで
• ```javascript:backgorund_service.js • var url = origin + app.manifest.background_page; • open(manifestURL, AUTO_OPEN_BG_PAGE_NAME, url); • ```
• な感じの処理をしている。 • background_pageにはバックグラウンド動作させたいhtmlのURLが入る。 • 他にもアプリ名とかとっている
var app = evt.detail.application;
var url = origin + app.manifest.background_page; open(manifestURL, AUTO_OPEN_BG_PAGE_NAME, url);
frameの保存
• framesにbackgroundserviceを持つアプリを集めている
• 更にsystemアプリのbodyに入ってる
• ```javascript:background_service.js
• document.body.appendChild(frame);
• ```
frameって何者
• framesに入るのはiframe
– 以下のようなオブジェクトが入ってるっぽい
• ```javascript • frame: { • 'mozbrowser': 'mozbrowser', • 'mozapp': manifestURL, // アプリのマニフェストのURL • 'name': name, // アプリ名 • 'remote': true, • 'src': url, // バックグラウンド動作するHTMLのURL • 'className': 'backgroundWindow', • 'dataset': { • 'frameType': 'background', • 'frameName': name • } • } • ```
frame: { 'mozbrowser': 'mozbrowser', 'mozapp': manifestURL, // アプリのマニフェストのURL 'name': name, // アプリ名 'remote': true, 'src': url, // バックグラウンド動作するHTMLのURL 'className': 'backgroundWindow', 'dataset': { 'frameType': 'background', 'frameName': name } }
Gaiaだけだと怒られるのでイベント処理を掘り下げてみる
• Gecko勉強会でGaiaだけのお話とかなしですよね。。。
• という事でバックグラウンドアプリ自体の仕組みは結局の所systemアプリとして動くページがロードされてるだけっぽいのでバックグラウンドサービスマネージャが動くために必要なイベント処理についてちょっと掘り下げる。 – と言ってもCOMの中までは終えてないので今後の課題。
addEventListner再掲
• systemアプリが起動するときにbackground_service.jsの
BackgroundServiceManagerがwidnowオブジェクトにいくつかイベントを登録する
• ``` • window.addEventListener('mozbrowseropenwindow',
function bsm_winopen(evt) { • window.addEventListener('mozbrowserclose', function
bsm_winclose(evt) { • window.addEventListener('mozbrowsererror', function
bsm_winclose(evt) { • window.addEventListener('applicationinstall', function
bsm_oninstall(evt) { • window.addEventListener('applicationuninstall', function
bsm_oninstall(evt) { • ```
window.addEventListener('mozbrowseropenwindow', function bsm_winopen(evt) { window.addEventListener('mozbrowserclose', function bsm_winclose(evt) { window.addEventListener('mozbrowsererror', function bsm_winclose(evt) { window.addEventListener('applicationinstall', function bsm_oninstall(evt) { window.addEventListener('applicationuninstall', function bsm_oninstall(evt) {
グローバルなwindow オブジェクトを見てみる
• グローバルオブジェクトのwindowにぶら下がっている
addEventListenerなので – cpp:gecko/dom/base/nsGlobalWindow.cppあたりにいるはず
• ```cpp:gecko/dom/base/nsGlobalWindow.cpp • NS_IMETHODIMP • nsGlobalWindow::AddEventListener(const nsAString& aType, • nsIDOMEventListener *aListener, • bool aUseCapture, bool aWantsUntrusted, • uint8_t aOptionalArgc) • ```
– こんなのがいた
NS_IMETHODIMP nsGlobalWindow::AddEventListener(const nsAString& aType, nsIDOMEventListener *aListener, bool aUseCapture, bool aWantsUntrusted, uint8_t aOptionalArgc)
グローバルなwindow オブジェクトを見てみる
– キモはこれ
•
```cpp:gecko/dom/base/nsGlobalWindow.cpp
• nsEventListenerManager* manager = GetListenerManager(true);
• NS_ENSURE_STATE(manager); • manager->AddEventListener(aType,
aListener, aUseCapture, aWantsUntrusted); • ```
nsEventListenerManager* manager = GetListenerManager(true); NS_ENSURE_STATE(manager); manager->AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted);
グローバルなwindow オブジェクトを見てみる
– 要するにイベントリスナマネージャとやらにAddEventListenerしている
•
```cpp:gecko/content/events/src/nsEventListenerManager.cpp
• void • nsEventListenerManager::AddEventListener(nsIDOME
ventListener *aListener, • uint32_t aType, • nsIAtom* aTypeAtom, • int32_t aFlags, • bool aHandler) • ```
void nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener, uint32_t aType, nsIAtom* aTypeAtom, int32_t aFlags, bool aHandler)
nsEventListenerManager
• ぱっと見すごく長いけどイベントリスナマネージャのAddEventListenerでは以下の部分がキモ
• ```cpp:gecko/content/events/src/nsEventListenerManager.cpp • ls = mListeners.AppendElement(); • ls->mListener = aListener; • ls->mEventType = aType; • ls->mTypeAtom = aTypeAtom; • ls->mFlags = aFlags; • ls->mListenerIsHandler = aHandler; • ls->mHandlerIsString = false; • ```
• 要するにリスナの配列に渡されたリスナを格納してるだけ
– メソッド全体で長いのはフラグとかタイプによって後処理を変えたりするための値の加工と保持とか別のイベントリスナへの登録とか
ls = mListeners.AppendElement(); ls->mListener = aListener; ls->mEventType = aType; ls->mTypeAtom = aTypeAtom; ls->mFlags = aFlags; ls->mListenerIsHandler = aHandler; ls->mHandlerIsString = false;
各関連イベントは誰がどう投げているのか
Mozbrowseropenwindow から掘り下げる
• gecko/dom/browser-
element/BrowserElementParent.cppにDispatchOpenWindowEventがいる
• キモの部分は
• ```cpp:gecko/dom/browser-element/BrowserElementParent.cpp
• bool dispatchSucceeded = • DispatchCustomDOMEvent(aOpenerFrameElement, •
NS_LITERAL_STRING("mozbrowseropenwindow"), • detail); • ```
bool dispatchSucceeded = DispatchCustomDOMEvent(aOpenerFrameElement, NS_LITERAL_STRING("mozbrowseropenwindow"), detail);
Mozbrowseropenwindow から掘り下げる
• そのDispatchCustomDOMEventはこんな感じ
• ```cpp:gecko/dom/browser-element/BrowserElementParent.cpp
• nsEventDispatcher::CreateEvent(presContext, nullptr, • NS_LITERAL_STRING("customevent"), • getter_AddRefs(domEvent)); • ```
• ```cpp:gecko/dom/browser-
element/BrowserElementParent.cpp • nsCOMPtr<nsIDOMCustomEvent> customEvent =
do_QueryInterface(domEvent); • ```
nsEventDispatcher::CreateEvent(presContext, nullptr, NS_LITERAL_STRING("customevent"), getter_AddRefs(domEvent));
nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
Mozbrowseropenwindow から掘り下げる
• ```cpp:gecko/dom/browser-element/BrowserElementParent.cpp • customEvent->InitCustomEvent(aEventName, • /* bubbles = */ true, • /* cancelable = */ false, • detailVariant); • customEvent->SetTrusted(true); • // Dispatch the event. • nsEventStatus status = nsEventStatus_eIgnore; • rv = nsEventDispatcher::DispatchDOMEvent(aFrameElement,
nullptr, • domEvent, presContext, &status); • ```
• カスタムイベントオブジェクトを作ってDispatchDOMEventしている
customEvent->InitCustomEvent(aEventName, /* bubbles = */ true, /* cancelable = */ false, detailVariant); customEvent->SetTrusted(true); // Dispatch the event. nsEventStatus status = nsEventStatus_eIgnore; rv = nsEventDispatcher::DispatchDOMEvent(aFrameElement, nullptr, domEvent, presContext, &status);
イベントのディスパッチの流れ
• DispatchCustomDOMEventで呼ばれているnsEventDispatcher::DispatchDOMEventから
• ```cpp:gecko/content/events/src/nsEventDispatcher.cpp • return nsEventDispatcher::Dispatch(aTarget,
aPresContext, innerEvent, • aDOMEvent, aEventStatus); • } else if (aEvent) { • return nsEventDispatcher::Dispatch(aTarget,
aPresContext, aEvent, • aDOMEvent, aEventStatus); • ```
• こんなかんじでDispatchが呼ばれて
return nsEventDispatcher::Dispatch(aTarget, aPresContext, innerEvent, aDOMEvent, aEventStatus); } else if (aEvent) { return nsEventDispatcher::Dispatch(aTarget, aPresContext, aEvent, aDOMEvent, aEventStatus);
イベントのディスパッチの流れ
• ```cpp:gecko/content/events/src/nsEventDispatcher.cpp • /* static */ nsresult • nsEventDispatcher::Dispatch(nsISupports* aTarget, • nsPresContext* aPresContext, • nsEvent* aEvent, • nsIDOMEvent* aDOMEvent, • nsEventStatus* aEventStatus, • nsDispatchingCallback* aCallback, • nsCOMArray<nsIDOMEventTarget>*
aTargets) • { • めっちゃ長い! • } • ```
/* static */ nsresult nsEventDispatcher::Dispatch(nsISupports* aTarget, nsPresContext* aPresContext, nsEvent* aEvent, nsIDOMEvent* aDOMEvent, nsEventStatus* aEventStatus, nsDispatchingCallback* aCallback, nsCOMArray<nsIDOMEventTarget>* aTargets) { めっちゃ長い! }
イベントディスパッチまとめ
• 時間切れで追いきれてませんがざっくり見た感じ
– カスタムイベントのオブジェクトからDispatchCustomEventされると
– nsEventDispatcherのDispatchまで流れてくる
– nsEventDispatcher::Dispatchでは • イベントのオブジェクトを作る (topEtci等々)
• 送信先のリストを作る (postVisitor)
– 対象のDOM要素から親へ親へたどってる?
» 教えてエライ人!
イベントディスパッチまとめ
• ```cpp:gecko/content/events/src/nsEventDispatcher.cpp
• rv = topEtci->HandleEventTargetChain(postVisitor,
• NS_EVENT_FLAG_BUBBLE |
• NS_EVENT_FLAG_CAPTURE,
• aCallback, • false, • &pusher); • ```
rv = topEtci->HandleEventTargetChain(postVisitor, NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_CAPTURE, aCallback, false, &pusher);
イベントディスパッチまとめ
• でpostVisitorに詰めた配信対象のDOM要素に対してイベントオブジェクトを投げつけていくはず – 教えてエライ人!
– mozbrowseropenwindow以外のイベントが見つからないけどカスタムイベントじゃないから?
• 教えてエライ人!