Androidアプリ開発

UIスレッドでタイムアウトを回避する

この記事は約26分で読めます。
記事内に広告が含まれています。
スポンサーリンク

こんにちは、まっさん(@Tera_Msaki)です。

この記事は Android スマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Java での開発経験、XML 構文規則、Android のアプリ開発経験がある方を対象としています。
Android のアプリ開発でお役にたててれば、嬉しいです。
(これから Android のアプリ開発や Java での開発を始めたい方への案内は、
記事の最後で紹介します)

この記事のテーマ


UIスレッドで実行した処理がANRエラーを発生しないようにする

ポイント

UIスレッドで実行した処理が5秒以上応答がない場合、Input event dispatching timed outのANRエラーが発生します。
これを防ぐには、UIスレッドとは別スレッドで処理を実行する必要があります。

対応前

UIスレッドで実行した処理が5秒以上応答がない場合にANRエラーが発生します
(ANRダイアログで「
wait」を選択すれば、アプリ自体は落ちません)

◎Javaコーディング【対応前】

ActivityResultLauncher.launchで処理を呼び出します。
結果は、ActivityResultContracts.StartActivityForResultで受け取ります。
処理中のUIコンポーネントを無効化し、処理を実行する。
(無効化したUIコンポーネントを有効化は、処理終了後に行う)

	:
    //バックアップ
    private final ActivityResultLauncher<Intent> activityResultLauncher1 = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    if (result.getData() != null) {
                        download.setVisibility(View.INVISIBLE);
                        upload.setVisibility(View.INVISIBLE);
                        progressBar.setVisibility(android.widget.ProgressBar.VISIBLE);
                    new Thread(new Runnable(){
  				        @Override
  				        public void run() {
                            getArchiveDatabase().dbExport(context, result.getData().getData());
  				    }}).start();
                }}
            });
	:

◎Logcat出力結果

