モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

目次目次を開く/閉じる

Android Service コンポーネントの基本的な使い方

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2017/08/05
最終更新最終更新
2021/09/07
記事区分記事区分
一般公開

目次

    Microservicesアーキテクチャを実践中!

    Android アプリケーションは一つ以上の Activity、Service を含みます。Service は UI をもたず、長時間かかる処理をバックグラウンドで行うためのコンポーネントです。基本的な使い方をまとめます。

    関連する公式ドキュメント

    単純な Service (非同期処理なし)

    Android アプリケーションにおいて、既定ではすべてのアプリケーションのスレッドはアプリケーション個別のプロセス内に生成されます。プロセス内のスレッドには、UI を操作する処理を実行する main スレッド (UI スレッド) や、バックグラウンド処理を行うための worker スレッドが存在します。以下の MyService は Service を直接継承しており、MainActivity から startService() すると、main スレッドで onStartCommand() の処理が実行されます。そのため、以下のサンプルコードに実用性はなく、一般の用途であれば、worker スレッドを利用する、後述の IntentService を継承した MyIntentService を利用すべきです。

    MyService.java

    onStartCommand() で main スレッドを 5 秒間占有してみます。また、onStartCommand() の返り値で、システムによって MyService が強制終了させられたときの挙動を指定できます。IntentService の場合と同様に、START_STICKY によって、サービスが Android システムによって kill されたらサービスを再作成して起動するように設定しています。

    package com.example.mycompany.myapp;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.support.annotation.Nullable;
    import android.util.Log;
    
    public class MyService extends Service {
    
        private final String TAG = "MyService";
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.d(TAG, "created");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d(TAG, "started");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.d(TAG, "started/done");
            return START_STICKY;
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            // https://developer.android.com/guide/components/bound-services.html
            return null;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.d(TAG, "destroyed");
        }
    }
    

    MainActivity.java

    MainActivity の myStartButton/myStopButton を押すとサービスが起動/停止します。それぞれ、MyService をクラス名として設定した Explicit intent を引数にして startService() および stopService() を実行しています。

    package com.example.mycompany.myapp;
    
    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button startButton = (Button)findViewById(R.id.myStartButton);
            startButton.setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Log.d(TAG, "start service");
                            Intent intent = new Intent(MainActivity.this, MyService.class);
                            startService(intent);
                            Log.d(TAG, "start service/done");
                        }
                    }
            );
    
            Button stopButton = (Button)findViewById(R.id.myStopButton);
            stopButton.setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Log.d(TAG, "stop service");
                            Intent intent = new Intent(MainActivity.this, MyService.class);
                            stopService(intent);
                            Log.d(TAG, "stop service/done");
                        }
                    }
            );
        }
    }
    

    AndroidManifest.xml

    Activity の場合と同様に、Service コンポーネントの存在を Android システムに認識させるためには AndroidManifest.xml を編集する必要があります。

    ...
    <manifest... package="com.example.mycompany.myapp">
        <application... >
            <activity android:name=".MainActivity">
                ...
            </activity>
            <service android:name=".MyService" />  ←追記
        </application>
    </manifest>
    

    実行例

    UI がフリーズするため、警告がログに出力されています。

    08-06 21:58:00.637 1153-1153/com.example.mycompany.myapp D/MainActivity: start service
    08-06 21:58:00.653 1153-1153/com.example.mycompany.myapp D/MainActivity: start service/done
    08-06 21:58:00.687 1153-1153/com.example.mycompany.myapp D/MyService: created
    08-06 21:58:00.689 1153-1153/com.example.mycompany.myapp D/MyService: started  ←ここから
    08-06 21:58:05.690 1153-1153/com.example.mycompany.myapp D/MyService: started/done  ←ここまで 5 秒間 main スレッドを占有するため UI がフリーズします。そのため↓の警告が出ています。
    08-06 21:58:05.690 1153-1153/com.example.mycompany.myapp I/Choreographer: Skipped 300 frames!  The application may be doing too much work on its main thread.
    
    08-06 21:58:11.319 1153-1153/com.example.mycompany.myapp D/MainActivity: stop service
    08-06 21:58:11.320 1153-1153/com.example.mycompany.myapp D/MainActivity: stop service/done
    08-06 21:58:11.321 1153-1153/com.example.mycompany.myapp D/MyService: destroyed
    

    非同期処理ありの IntentService

    MainActivity.java および AndroidManifest.xml は、前述「単純な Service (非同期処理なし)」の場合と以下の差分を除いて同じです。Service を直接継承した場合と異なり、start リクエストによって渡されたインテントを main スレッドではなく worker スレッドによってキューから一つずつ取り出して処理するため UI がフリーズすることはありません。

    MainActivity.java

    -    Intent intent = new Intent(MainActivity.this, MyService.class);
    +    Intent intent = new Intent(MainActivity.this, MyIntentService.class);
    

    AndroidManifest.xml

    +    <service android:name=".MyIntentService" />
    

    MyIntentService.java

    package com.example.mycompany.myapp;
    
    import android.app.IntentService;
    import android.content.Intent;
    import android.util.Log;
    
    public class MyIntentService extends IntentService {
    
        private final String TAG = "MyIntentService";
    
        public MyIntentService() {
            super("MyIntentService"); // name for the worker thread
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            Log.d(TAG, "handled");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.d(TAG, "handled/done");
        }
    }
    

    実行例

    08-07 17:41:36.202 2675-2675/com.example.mycompany.myapp D/MainActivity: start service  ←ボタンをタップ (一回目)
    08-07 17:41:36.206 2675-2675/com.example.mycompany.myapp D/MainActivity: start service/done
    08-07 17:41:36.231 2675-9955/com.example.mycompany.myapp D/MyIntentService: handled
    08-07 17:41:37.445 2675-2675/com.example.mycompany.myapp D/MainActivity: start service  ←ボタンをタップ (二回目)
    08-07 17:41:37.451 2675-2675/com.example.mycompany.myapp D/MainActivity: start service/done
    08-07 17:41:41.234 2675-9955/com.example.mycompany.myapp D/MyIntentService: handled/done  ←5秒後、一つ目のリクエスト処理が完了
    08-07 17:41:41.235 2675-9955/com.example.mycompany.myapp D/MyIntentService: handled  ←キューから次のインテントを取り出して処理開始
    08-07 17:41:46.236 2675-9955/com.example.mycompany.myapp D/MyIntentService: handled/done  ←5秒後、処理完了
    

    サービスの処理完了時に結果を返す

    サービスで行う処理のうち、特に結果を返したい場合はブロードキャストや通知を利用します。

    ブロードキャスト

    前述の「非同期処理ありの IntentService」のサンプルコードをもとに、以下のように変更します。また、ブロードキャストでは受信したとしても Activity がバックグラウンドの場合はフォアグラウンドにはなりません。ユーザーへの完了通知を目的とする場合は後述の「通知」を利用します。

    Constants.java

    今回のサンプルで必要になる定数を定義します。

    package com.example.mycompany.myapp;
    
    public final class Constants {
    
        // 様々なブロードキャストがなされる中で、特に今回検証のために実行するブロードキャストの識別子
        public static final String MY_BROADCAST_ACTION = "com.example.mycompany.myapp.MY_BROADCAST";
    
        // インテントに putExtra して格納するデータを取り出すためのキー名
        public static final String EXTRA_MESSAGE = "com.example.mycompany.myapp.MESSAGE";
    }
    

    MyIntentService.java

    onHandleIntent() 内でメッセージをブロードキャストします。

    package com.example.mycompany.myapp;
    
    import android.app.IntentService;
    import android.content.Intent;
    import android.support.v4.content.LocalBroadcastManager;
    import android.util.Log;
    
    public class MyIntentService extends IntentService {
    
        public MyIntentService() {
            super("MyIntentService"); // name for the worker thread
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            Intent localIntent = new Intent(Constants.MY_BROADCAST_ACTION);
            localIntent.putExtra(Constants.EXTRA_MESSAGE, "my message from MyIntentService");
    
            // 本アプリケーションに制限して、レシーバにインテントをブロードキャスト
            LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
        }
    }
    

    MyReceiver.java

    受信時にメッセージをログに出力します。

    package com.example.mycompany.myapp;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.util.Log;
    
    public class MyReceiver extends BroadcastReceiver {
    
        private final String TAG = "MyReceiver";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String msg = intent.getStringExtra(Constants.EXTRA_MESSAGE);
            Log.d(TAG, "received: " + msg);
        }
    }
    

    MainActivity.java

    レシーバとフィルタを Android システムに登録します。

    package com.example.mycompany.myapp;
    
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.support.v4.content.LocalBroadcastManager;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    public class MainActivity extends AppCompatActivity {
    
        private static final String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // ここから追加>>>>>>>>>>
    
            // MyIntentService がブロードキャストする想定のインテントを受信するレシーバー。
            MyReceiver myReceiver = new MyReceiver();
    
            // MyIntentService がブロードキャストする想定のインテントだけをフィルタリングするためのもの。
            IntentFilter myIntentFilter = new IntentFilter(Constants.MY_BROADCAST_ACTION);
    
            // レシーバとフィルタをセットにして Android システムに登録します。
            LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver, myIntentFilter);
    
            // <<<<<<<<<<ここまで追加
    
            Button startButton = (Button)findViewById(R.id.myStartButton);
            startButton.setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Log.d(TAG, "start service");
                            Intent intent = new Intent(MainActivity.this, MyIntentService.class);
                            startService(intent);
                            Log.d(TAG, "start service/done");
                        }
                    }
            );
    
            Button stopButton = (Button)findViewById(R.id.myStopButton);
            stopButton.setOnClickListener(
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            Log.d(TAG, "stop service");
                            Intent intent = new Intent(MainActivity.this, MyIntentService.class);
                            stopService(intent);
                            Log.d(TAG, "stop service/done");
                        }
                    }
            );
        }
    }
    

    実行例

    08-07 23:47:53.668 27760-27760/com.example.mycompany.myapp D/MainActivity: start service
    08-07 23:47:53.674 27760-27760/com.example.mycompany.myapp D/MainActivity: start service/done
    08-07 23:47:53.684 27760-27760/com.example.mycompany.myapp D/MyReceiver: received: my message from MyIntentService
    

    通知

    前述の「非同期処理ありの IntentService」のサンプルコードをもとに MyIntentService.java を以下のように変更します。通知をタップすると MainActivity が起動します。

    package com.example.mycompany.myapp;
    
    import android.app.IntentService;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.content.Intent;
    import android.support.v7.app.NotificationCompat;
    
    public class MyIntentService extends IntentService {
    
        // 「通知」に後からアクセスする際に必要となる ID です。
        private static final int notificationId = 001;
    
        // 通知の管理者
        private NotificationManager notificationManager;
    
        public MyIntentService() {
            super("MyIntentService"); // name for the worker thread
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
    
            // 通知をタップした際に start する Activity を指定してインテントを生成します。
            Intent resultIntent = new Intent(this, MainActivity.class);
    
            // インテントを PendingIntent にラップします。
            PendingIntent resultPendingIntent = PendingIntent.getActivity(
                    this,
                    0,
                    resultIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
    
            // 通知を生成する builder に情報を設定します。
            NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
            notificationBuilder.setSmallIcon(R.drawable.my_icon)
                    .setContentTitle("MyTitle")
                    .setContentText("MyText")
                    .setContentIntent(resultPendingIntent);
    
            // 通知します。
            notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            notificationManager.notify(notificationId, notificationBuilder.build());
        }
    }
    
    Likeボタン(off)0
    詳細設定を開く/閉じる
    アカウント プロフィール画像

    Microservicesアーキテクチャを実践中!

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    >>さらに詳しくステッカーを贈る
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      ログインする

      関連記事

      • Spring Security フォームログインのサンプルコード
        Spring フレームワークによる Web アプリケーション開発で、ログイン処理を実装する際は Spring Security が便利です。ここでは特に Spring Boot で Web アプリケーションを開発する場合を対象とし、フォームによる ID/Password ログインを行うためのサンプルコードをまとめます。 公式ドキュメント [Spring Security チュートリアル](http...
        えびちゃんえびちゃん11/4/2019に更新
        いいねアイコン画像0
      • Java配列の宣言方法 (C/C++との違い)
        Javaの配列 Javaの配列宣言方法はC/C++と似ているようで若干異なる。 初期化しない場合 C/C++の int array[10]; はJavaでは int array[] = new int[10]; となる。同様にC/C++の int array[3][3]; はJavaでは int array[][] = new int[3][3]; となる。 初期化
        てんとうむしてんとうむし4/13/2018に更新
        いいねアイコン画像0
      • PlantUML による UML 図の描き方
        サムネイル画像-c788fffde5
        PlantUML はテキスト形式で表現されたシーケンス図やクラス図といった UML (Unified Modeling Language) 図の情報から画像を生成するためのツールです。簡単な使い方をまとめます。 インストール方法の選択 Atom や Eclipse のプラグインをインストールしてエディタから利用する方法、JAR をダウンロードして Java コマンドで実行する方法、Redmine ...
        kentakenta12/21/2019に更新
        いいねアイコン画像0
      • Akka HTTP サンプルコード (Scala)
        サムネイル画像-a98142497c
        Akka アクターを用いて実装された汎用 HTTP フレームワークです。Spray の後継です。コアモジュールである akka-http-core は 2016/2/17 に experimental が外れました。akka-http などのいくつかのサブモジュールは 2016/3/1 現在 experimental のままですが、基本的な
        雄太雄太9/7/2021に更新
        いいねアイコン画像0
      • Kestrel の使用例
        Kestrel は Message Queue (MQ) の実装のひとつです。一般に MQ はアプリケーション間やプロセス間、スレッド間で非同期に通信するために用いられます。メッセージの送信側は MQ に書き込めば受信側の応答を待たずに次の処理に非同期に進むことができます。Kestrel はわずか 2500 行程の Scala で実装されており JVM で動作します。MQ 自体はメモリ上に存在する...
        したくんしたくん9/12/2017に更新
        いいねアイコン画像0