I.はじめに
この記事では、主に ArkUI に関連するスレッドとウォッチドッグ メカニズムを分析します。
2. ArkUI のスレッド
アビリティを適用して初めてインターフェースを作成するプロセスは、大まかに次のようになります。
説明します:
• AceContainer は、フロント エンド、タスク エグゼキュータ、リソース マネージャ、レンダリング パイプライン、ビューなどで構成されるコンテナ クラスです。ライフサイクル ドッキング、機能スケジューリング インターフェイス、UI レンダリングのさまざまな機能を提供します。
• アビリティは、実際には FA モデルの AceAbility であり、AceContainer コンテナ クラスの管理インターフェイスと一致します。AceAbility のライフサイクル関数 AceAbility::OnStart(const Want& want) で AceContainer インスタンスを作成します。
• Stage モデルの場合、UIContentImpl::CommonInitialize() 関数で AceContainer インスタンスを作成します。AceContainer は、コンストラクターでタスク エグゼキューターを作成して、ArkUI 関連のタスクを実行します。
void AceContainer::InitializeTask()
{
auto flutterTaskExecutor = Referenced::MakeRefPtr<FlutterTaskExecutor>();
flutterTaskExecutor->InitPlatformThread(useCurrentEventRunner_);
taskExecutor_ = flutterTaskExecutor;
// No need to create JS Thread for DECLARATIVE_JS
if (type_ == FrontendType::DECLARATIVE_JS) {
GetSettings().useUIAsJSThread = true;
} else {
flutterTaskExecutor->InitJsThread();
}
}
タスクには以下のようにいくつかの種類があり、各種類のタスク (バックグラウンド タスクを除く) は fml::TaskRunner によって実行されます。TaskRunner コードは、サードパーティ ライブラリ third_party\flutter\engine\flutter\common\task_runners.h にあります。実装の原則は、EventRunner および EventHandler の場合と同様です。
enum class TaskType : uint32_t {
PLATFORM = 0,
UI,
IO,
GPU,
JS,
BACKGROUND,
UNKNOWN,
};
FlutterTaskExecutor クラス図は次のとおりです。
説明します:
• タスク エグゼキュータを使用して、非同期 (PostTask) および同期 (PostSyncTask) タスクを実行できます。
• 非同期タスク: 現在のスレッドをブロックすることなく、指定されたタイプのスレッドにタスクをスローして処理します。
• 同期タスク: 処理のために指定されたタイプのスレッドにタスクをスローし、タスクが実行されるまで現在のスレッドをブロックして、現在のスレッドを続行します。
• たとえば、タッチ イベントの処理は、非同期タスクの形式で処理するために UI スレッドにスローされます。
auto&& touchEventCallback = [context = pipelineContext_, id = instanceId_](
const TouchEvent& event, const std::function<void()>& markProcess) {
ContainerScope scope(id);
context->GetTaskExecutor()->PostTask(
[context, event, markProcess]() {
context->OnTouchEvent(event);
CHECK_NULL_VOID_NOLOG(markProcess);
markProcess();
},
TaskExecutor::TaskType::UI);
};
3. さまざまなタイプの TaskRunner を初期化する方法は?
1. platformRunner_
InitPlatformThread 関数で初期化します。
void FlutterTaskExecutor::InitPlatformThread(bool useCurrentEventRunner)
{
#ifdef OHOS_STANDARD_SYSTEM
platformRunner_ = flutter::PlatformTaskRunner::CurrentTaskRunner(useCurrentEventRunner);
#else
fml::MessageLoop::EnsureInitializedForCurrentThread();
platformRunner_ = fml::MessageLoop::GetCurrent().GetTaskRunner();
#endif
FillTaskTypeTable(TaskType::PLATFORM);
}
標準の OHOS の場合、platformRunner_ は実際には
flutter::PlatformTaskRunner::CurrentTaskRunner(useCurrentEventRunner)
具体的な実装を見てください:
fml::RefPtr<fml::TaskRunner> PlatformTaskRunner::CurrentTaskRunner(bool useCurrentEventRunner)
{
return PlatformTaskRunnerAdapter::CurrentTaskRunner(useCurrentEventRunner);
}
fml::RefPtr<fml::TaskRunner> PlatformTaskRunnerAdapter::CurrentTaskRunner(bool useCurrentEventRunner)
{
if (useCurrentEventRunner) {
return fml::MakeRefCounted<PlatformTaskRunnerAdapter>(useCurrentEventRunner);
}
if (taskRunner_) {
return taskRunner_;
}
taskRunner_ = fml::MakeRefCounted<PlatformTaskRunnerAdapter>(useCurrentEventRunner);
return taskRunner_;
}
説明します:
platformRunner の実際のタイプは PlatformTaskRunnerAdapter です。
PlatformTaskRunnerAdapter は fml::TaskRunner を継承し、仮想 void PostTask(fml::closure task) などのインターフェイス関数を実装します。実際、レイヤーのカプセル化は、EventRunner および EventHandler メカニズムに基づいて行われます。コード内の useCurrentEventRunner の実パラメータは false です。これは、platformRunner が実際にタスクをメイン スレッドにスローして実行することを意味します。(MainEventRunner に対応するスレッドがメインスレッドで、MainEventRunner の初期化は Ability フレームワークの MainThread::Start() 関数で行われます)
PlatformTaskRunnerAdapter::PlatformTaskRunnerAdapter(bool useCurrentEventRunner)
: fml::TaskRunner(nullptr)
{
if (useCurrentEventRunner) {
eventRunner_ = OHOS::AppExecFwk::EventRunner::Current();
} else {
eventRunner_ = OHOS::AppExecFwk::EventRunner::GetMainEventRunner();
}
eventHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(eventRunner_);
}
void PlatformTaskRunnerAdapter::PostTask(fml::closure task)
{
eventHandler_->PostTask(std::move(task));
}
2.uiRunner、ioRunner、gpuRunner_
これら 3 種類の TaskRunner 初期化は、FlutterTaskExecutor::InitOtherThreads 関数にあります。
void FlutterTaskExecutor::InitOtherThreads(const flutter::TaskRunners& taskRunners)
{
uiRunner_ = taskRunners.GetUITaskRunner();
ioRunner_ = taskRunners.GetIOTaskRunner();
#ifdef NG_BUILD
gpuRunner_ = taskRunners.GetRasterTaskRunner();
#else
gpuRunner_ = taskRunners.GetGPUTaskRunner();
#endif
//...此处省略若干行
}
FlutterTaskExecutor::InitOtherThreads 関数のパラメータ taskRunners はどこから来たのですか?
一部の構成項目は FlutterAceView::CreateView() 関数で初期化され、次に flutter::OhosShellHolder オブジェクトが作成されます。
FlutterAceView* FlutterAceView::CreateView(int32_t instanceId, bool useCurrentEventRunner, bool usePlatformThread)
{
FlutterAceView* aceSurface = new Platform::FlutterAceView(instanceId);
if (aceSurface != nullptr) {
aceSurface->IncRefCount();
}
flutter::Settings settings;
settings.instanceId = instanceId;
settings.platform = flutter::AcePlatform::ACE_PLATFORM_OHOS;
#ifndef GPU_DISABLED
settings.enable_software_rendering = false;
#else
settings.enable_software_rendering = true;
#endif
#ifdef ENABLE_ROSEN_BACKEND
settings.use_system_render_thread = SystemProperties::GetRosenBackendEnabled();
#endif
settings.platform_as_ui_thread = usePlatformThread;
settings.use_current_event_runner = useCurrentEventRunner;
// ...此处省略若干行
auto shell_holder = std::make_unique<flutter::OhosShellHolder>(settings, false);
if (aceSurface != nullptr) {
aceSurface->SetShellHolder(std::move(shell_holder));
}
return aceSurface;
}
OhosShellHolder コンストラクターは、渡されたパラメーターに従って flutter::TaskRunners を作成します。
OhosShellHolder::OhosShellHolder(
flutter::Settings settings,
bool is_background_view)
: settings_(std::move(settings))
{
// ...此处省略若干行
// The current thread will be used as the platform thread. Ensure that the
// message loop is initialized.
fml::MessageLoop::EnsureInitializedForCurrentThread();
fml::RefPtr<fml::TaskRunner> gpu_runner;
fml::RefPtr<fml::TaskRunner> ui_runner;
fml::RefPtr<fml::TaskRunner> io_runner;
fml::RefPtr<fml::TaskRunner> platform_runner =
PlatformTaskRunnerAdapter::CurrentTaskRunner(settings_.use_current_event_runner);
if (is_background_view) {
auto single_task_runner = thread_host_.ui_thread->GetTaskRunner();
gpu_runner = single_task_runner;
ui_runner = single_task_runner;
io_runner = single_task_runner;
} else {
if (settings_.platform_as_ui_thread) {
ui_runner = platform_runner;
} else {
ui_runner = thread_host_.ui_thread->GetTaskRunner();
}
if (!settings_.use_system_render_thread) {
gpu_runner = thread_host_.gpu_thread->GetTaskRunner();
} else {
gpu_runner = ui_runner;
}
if (settings_.use_io_thread) {
io_runner = thread_host_.io_thread->GetTaskRunner();
} else {
io_runner = ui_runner;
}
}
flutter::TaskRunners task_runners(thread_label, // label
platform_runner, // platform
gpu_runner, // gpu
ui_runner, // ui
io_runner // io
);
注: 現在 OHOS では、構成パラメーターは次のとおりです。
上記のコード セグメントと比較すると、実際の gpu_runner、ui_runner、および io_runner は同じであり、タスクはすべて UI スレッドで実行されます。また、Stage モデルの場合、ui_runner と platform_runner は同じなので、Stage モデルの場合、TaskType::UI、TaskType::IO、TaskType::GPU、TaskType::PLATFORM タイプのタスクは、実際には のメイン スレッドによって実行されます。 .
3.jsRunner_
初期化は FlutterTaskExecutor::InitJsThread(bool newThread) 関数で行われます。
void FlutterTaskExecutor::InitJsThread(bool newThread)
{
if (newThread) {
jsThread_ = std::make_unique<fml::Thread>(GenJsThreadName());
jsRunner_ = jsThread_->GetTaskRunner();
} else {
jsRunner_ = uiRunner_;
}
PostTaskToTaskRunner(
jsRunner_, [weak = AceType::WeakClaim(this)] { FillTaskTypeTable(weak, TaskType::JS); }, 0);
}
説明: 宣言型フロント エンドの場合、newThread パラメーターは false ですが、JS フロント エンドの場合は true です。したがって、宣言型フロントエンド JS スレッドは実際には UI スレッドであり、JS フロントエンドでは、JS 関連のタスクを処理するために独立した JS スレッドが設定されます。
4. TaskType::BACKGROUND タイプのタスクを実行する方法は?
タイプ TaskType::BACKGROUND のタスクは、シングルトンの BackgroundTaskExecutor によって実行されます。時間のかかるバックグラウンド操作を処理するために、8 つのスレッドのスレッド プールが BackgroundTaskExecutor で維持されます。スレッド名は「ace.bg」で始まります。たとえば、RosenFontLoader が Web フォントをロードする場合、ダウンロード操作はバックグラウンド タスク スレッドで実行されます。
void RosenFontLoader::LoadFromNetwork(const OHOS::Ace::RefPtr<OHOS::Ace::PipelineBase>& context)
{
auto weakContext = AceType::WeakClaim(AceType::RawPtr(context));
context->GetTaskExecutor()->PostTask(
[weak = AceType::WeakClaim(this), weakContext] {
auto fontLoader = weak.Upgrade();
auto context = weakContext.Upgrade();
if (!fontLoader || !context) {
return;
}
std::vector<uint8_t> fontData;
if (!DownloadManager::GetInstance().Download(fontLoader->familySrc_, fontData) || fontData.empty()) {
return;
}
//...此处省略若干行
},
TaskExecutor::TaskType::BACKGROUND);
}
要約すると、ArkUI では、インターフェイスを持つ各アビリティに対して AceContainer が作成され、アビリティ ArkUI に関連するタスクを処理するために各 AceContainer に FlutterTaskExecutor が作成されます。異なるモデルに従って、ArkUI によって作成されるスレッドは異なります。
• Stage モデル アプリケーションの場合、ui スレッドはメイン スレッドを再利用します。Stage モデル アプリケーションは現在、宣言型フロントエンドであるため、js スレッドが ui スレッドを再利用します。したがって、ArkUI は、名前が「ace.bg」で始まる 8 つの追加のバックグラウンド タスク スレッドを作成するだけで済みます。
• FA モデルの適用では、8 つのバックグラウンド タスク スレッドに加えて、名前が「.ui」で終わるスレッドが、アビリティの数に応じていくつか作成されます。JS フロントエンドの場合、「jsThread-」で始まる名前のスレッドがいくつか作成されます。
4.ArkUIのウォッチドッグ
AceEngine はシングルトンであり、グローバルに一意です。WatchDog インスタンスは、AceEngine のコンストラクターで作成されます。FA モデルの適用のために、AceContainer::AttachView() 関数呼び出し
AceEngine::Get().RegisterToWatchDog(instanceId, taskExecutor_,
GetSettings().useUIAsJSThread);
保持している FlutterTaskExecutor をウォッチドッグに登録します。
ウォッチドッグは、FlutterTaskExecutor の UI スレッドと JS スレッドのみを保護します。UI スレッドと JS スレッドは実際には多重化のメイン スレッドであるため、Stage モデルのアプリケーションを ArkUI で保護する必要はありません。アビリティ フレームワークには、メイン スレッドを保護するためのウォッチドッグがあります。スレッド内に 3 秒以上のタスク処理がある場合、RawEventType::WARNING に対応するシステム イベントが hiview プラグイン プラットフォームに報告され、タスク処理が 5 秒を超える場合、RawEventType::FREEZE に対応するシステム イベントが報告されます。 hiview プラグイン プラットフォームに報告され、hiview プラグイン プラットフォーム appfreeze のダンプ ファイルが生成されます。
メインスレッドや UI スレッドがスタックして appfreeze が発生するのを防ぐため、アプリケーション開発を行う際は、アビリティのライフサイクル関数やコントロール クリック イベントなどのコールバック関数で時間のかかる操作を実行しないでください。