506

私はAndroidを初めて使用します。Looperクラスの内容と使い方を知りたいです。Android Looperクラスのドキュメントを読みましたが、完全に理解できません。いろいろなところで見ましたが、その目的がわかりません。Looper誰かが目的を定義し、可能であれば簡単な例を示すことによって私を助けることができますか?

4

13 に答える 13

421

ルーパーとは何ですか?

Looperは、キュー内のメッセージ(Runnables)を実行するために使用されるクラスです。通常のスレッドにはそのようなキューはありません。たとえば、単純なスレッドにはキューがありません。これは1回実行され、メソッドの実行が終了した後、スレッドは別のメッセージ(実行可能)を実行しません。

Looperクラスはどこで使用できますか?

誰かが複数のメッセージ(Runnables)を実行したい場合は、スレッドにキューを作成する責任があるLooperクラスを使用する必要があります。たとえば、インターネットからファイルをダウンロードするアプリケーションを作成しているときに、Looperクラスを使用して、ダウンロードするファイルをキューに入れることができます。

使い方?

prepare()ルーパーを準備する方法があります。次に、loop()メソッドを使用して現在のスレッドにメッセージループを作成できます。これで、ループを終了するまで、ルーパーはキュー内のリクエストを実行する準備が整いました。

ルーパーを準備するためのコードは次のとおりです。

class LooperThread extends Thread {
      public Handler mHandler;

      @Override
      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              @Override
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }
于 2011-09-29T13:17:25.683 に答える
371

GUIフレームワークのコンテキストでLooperが何であるかをよりよく理解できます。ルーパーは2つのことをするように作られています。

1)Looperは、run()メソッドが戻ると終了する通常のスレッドを、 Androidアプリが実行されるまで継続的に実行されるスレッドに変換します。これはGUIフレームワークで必要です(技術的には、run()メソッドが戻ると終了します。以下で私が何を意味するのかを明確にしてください)。

2)Looperは、実行するジョブがキューに入れられるキューを提供します。これは、GUIフレームワークでも必要です。

ご存知かもしれませんが、アプリケーションが起動されると、システムは「メイン」と呼ばれるアプリケーションの実行スレッドを作成します。Androidアプリケーションは通常、デフォルトで「メインスレッド」と呼ばれる単一のスレッドで完全に実行されます。しかし、メインスレッドは秘密の特別なスレッドではありません。これは、コードで作成するスレッドに似た通常のスレッドですnew Thread()。つまり、run()メソッドが返されると終了します。以下の例を考えてみてください。

public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

それでは、この単純な原則をAndroidアプリに適用してみましょう。Androidアプリが通常のスレッドで実行された場合はどうなりますか?「メイン」または「UI」またはアプリケーションを起動するものと呼ばれるスレッドで、すべてのUIを描画します。そのため、最初の画面がユーザーに表示されます。ならどうしよう?メインスレッドは終了しますか?いいえ、すべきではありません。ユーザーが何かをするまで待つべきですよね?しかし、どうすればこの動作を実現できますか?さて、私たちはObject.wait()またはで試すことができますThread.sleep()。たとえば、メインスレッドは最初の画面を表示するための最初のジョブを終了し、スリープします。新しいジョブがフェッチされると、起動します。つまり、中断されます。これまでのところ良好ですが、現時点では、複数のジョブを保持するためのキューのようなデータ構造が必要です。ユーザーが画面を連続してタッチし、タスクの完了に時間がかかる場合を考えてみてください。したがって、先入れ先出し方式で実行されるジョブを保持するためのデータ構造が必要です。また、割り込みを使用して、常に実行され、プロセスジョブが到着したときにスレッドを実装することは簡単ではなく、複雑で、多くの場合、保守不可能なコードにつながることを想像してみてください。そのような目的のために新しいメカニズムを作成したいのですが、それがLooperのすべてですルーパークラスの公式文書「スレッドにはデフォルトでメッセージループが関連付けられていません」と述べており、Looperは「スレッドのメッセージループを実行するために使用される」クラスです。これで、それが何を意味するのかを理解できます。

より明確にするために、メインスレッドが変換されるコードを確認しましょう。それはすべてActivityThreadクラスで発生します。そのmain()メソッドには、通常のメインスレッドを必要なものに変換する以下のコードがあります。

