KonyのスレッドAPIについて

目次
---

1. はじめに<br>
2. 実装方法<br>
3. まとめ


はじめに
---

本記事では、Konyの**Threading API**についてデモを用いて解説いたします。  

まず**スレッド（Thread）**とは、プログラムが実行されていく一連の流れのことです。
この流れは通常一本ですが、処理に時間がかかる重たい処理を実行する時などには、複数の流れを用意しないと詰まってしまう(ユーザのアクションに対応できない、別の処理が実行できない)場合もあります。

例えば、以下のような処理は複数のスレッドによって成り立っています。

- ブラウザを使用中、HTMLを読み込んでいる途中でも、戻るボタンを押せる
- 重いファイルをダウンロードしている時に、ダウンロードの進行度が表示される

Konyでは **kony.runOnMainThread**、**kony.runOnWorkerThread**というAPIを用いて、使用するスレッドを指定して処理を実行することができます。 


それでは、APIの詳細について説明していきます。

#### **kony.runOnMainThread**

非同期のAPIで、メインスレッドでJavaScriptコードを実行するのに使用します。

|パラメータ|説明|
|---|---|
|f [Function]【必須】|実行するコールバック関数を指定します。|
|args [Array]【必須】|**f** に渡されるパラメータを保持するJavaScript配列を指定します。|

#### **kony.runOnWorkerThread**
メインの処理と並行して実行できるマルチスレッドを提供するAPIです。

|パラメータ|説明|
|---|---|
|f [Function]【必須】|実行するコールバック関数を指定します。|
|args [Array]【必須】|**f** に渡されるパラメータを保持するJavaScript配列を指定します。|

Konyでは、スレッドAPIを使用するためのガイドラインが以下のように記載されています。


> ・スレッドAPIは、マルチスレッド環境をネイティブにサポートします。
> 
> ・アプリケーションは、複数の並行スレッドで構成できます。
> 
> ・メインスレッド(UIスレッド)は、ウィジェットにイベントを割り当てて、UIの要素を描画する役割を果たします。
> 
> ・メインスレッド(UIスレッド)が止まるような処理はNGです。メインスレッドでネットワークアクセスやデータベースクエリなどの時間のかかる操作を実行すると、ユーザーインターフェイスがブロックされます。
> 
> ・メインスレッド(UIスレッド)の外部からUIコンポーネントにアクセスしないでください。 
> 
> JavaScriptスレッド：
>   ・UIの更新を必要としないJavaScriptで記述されたアプリケーションロジックは、メインスレッドまたはUIスレッドとは異なるスレッドで実行されます。
> 
>   ・UIを更新する操作は、メインスレッドまたはUIスレッドに投稿されます。
> 
>   ・API kony.runOnMainThread（function、args）を使用して、UIスレッドでJavaScriptバインディングとJavaScriptロジックを実行します。

注意：下記の場合は、非同期ではなく同期モードで実行されます。

例） 
- Mainスレッド上でrunOnMainThread()を実行した時
- Workerスレッド上でrunOnWorkerThread()を実行した時

<br>
APIの説明は以上なので、
次に、デモを用いて実際の挙動について見てみましょう。

作成したデモ
---

実際にrunOnMainThread()、runOnWorkerThread()を使って、
以下の処理を各スレッドで実行してログを見ていきたいと思います。

- Mainスレッド: ラベルの変更
- Workerスレッド: 終了まで5秒かかる処理

![ss.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/05b2b00a-4ae3-c555-27bd-ef67d204d1c8.gif)


まず、デモ用にFormを作成し、**ラベル : lblMain** と **ボタン : btnRun** を配置しました。
紹介する2つのAPIは iOS/Android用なので、Mobileで作成します。
![スクリーンショット 2021-06-21 18.31.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/a37513da-aea9-434c-c75a-125dbfb88db8.png)


以下は、FromControllerに記載したソースです。


```js:FromController
define({ 

  onClickBtn : function() {
①    kony.print("___onClick");
    kony.runOnMainThread(this.mainFunction, [this.view.lblMain]);
②    kony.print("___finish_onClick");
  },

  wokerFunction　: function() {
    // 時間のかかる処理
④  kony.print("___start_workerThread");
    var dt1 = new Date().getTime();
    var dt2 = new Date().getTime();
    var dtDiff = 0;
    var count = 0;

    // 5秒後に処理終了
    while (dtDiff < 5000){
      dt2 = new Date().getTime();
      dtDiff = dt2 - dt1;
      //2.5秒経過ごとにログを出力
      if(dtDiff % 2500 === 0){
        count++;
⑤ ⑦    kony.print("___count_workerThread : " + count);
      }
    }
⑧  kony.print("___finish_workerThread");
  },

  mainFunction　: function(widget) {
    kony.runOnWorkerThread(this.wokerFunction, [this.view.lblMain]);
    // テキストを変更する
③  kony.print("___start_mainThread");
    widget.text = "【 Main Thread 】";
⑥  kony.print("___finish_mainThread");
  },


});
```

``wokerFunction`` では、Whileと``Date().getTime()``※現在時刻の取得 
を使って、5秒後に終了する処理を書いています。  
また、進捗がわかるように2.5秒に一回ログを出力させています。