2021-12-13 09:20:36.633 19608-19608/? D/Documents: shouldRestrictStorageAccessFramework = true, packageName = com.gymtec.archivepro
2021-12-13 09:21:00.102 1724-2299/? I/WindowManager: Input event dispatching timed out sending to com.gymtec.archivepro/com.gymtec.archivepro.view.SystemActivity.  Reason: 9dc79eb com.gymtec.archivepro/com.gymtec.archivepro.view.SystemActivity (server) is not responding. Waited 5003ms for FocusEvent(hasFocus=true)
2021-12-13 09:21:00.149 1724-2299/? W/InputDispatcher: Canceling events for 9dc79eb com.gymtec.archivepro/com.gymtec.archivepro.view.SystemActivity (server) because it is unresponsive
2021-12-13 09:21:00.657 30829-30842/com.gymtec.archivepro I/mtec.archivepr: Thread[3,tid=30842,WaitingInMainSignalCatcherLoop,Thread*=0xb400007588473010,peer=0x12dc0318,"Signal Catcher"]: reacting to signal 3
2021-12-13 09:21:00.726 30829-30842/com.gymtec.archivepro I/mtec.archivepr: Wrote stack traces to tombstoned
2021-12-13 09:21:01.986 1724-2299/? I/InputDispatcher: 9dc79eb com.gymtec.archivepro/com.gymtec.archivepro.view.SystemActivity (server) spent 6888ms processing FocusEvent(hasFocus=true)
2021-12-13 09:21:06.016 1724-31369/? E/ActivityManager: ANR in com.gymtec.archivepro (com.gymtec.archivepro/.view.SystemActivity)
    PID: 30829
    Reason: Input dispatching timed out (9dc79eb com.gymtec.archivepro/com.gymtec.archivepro.view.SystemActivity (server) is not responding. Waited 5003ms for FocusEvent(hasFocus=true))
    Parent: com.gymtec.archivepro/.view.SystemActivity
    Load: 0.0 / 0.0 / 0.0
    ----- Output from /proc/pressure/memory -----
    some avg10=0.00 avg60=0.00 avg300=0.00 total=14703203
    full avg10=0.00 avg60=0.00 avg300=0.00 total=4851181
    ----- End output from /proc/pressure/memory -----
    
    CPU usage from 0ms to 5816ms later (2021-12-13 09:21:00.150 to 2021-12-13 09:21:05.966):
      34% 30829/com.gymtec.archivepro: 18% user + 15% kernel / faults: 8823 minor
        14% 30860/RenderThread: 7.4% user + 7% kernel
        13% 30829/mtec.archivepro: 5.8% user + 7.9% kernel
        1.5% 30846/HeapTaskDaemon: 1.5% user + 0% kernel
        1.2% 30842/Signal Catcher: 1% user + 0.1% kernel
        0.1% 30847/ReferenceQueueD: 0.1% user + 0% kernel
        0.1% 30851/Binder:30829_2: 0.1% user + 0% kernel
        0.1% 30853/Binder:30829_3: 0% user + 0.1% kernel
       +0% 31409/pool-15-thread-: 0% user + 0% kernel
      0.3% 1557/media.codec: 0.1% user + 0.1% kernel / faults: 46878 minor
        0% 1557/omx@1.0-service: 0% user + 0% kernel
        0% 16672/HwBinder:1557_C: 0% user + 0% kernel
        0% 16842/HwBinder:1557_1: 0% user + 0% kernel
        0% 17025/HwBinder:1557_1: 0% user + 0% kernel
        0% 3606/HwBinder:1557_3: 0% user + 0% kernel
        0% 16235/HwBinder:1557_6: 0% user + 0% kernel
        0% 16677/HwBinder:1557_E: 0% user + 0% kernel
        0% 16843/HwBinder:1557_1: 0% user + 0% kernel
        0% 1618/Binder:1557_2: 0% user + 0% kernel
        0% 16236/HwBinder:1557_7: 0% user + 0% kernel
        0% 16805/HwBinder:1557_1: 0% user + 0% kernel
        0% 1612/Binder:1557_1: 0% user + 0% kernel
        0% 16238/HwBinder:1557_8: 0% user + 0% kernel
        0% 16680/HwBinder:1557_1: 0% user + 0% kernel
        0% 17023/HwBinder:1557_1: 0% user + 0% kernel
        0% 1653/HwBinder:1557_2: 0% user + 0% kernel
        0% 16676/HwBinder:1557_D: 0% user + 0% kernel
        0% 16678/HwBinder:1557_F: 0% user + 0% kernel
        0% 16681/HwBinder:1557_1: 0% user + 0% kernel
        0% 16867/HwBinder:1557_1: 0% user + 0% kernel
        0% 16239/HwBinder:1557_9: 0% user + 0% kernel
        0% 16671/HwBinder:1557_B: 0% user + 0% kernel
        0% 16679/HwBinder:1557_1: 0% user + 0% kernel
        0% 16682/HwBinder:1557_1: 0% user + 0% kernel
        0% 16726/HwBinder:1557_1: 0% user + 0% kernel
        0% 16727/HwBinder:1557_1: 0% user + 0% kernel
        0% 16804/HwBinder:1557_1: 0% user + 0% kernel
        0% 16844/HwBinder:1557_1: 0% user + 0% kernel
        0% 17024/HwBinder:1557_1: 0% user + 0% kernel
        0% 1628/HwBinder:1557_1: 0% user + 0% kernel
        0% 3671/HwBinder:1557_4: 0% user + 0% kernel
        0% 16231/HwBinder:1557_5: 0% user + 0% kernel
        0% 16240/HwBinder:1557_A: 0% user + 0% kernel
        0% 16868/HwBinder:1557_1: 0% user + 0% kernel
      24% 1724/system_server: 13% user + 11% kernel / faults: 8548 minor
        7.5% 31369/AnrConsumer: 1.8% user + 5.6% kernel
        6.2% 14553/Binder:1724_1F: 4.1% user + 2% kernel
        3.6% 1739/Signal Catcher: 2.4% user + 1.2% kernel
        3.2% 13275/Binder:1724_13: 2.2% user + 1% kernel
        0.6% 1743/HeapTaskDaemon: 0.6% user + 0% kernel
        0.5% 1724/Binder:1724_3: 0.3% user + 0.1% kernel
        0.5% 13253/Binder:1724_11: 0.5% user + 0% kernel
        0.5% 14550/Binder:1724_1C: 0.3% user + 0.1% kernel
        0.1% 1744/ReferenceQueueD: 0.1% user + 0% kernel
        0.1% 2015/android.ui: 0% user + 0.1% kernel
        0.1% 2016/android.io: 0.1% user + 0% kernel
        0.1% 2025/ActivityManager: 0.1% user + 0% kernel
        0.1% 2286/Binder:1724_4: 0.1% user + 0% kernel
        0.1% 3426/WifiHandlerThre: 0% user + 0.1% kernel
        0.1% 20804/RenderThread: 0.1% user + 0% kernel
      20% 1210/surfaceflinger: 9.8% user + 10% kernel / faults: 536 minor
        10% 1210/surfaceflinger: 6.2% user + 4.4% kernel
        1.7% 1451/app: 0.5% user + 1.2% kernel
        1.5% 1249/Binder:1210_2: 0.1% user + 1.3% kernel
        1.2% 1449/TimerDispatch: 0.3% user + 0.8% kernel