public final class ActivityThread {
    ...
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        Looper.loop();
        ...
    }
}

Looper.loop()メソッドは無限にループし、メッセージをデキューして一度に1つずつ処理します。

public static void loop() {
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
    }
}

つまり、基本的にLooperは、GUIフレームワークで発生する問題に対処するために作成されたクラスです。しかし、この種のニーズは他の状況でも発生する可能性があります。実際、これはマルチスレッドアプリケーションで非常に有名なパターンであり、Doug Leaによる「 Javaでのコンカレントプログラミング」で詳しく知ることができます(特に、4.1.4章「ワーカースレッド」が役立ちます)。また、この種のメカニズムはAndroidフレームワークに固有のものではないことを想像できますが、すべてのGUIフレームワークはこれにいくらか類似している必要があります。JavaSwingフレームワークにもほぼ同じメカニズムがあります。

于 2015-12-30T04:09:03.050 に答える
81

Looperを使用すると、タスクを1つのスレッドで順番に実行できます。そして、ハンドラーは、実行する必要のあるタスクを定義します。これは、この例で説明しようとしている典型的なシナリオです。

class SampleLooper extends Thread {
@Override
public void run() {
  try {
    // preparing a looper on current thread     
    // the current thread is being detected implicitly
    Looper.prepare();

    // now, the handler will automatically bind to the
    // Looper that is attached to the current thread
    // You don't need to specify the Looper explicitly
    handler = new Handler();

    // After the following line the thread will start
    // running the message loop and will not normally
    // exit the loop unless a problem happens or you
    // quit() the looper (see below)
    Looper.loop();
  } catch (Throwable t) {
    Log.e(TAG, "halted due to an error", t);
  } 
}
}

これで、他のいくつかのスレッド(UIスレッドなど)でハンドラーを使用して、実行するタスクをLooperに投稿できます。

handler.post(new Runnable()
{
public void run() {
//This will be executed on thread using Looper.
    }
});

UIスレッドには、UIスレッドでメッセージを処理できる暗黙のルーパーがあります。

于 2013-10-01T11:22:08.200 に答える
37

AndroidLooperはアタッチするラッパーでMessageQueueありThread、キュー処理を管理します。Androidのドキュメントでは非常にわかりにくいように見え、Looper関連するUIアクセスの問題に何度も直面する可能性があります。基本を理解していないと、扱いが非常に難しくなります。

ライフサイクル、使い方、inの使い方を説明した記事です。LooperLooperHandler

ここに画像の説明を入力してください

ルーパー=スレッド+メッセージキュー

于 2013-09-18T08:34:20.750 に答える
17

ルーパーとハンドラーの最も簡単な定義:

Looperは、スレッドをパイプラインスレッドに変換するクラスであり、Handlerは、他のスレッドからこのパイプにタスクをプッシュするメカニズムを提供します。

一般的な言い回しの詳細:

したがって、PipeLineスレッドは、ハンドラーを介して他のスレッドからより多くのタスクを受け入れることができるスレッドです。

Looperは、ループを実装するため、そのように名前が付けられています。次のタスクを実行し、実行してから、次のタスクを実行します。ハンドラーは、他のスレッドから毎回次のタスクを処理または受け入れるために使用され、ルーパー(スレッドまたはパイプラインスレッド)に渡されるため、ハンドラーと呼ばれます。

例:

Looper andHandlerまたはPipeLineThreadの非常に完璧な例は、バックグラウンドでネットワーク呼び出しごとに新しいスレッドを開始する代わりに、複数の画像をダウンロードするか、単一のスレッドで1つずつサーバー(Http)にアップロードすることです。

ルーパーとハンドラー、およびパイプラインスレッドの定義について詳しくは、こちらをご覧ください。

Android Guts:ルーパーとハンドラーの紹介

于 2016-08-16T11:12:14.420 に答える
11

ルーパースレッドを理解する

run()メソッドでタスクを実行し、その後終了するように設計された実行ユニットのJavaスレッド: ここに画像の説明を入力してください

しかし、Androidには、スレッドを存続させ、ユーザーの入力/イベントを待つ必要がある多くのユースケースがあります。UIスレッド別名Main Thread