``mainFunction``では、MainスレッドからrunOnWorkerThreadを呼び出して、
その後 引数として受け取っているLabelのテキストを変更する処理を書いています。


#### 出力されるログ

ボタン押下後、Android StudioのLogcatで確認すると以下のようなログが出力されます。

![スクリーンショット 2021-06-21 17.40.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/d792a156-b60a-77e0-8529-634afc2c113b.png)


MainとWorkerに分けて見てみましょう。

- <font color="Gold">黄色い枠</font>が Mainスレッドで実行されている処理
- <font color="OrangeRed">赤い枠</font>が 別スレッドで実行されている処理

![スクリーンショット 2021-06-21 17.39.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/094ac630-cc10-4e60-3988-31ed27122b2f.png)
![スクリーンショット 2021-06-21 17.37.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/cae7397d-6394-3124-67f1-47c7f4f7a9d2.png)


 **ログについての説明**
使用されているスレッドをログから確認することができます。

 2021-06-21 15:54:08.834 **12941-12965**/com.orgname.appp D/StandardLib: ___onClick

ログメッセージの形式は次のとおりです。

<font color="DeepPink">date time **PID-TID**/package priority/tag: message</font>

使用スレッドは、ログの **PID-TID** の部分で確認することができます。 
PIDとTIDが同じ場合は基本となるスレッド(メインスレッド)が使用されています。

> PID はプロセス ID の略で、TID はスレッド ID の略です。スレッドが 1 つしか存在しない場合、これらは同一になることがあります
https://developer.android.com/studio/debug/am-logcat?hl=ja


#### 解説

##### ①・②
![スクリーンショット 2021-06-21 17.46.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/f643ac13-827d-ab88-408c-07c387638237.png)


```js
  onClickBtn : function() {
①    kony.print("___onClick");
    kony.runOnMainThread(this.mainFunction, []);
②    kony.print("___finish_onClick");
  },
```

①ボタン押下によって``___onClick``が出力されます。
②kony.runOnMainThread()は非同期のため、次に``___finish_onClick``が出力されます。

※ controller内の``onClickBtn``で出力しているログは、メインではなく``runOnWorkerThread``で使われるスレッドと同様のスレッドが使われるようです。


##### ③〜⑧

![スクリーンショット 2021-06-21 17.46.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/c7c06715-d8a4-11e6-714a-c2f859ae77ea.png)

まず、Workerスレッドとは別スレッド(Mainスレッド)から、``kony.runOnWorkerThread``を呼び出しているため非同期モードとなり、Workerスレッドの処理を待たずに③が実行されています。

```js
  mainFunction　: function(widget) {
    kony.runOnWorkerThread(this.wokerFunction, []);
    // テキストを変更する
③  kony.print("___start_mainThread");
    widget.text = "【 Main Thread 】";
⑥  kony.print("___finish_mainThread");
  },
```

その後、並行して``wokerFunction``が実行され、④・⑤が出力されます。
次は``mainFunction``の処理が終了することで⑥が出力されて、``wokerFunction``にある残りの⑦・⑧が出力されます。


```js
  wokerFunction　: function() {
    // 時間のかかる処理
④  kony.print("___start_workerThread");
    var dt1 = new Date().getTime();
    var dt2 = new Date().getTime();
    var dtDiff = 0;
    var count = 0;

    // 5秒後に処理終了
    while (dtDiff < 5000){
      dt2 = new Date().getTime();
      dtDiff = dt2 - dt1;
      //2.5秒経過ごとにログを出力
      if(dtDiff % 2500 === 0){
        count++;
⑤ ⑦    kony.print("___count_workerThread : " + count);
      }
    }
⑧  kony.print("___finish_workerThread");
  },
```

![ss.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/592264/05b2b00a-4ae3-c555-27bd-ef67d204d1c8.gif)

デモの説明は以上です。

: 注意 : 
[はじめに](https://qiita.com/drafts/0959eaa3bdcef82183c2/edit#:~:text=%E3%81%BE%E3%81%A8%E3%82%81-,%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB,-%E6%9C%AC%E8%A8%98%E4%BA%8B%E3%81%A7%E3%81%AF)の最後でもご紹介いたしましたが、controller内のメソッドでの処理は、runOnWorkerThreadで使われるスレッドと同様のスレッドが使われるようでした。

今回はMainスレッドの中で呼び出しましたが、
普通にrunOnWorkerThread()を実行すると、以下の理由から同期モードで実行されてしまうので、中身が全て実行されるまで待機状態になってしまいます。
使用の際は、使用元のメソッドがどのスレッドで実行されているかを確認しましょう。

> 注意：下記の場合は、非同期ではなく同期モードで実行されます。 
例） 
- Mainスレッド上でrunOnMainThread()を実行した時
- Workerスレッド上でrunOnWorkerThread()を実行した時




まとめ
---
今回は**kony.runOnMainThread**、**kony.runOnWorkerThread**というAPIの使用説明・解説をいたしました。本記事がお役に立てば幸いです。

#### 参考

https://docs.kony.com/konylibrary/visualizer/viz_api_dev_guide/content/threading_apis.htm

https://docs.kony.com/konylibrary/visualizer/viz_api_dev_guide/content/kony_functions_threadingapis.htm