2021-12-13 09:21:06.016 1724-31369/? E/ActivityManager: CPU usage from 129ms to 486ms later (2021-12-13 09:21:00.278 to 2021-12-13 09:21:00.635):
      57% 1724/system_server: 14% user + 42% kernel / faults: 697 minor
        28% 31369/AnrConsumer: 0% user + 28% kernel
        25% 14553/Binder:1724_1F: 17% user + 7.1% kernel
        3.5% 3426/WifiHandlerThre: 0% user + 3.5% kernel
      67% 30829/com.gymtec.archivepro: 27% user + 40% kernel / faults: 875 minor
        45% 30829/mtec.archivepro: 18% user + 27% kernel
        18% 30860/RenderThread: 4.5% user + 13% kernel
        4.5% 30846/HeapTaskDaemon: 4.5% user + 0% kernel
        4.5% 30847/ReferenceQueueD: 4.5% user + 0% kernel
      20% 1210/surfaceflinger: 6.7% user + 13% kernel
        10% 1210/surfaceflinger: 10% user + 0% kernel
        3.3% 1247/surfaceflinger: 3.3% user + 0% kernel
        3.3% 1249/Binder:1210_2: 0% user + 3.3% kernel
        3.3% 1451/app: 0% user + 3.3% kernel
        3.3% 2104/Binder:1210_3: 3.3% user + 0% kernel
        3.3% 29619/Binder:1210_2: 3.3% user + 0% kernel
      23% 13212/com.google.android.providers.media.module: 0% user + 23% kernel
        3.8% 8440/Thread-14: 0% user + 3.8% kernel
        3.8% 26751/Thread-18: 0% user + 3.8% kernel
        3.8% 28788/Thread-29: 3.8% user + 0% kernel
        3.8% 31071/Thread-28: 0% user + 3.8% kernel
      6.4% 1053/android.hardware.graphics.composer@2.4-service: 3.2% user + 3.2% kernel
        6.4% 1053/composer@2.4-se: 3.2% user + 3.2% kernel
      7.2% 3505/com.android.systemui: 3.6% user + 3.6% kernel / faults: 10 minor
        7.2% 3505/ndroid.systemui: 3.6% user + 3.6% kernel
      9% 30526/kworker/u16:3: 0% user + 9% kernel
      3% 506/crtc_commit:134: 0% user + 3% kernel
    26% TOTAL: 10% user + 14% kernel + 0.7% irq + 0.3% softirq

対応後

UIスレッドで実行した処理が5秒以上応答がない場合でもANRエラーが発生しません

◎Javaコーディング【対応①】