AndroidのメインスレッドはJavaスレッドであり、アプリの起動時にJVMによって最初に開始され、ユーザーがそれを閉じることを選択するか、未処理の例外が発生するまで実行を続けます。

アプリケーションが起動されると、システムは「メイン」と呼ばれるアプリケーションの実行スレッドを作成します。このスレッドは、描画イベントを含む適切なユーザーインターフェイスウィジェットへのイベントのディスパッチを担当するため、非常に重要です。

ここに画像の説明を入力してください

ここで注意すべき点は、メインスレッドはJavaスレッドですが、ユーザーイベントをリッスンし続け、画面に60 fpsフレームを描画し、それでも各サイクルの後で死ぬことはありません。どうですか?

答えはLooperクラスです。Looperは、スレッドを存続させ、そのスレッドでタスクを実行するためのメッセージキューを管理するために使用されるクラスです。

デフォルトでは、スレッドにはメッセージループが関連付けられていませんが、runメソッドでLooper.prepare()を呼び出してから、Looper.loop()を呼び出すことで、メッセージループを割り当てることができます。

Looperの目的は、スレッドを存続させ、入力Messageオブジェクトの次のサイクルが計算を実行するのを待つことです。そうしないと、実行の最初のサイクルの後に破棄されます。

MessageLooperがオブジェクトキューを管理する方法をさらに深く掘り下げたい場合は、次のソースコードを参照してLooperclassください。

https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/os/Looper.java

以下は、を使用してクラスを作成し、クラスLooper Threadと通信する方法の例です。ActivityLocalBroadcast

class LooperThread : Thread() {

    // sendMessage success result on UI
    private fun sendServerResult(result: String) {
        val resultIntent = Intent(ServerService.ACTION)
        resultIntent.putExtra(ServerService.RESULT_CODE, Activity.RESULT_OK)
        resultIntent.putExtra(ServerService.RESULT_VALUE, result)
        LocalBroadcastManager.getInstance(AppController.getAppController()).sendBroadcast(resultIntent)
    }

    override fun run() {
        val looperIsNotPreparedInCurrentThread = Looper.myLooper() == null

        // Prepare Looper if not already prepared
        if (looperIsNotPreparedInCurrentThread) {
            Looper.prepare()
        }

        // Create a handler to handle messaged from Activity
        handler = Handler(Handler.Callback { message ->
            // Messages sent to Looper thread will be visible here
            Log.e(TAG, "Received Message" + message.data.toString())

            //message from Activity
            val result = message.data.getString(MainActivity.BUNDLE_KEY)

            // Send Result Back to activity
            sendServerResult(result)
            true
        })

        // Keep on looping till new messages arrive
        if (looperIsNotPreparedInCurrentThread) {
            Looper.loop()
        }
    }

    //Create and send a new  message to looper
    fun sendMessage(messageToSend: String) {
        //Create and post a new message to handler
        handler!!.sendMessage(createMessage(messageToSend))
    }


    // Bundle Data in message object
    private fun createMessage(messageToSend: String): Message {
        val message = Message()
        val bundle = Bundle()
        bundle.putString(MainActivity.BUNDLE_KEY, messageToSend)
        message.data = bundle
        return message
    }

    companion object {
        var handler: Handler? = null // in Android Handler should be static or leaks might occur
        private val TAG = javaClass.simpleName

    }
}

使用法

 class MainActivity : AppCompatActivity() {

    private var looperThread: LooperThread? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start looper thread
        startLooperThread()

        // Send messages to Looper Thread
        sendMessage.setOnClickListener {

            // send random messages to looper thread
            val messageToSend = "" + Math.random()

            // post message
            looperThread!!.sendMessage(messageToSend)

        }   
    }

    override fun onResume() {
        super.onResume()

        //Register to Server Service callback
        val filterServer = IntentFilter(ServerService.ACTION)
        LocalBroadcastManager.getInstance(this).registerReceiver(serverReceiver, filterServer)

    }

    override fun onPause() {
        super.onPause()

        //Stop Server service callbacks
     LocalBroadcastManager.getInstance(this).unregisterReceiver(serverReceiver)
    }


    // Define the callback for what to do when data is received
    private val serverReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val resultCode = intent.getIntExtra(ServerService.RESULT_CODE, Activity.RESULT_CANCELED)
            if (resultCode == Activity.RESULT_OK) {
                val resultValue = intent.getStringExtra(ServerService.RESULT_VALUE)
                Log.e(MainActivity.TAG, "Server result : $resultValue")

                serverOutput.text =
                        (serverOutput.text.toString()
                                + "\n"
                                + "Received : " + resultValue)

                serverScrollView.post( { serverScrollView.fullScroll(View.FOCUS_DOWN) })
            }
        }
    }

    private fun startLooperThread() {

        // create and start a new LooperThread
        looperThread = LooperThread()
        looperThread!!.name = "Main Looper Thread"
        looperThread!!.start()

    }

    companion object {
        val BUNDLE_KEY = "handlerMsgBundle"
        private val TAG = javaClass.simpleName
    }
}

代わりに非同期タスクまたはインテントサービスを使用できますか?

  • 非同期タスクは、バックグラウンドで短い操作を実行し、UIスレッドで進行状況と結果を提供するように設計されています。非同期タスクには、 128を超える非同期タスクを作成できないなどの制限があり、最大5つの非同期タスクThreadPoolExecutorしか許可されません。

  • IntentServicesまた、バックグラウンドタスクを少し長く実行するように設計されており、とのLocalBroadcast通信に使用できますActivity。ただし、サービスはタスクの実行後に破棄されます。あなたがそれをあなたがのようなヘックをする必要があるよりも長い間実行し続けたいならば while(true){...}

Looper Threadのその他の意味のあるユースケース:

  • サーバーがクライアントソケットをリッスンし続け、確認応答を書き戻す双方向ソケット通信に使用されます

  • バックグラウンドでのビットマップ処理。画像のURLをルーパースレッドに渡すと、フィルター効果が適用され、一時的な場所に保存されてから、画像の一時的なパスがブロードキャストされます。

于 2019-03-14T12:45:36.257 に答える
7

ルーパーにはsynchronized MessageQueue、キューに配置されたメッセージを処理するために使用されるがあります。

Thread特定のストレージパターンを実装します。

Looperにつき1つのみThread。主なメソッドには、、、が含まprepare()loop()ますquit()

prepare()Thread現在を。として初期化しLooperます。以下に示すようにクラスを使用するメソッドですprepare()staticThreadLocal

   public static void prepare(){
       ...
       sThreadLocal.set
       (new Looper());
   }
  1. prepare()イベントループを実行する前に、明示的に呼び出す必要があります。
  2. loop()メッセージが特定のスレッドのメッセージキューに到着するのを待つイベントループを実行します。次のメッセージが受信されると、loop()メソッドはメッセージをターゲットハンドラーにディスパッチします
  3. quit()イベントループをシャットダウンします。ループを終了しませんが、代わりに特別なメッセージをキューに入れます

LooperThreadいくつかのステップを介してプログラムすることができます

  1. 拡張するThread

  2. Looper.prepare()スレッドを初期化するための呼び出しLooper

  3. Handler受信メッセージを処理するために1つ以上を作成します

  4. Looper.loop()ループがに指示されるまでメッセージを処理するために呼び出しますquit()
于 2014-05-26T10:40:43.610 に答える
5

メソッドの完了後、Javaスレッドの寿命は終わりました。run()同じスレッドを再開することはできません。

Looperは、通常Threadをメッセージループに変換します。主な方法は次のLooperとおりです。

void prepare ()

現在のスレッドをルーパーとして初期化します。これにより、実際にループを開始する前に、このルーパーを参照するハンドラーを作成する機会が得られます。このメソッドを呼び出した後は必ずloop()を呼び出し、quit()を呼び出して終了してください。

void loop ()

このスレッドでメッセージキューを実行します。必ずquit()を呼び出してループを終了してください。

void quit()

ルーパーを終了します。

メッセージキュー内のメッセージを処理せずにloop()メソッドを終了させます。

Janisharによるこのmindorksの記事は、コアコンセプトをうまく説明しています。

ここに画像の説明を入力してください