UIスレッドとは別スレッドで処理を実行する
しかし、この実装で別スレッドで実行した処理でUIコンポーネントを操作すると別のエラーが発生します。
UIコンポーネントを操作しない場合は、この実装でも問題ないと思います。

	:
    //バックアップ
    private final ActivityResultLauncher<Intent> activityResultLauncher1 = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    if (result.getData() != null) {
                        download.setVisibility(View.INVISIBLE);
                        upload.setVisibility(View.INVISIBLE);
                        progressBar.setVisibility(android.widget.ProgressBar.VISIBLE);
			          new Thread(new Runnable(){
  				          @Override
  				          public void run() {
                     			getArchiveDatabase().dbExport(context, result.getData().getData());
                            }
                        }).start();
                    }
                }
            });
  :

◎Logcat出力結果

別スレッドでUI操作を行うと、Can’t create handler inside thread that has not called Looper.prepareのエラーが発生します。
別スレッドで実行した処理でUI操作する場合、UIスレッドのキューにジョブを投入する実装が必要です。

2021-12-13 09:28:19.249 32509-32678/com.gymtec.archivepro E/AndroidRuntime: FATAL EXCEPTION: Thread-3
    Process: com.gymtec.archivepro, PID: 32509
    java.lang.RuntimeException: Can't create handler inside thread Thread[Thread-3,5,main] that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:227)
        at android.os.Handler.<init>(Handler.java:129)
        at android.app.Activity.<init>(Activity.java:886)
        at androidx.core.app.ComponentActivity.<init>(ComponentActivity.java:48)
        at androidx.activity.ComponentActivity.<init>(ComponentActivity.java:220)
        at androidx.fragment.app.FragmentActivity.<init>(FragmentActivity.java:102)
        at androidx.appcompat.app.AppCompatActivity.<init>(AppCompatActivity.java:94)
        at com.gymtec.archivepro.utility.ArchiveUtilities.<init>(ArchiveUtilities.java:43)
        at com.gymtec.archivepro.utility.ExternalStorageWriter.<init>(ExternalStorageWriter.java:57)
        at com.gymtec.archivepro.database.ArchiveDatabaseHelper.dbExport(ArchiveDatabaseHelper.java:406)
        at com.gymtec.archivepro.view.SystemActivity$1.run(SystemActivity.java:73)
        at java.lang.Thread.run(Thread.java:923)

◎Javaコーディング【対応②】

UIスレッドのキューにジョブを投入する場合、Handlerを使用します。

    private final Handler               handler = new Handler(Looper.getMainLooper());
	:
    //バックアップ
    private final ActivityResultLauncher<Intent> activityResultLauncher1 = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
            result -> {
                if (result.getResultCode() == Activity.RESULT_OK) {
                    if (result.getData() != null) {
                        download.setVisibility(View.INVISIBLE);
                        upload.setVisibility(View.INVISIBLE);
                        progressBar.setVisibility(android.widget.ProgressBar.VISIBLE);
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                handler.post(() -> getArchiveDatabase().dbExport(context, result.getData().getData()));
                            }
                        }).start();
                    }
                }
            });
	:

今回は、ここまでです。

手になじむ、大きさが秀逸です♪

誤字脱字、意味不明でわかりづらい、
もっと詳しく知りたいなどのご意見は、
このページの最後にある
コメントか、
こちらから、お願いいたします♪

ポチッとして頂けると、
次のコンテンツを作成する励みになります♪

ブログランキング・にほんブログ村へ

これからAndroidのアプリ開発やJavaでの開発を始めたい方へ

アプリケーション開発経験がない方や、アプリケーション開発経験がある方でも、Java や C# などのオブジェクト指向言語が初めての方は、Android のアプリ開発ができるようになるには、かなりの時間がかかります。
オンラインスクールでの習得を、強くおススメします。

未経験者からシステムエンジニアを目指すのに最適かと、まずは無料相談から♪

未経験者からプログラマーを目指すのに最適かと、まずは無料カウンセリングから♪

カリキュラムとサポートがしっかりしています、お得なキャンペーンとかいろいろやっています♪

ゲーム系に強いスクール、UnityやUnrealEngineを習得するのに最適かと、まずは無料オンライン相談から♪

参考になったら、💛をポッチとしてね♪

スポンサーリンク
msakiをフォローする
スポンサーリンク

コメント欄

タイトルとURLをコピーしました