Looperスレッドに関連付けられています。LooperUIスレッドが必要な場合は、Looper.getMainLooper()関連するスレッドを返します。

ハンドラーLooperに関連付ける必要があります。

Looper、、HandlerおよびHandlerThreadは、非同期プログラミングの問題を解決するAndroidの方法です。

を取得したらHandler、以下のAPIを呼び出すことができます。

post (Runnable r)

Runnablerをメッセージキューに追加します。ランナブルは、このハンドラーが接続されているスレッドで実行されます。

boolean sendMessage (Message msg)

現在の時刻より前のすべての保留中のメッセージの後で、メッセージをメッセージキューの最後にプッシュします。これは、このハンドラーに接続されたスレッドのhandleMessage(Message)で受信されます。

HandlerThreadは、ルーパーを持つ新しいスレッドを開始するための便利なクラスです。その後、ルーパーを使用してハンドラークラスを作成できます

Runnable一部のシナリオでは、 UIスレッドでタスクを実行できません。例:ネットワーク操作:ソケットでメッセージを送信し、URLを開き、以下を読んでコンテンツを取得しますInputStream

このような場合、HandlerThreadは便利です。メインスレッドの代わりにLooperオブジェクトを取得してonHandlerThreadを作成できます。HandlerHandlerThread

HandlerThreadコードは次のようになります。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

コード例については、以下の投稿を参照してください。

Android:スレッドで乾杯

于 2017-08-31T08:15:23.790 に答える
5

この答えは質問とは何の関係もありませんが、ここでのすべての答えでルーパーの使用と人々がハンドラーとルーパーを作成した方法は明らかに悪い習慣です(いくつかの説明は正しいですが)、私はこれを投稿する必要があります:

HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);

完全な実装の場合

于 2017-11-16T11:53:41.647 に答える
3

サービスで複数のダウンアイテムまたはアップロードアイテムを処理することは、より良い例です。

HandlerAsnycTask多くの場合、UI(スレッド)とワーカースレッド間でイベント/メッセージを伝播したりアクション遅らせたりするために使用されます。そのため、UIとの関連性が高くなります。

Aは、バックグラウンドでスレッド関連のキュー内のLooperタスク(Runnables、Futures )を処理​​します-ユーザーの操作やUIが表示されていない場合でも(アプリは呼び出し中にバックグラウンドでファイルをダウンロードします)。

于 2013-03-20T16:49:52.890 に答える
2

ルーパーとは何ですか?

ドキュメントから

Looper

Looperのメッセージループを実行するために使用されるクラスthread。デフォルトでは、スレッドにはメッセージループが関連付けられていません。prepare()作成するには、ループを実行するスレッドを呼び出してloop()から、ループが停止するまでメッセージを処理させます。

  • ALooperはメッセージ処理ループです。
  • Looperの重要な特徴は、Looperが作成されるスレッドに関連付けられていることです。
  • LooperクラスMessageQueueは、リストメッセージを含むを維持します。Looperの重要な特徴は、Looperが作成されるスレッドに関連付けられていることです。
  • は、ループを実装するため、そのLooperように名前が付けられています–次のタスクを取得し、それを実行してから、次のタスクを取得します。Handler誰かがより良い名前を発明できなかったので、これはハンドラーと呼ばれます
  • AndroidLooperは、Androidユーザーインターフェイス内のJavaクラスであり、Handlerクラスとともに、ボタンのクリック、画面の再描画、向きの切り替えなどのUIイベントを処理します。

使い方?

ここに画像の説明を入力してください

ルーパーの作成

スレッドは、実行後に呼び出すことによってとをLooper取得します。呼び出し元のスレッドを識別し、ルーパーとオブジェクトを作成して、スレッドを関連付けますMessageQueueLooper.prepare()Looper.prepare()MessageQueue

サンプルコード

class MyLooperThread extends Thread {

      public Handler mHandler; 

      public void run() { 

          // preparing a looper on current thread  
          Looper.prepare();

          mHandler = new Handler() { 
              public void handleMessage(Message msg) { 
                 // process incoming messages here
                 // this will run in non-ui/background thread
              } 
          }; 

          Looper.loop();
      } 
  }

詳細については、以下の投稿を確認してください

于 2018-11-27T06:48:42.440 に答える
0

ルーパークラスの目的をできるだけ簡単に説明しようと思います。通常のJavaのスレッドでは、runメソッドが実行を完了すると、スレッドはそのジョブを完了し、スレッドはその後は存続しなくなります。もう生きていない同じスレッドでプログラム全体でより多くのタスクを実行したい場合はどうなりますか?ああ、今問題がありますか?はい。より多くのタスクを実行したいのですが、スレッドはもう生きていません。ルーパーが私たちを救うためにやってくるところです。名前が示すようにルーパーはループを示唆しています。ルーパーは、スレッド内の無限ループにすぎません。したがって、quit()メソッドを明示的に呼び出すまで、スレッドは無限に存続します。無限に生きているスレッドでquit()メソッドを呼び出すと、スレッド内の無限ループで条件がfalseになり、無限ループが終了します。それで、スレッドは死ぬか、もう生きていません。また、ルーパーが接続されているスレッドでquit()メソッドを呼び出すことが重要です。そうしないと、ゾンビと同じようにシステムに存在します。したがって、たとえば、バックグラウンドスレッドを作成して、そのスレッドに対して複数のタスクを実行する場合です。単純なJavaのスレッドを作成し、Looperクラスを使用してルーパーを準備し、準備したルーパーをそのスレッドにアタッチします。これにより、終了するときはいつでもquit()を呼び出すことができるため、スレッドを必要なだけ長く存続させることができます。私たちのスレッド。したがって、ルーパーはスレッドを存続させ、同じスレッドで複数のタスクを実行できるようにします。完了したら、quit()を呼び出してスレッドを終了します。メインスレッドまたはUIスレッドで、一部のUI要素のバックグラウンドスレッドまたは非UIスレッドによって計算された結果を表示したい場合はどうなりますか?その目的のために、ハンドラーの概念があります。ハンドラーを介してプロセス間通信を行うか、ハンドラーを介して2つのスレッドが相互に通信できると言うことができます。したがって、メインスレッドにはハンドラーが関連付けられ、バックグラウンドスレッドはそのハンドラーを介してメインスレッドと通信し、メインスレッドの一部のUI要素で計算された結果を表示するタスクを実行します。ここでは理論だけを説明していることは知っていますが、概念を深く理解することが非常に重要であるため、概念を理解するようにしてください。そして、私はあなたをルーパーについての小さなビデオシリーズに連れて行く以下のリンクを投稿しています、

https://www.youtube.com/watch?v=rfLMwbOKLRk&list=PL6nth5sRD25hVezlyqlBO9dafKMc5fAU2&index=1

于 2021-01-01T16:36:11.577 に答える
0

Kotlinで例を挙げてみます。以下にコード例を示します。

まず、メインスレッド(Looper.getMainLooper())を要求するハンドラー(デフォルトのルーパーではなく、提供されているルーパー)から変数ハンドラーをインスタンス化する必要があります。

関数getAllCourses()はLiveDataを返す必要があるため、handler.postDelayed()を使用してメッセージキューに追加し、定数SERVICE_LATENCY_IN_MILLISで指定されたxミリ秒後に実行します。

より明確にするために、私の説明にもっと言葉遣いを詳しく説明してください。

class RemoteDataSource private constructor(private val jsonHelper: JsonHelper) {

    private val handler = Handler(Looper.getMainLooper())

    companion object {
        private const val SERVICE_LATENCY_IN_MILLIS: Long = 2000

        @Volatile
        private var instance: RemoteDataSource? = null

        fun getInstance(helper: JsonHelper): RemoteDataSource =
                instance ?: synchronized(this) {
                    RemoteDataSource(helper).apply { instance = this }
                }
    }

    fun getAllCourses(): LiveData<ApiResponse<List<CourseResponse>>> {
        EspressoIdlingResource.increment()
        val resultCourse = MutableLiveData<ApiResponse<List<CourseResponse>>>()
        handler.postDelayed({
            resultCourse.value = ApiResponse.success(jsonHelper.loadCourses())
            EspressoIdlingResource.decrement()
        }, SERVICE_LATENCY_IN_MILLIS)
        return resultCourse
    }
于 2021-06-07T03:30:01.377 に答える