LoginSignup
4
2

More than 3 years have passed since last update.

51歳からのプログラミング 備忘 FusedLocationProviderClientをForegroundServiceで常駐させる

Last updated at Posted at 2019-10-24

FusedLocationProviderClientをForegroundSeriviceで常駐させる

androidDeveloperとgoogleApi様を参照してます。
あと、nyan様のサイトも、いっつも参照しています。ありがとうございます。


以下のサンプルコードは、androidのビルドバージョンはapi26以上です。

Manifest gradleの確認

Manifest
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

builed.gradle
dependencies {
    ...
    implementation 'com.google.android.gms:play-services-location:17.0.0'

この後、必ずgradleを同期させましょう!

NotificationしないクラッシュCode

Notificationするとコードが長くなるので、まずはシンプルに動作確認するためのコードです。
起動すると位置情報を取得し始め、ボタンプッシュでサービスと位置情報取得を破棄。
5秒経つとクラッシュ

MainActivity
public class MainActivity extends AppCompatActivity {

    static MainActivity activity;
    static TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        activity = MainActivity.this;
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);

        final Intent intent = new Intent(this,MyService.class);
        startForegroundService(intent);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopService(intent);
            }
        });
    }
}

MyService
public class MyService extends Service {

    MainActivity activity;
    TextView textView;
    FusedLocationProviderClient fusedLocationClient;
    LocationCallback myCallback;

    @Override
    public void onCreate() {
        super.onCreate();
        activity = MainActivity.activity;
        textView = MainActivity.textView;
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(activity);
    }

    @Override
    public int onStartCommand(Intent intent,int flags,int startId){
        // 本当ならここでNotificationを設定してstartForeground
        // でも見辛くなるので今回は省略
        myCallback = new myCallback(); // LocationCallback()インスタンス
        myCheckPermission();           // ActivityCompat.checkSelfPermission()
        myGetLocation();               // FusedLocationProviderClient.requestLocationUpdates(); 
        return START_STICKY;
    }

    // Manifestで設定したpermissonの ACCESS_FINE_LOCATIONってば
    // ユーザーデンジャラス なので、permissionの許諾が必要!
    public void myCheckPermission(){
        if(ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.ACCESS_FINE_LOCATION},100);
            // requestPermissions()で確認した内容は
            // 本当ならonRequestPermissionsResult()で処理するのですが省略です
        }else{
            return;
        }
    }

    public void myGetLocation(){
        // fusedLocationProviderClientの動作を設定
        LocationRequest locationRequest = LocationRequest.create();
            locationRequest.setInterval(1000*3);
            locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        // では!位置情報を取得するよ!取得した情報はCallback()関数で扱います!
        fusedLocationClient.requestLocationUpdates(locationRequest,myCallback,null);
    }

    // MainActivityで、ストップボタンを押すと、Serviceが破棄されるようにしたので
    // その時に、onDestroyが呼ばれます
    // onDestroy()でfusedLocationProviderClientを破棄するよ!
    @Override
    public void onDestroy() {
        super.onDestroy();
        fusedLocationClient.removeLocationUpdates(myCallback);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    // LocationCallback()です。
    // 取得した位置情報を扱うのはこのクラス!
    class myCallback extends LocationCallback{
          @Override
          public void onLocationResult(LocationResult locationResult){
              super.onLocationResult(locationResult);
              // locationResultに位置情報があるので、それを使おう!
              double latitude = locationResult.getLastLocation().getLatitude();
              String strLatitude = String.valueOf(latitude);
              textView.setText(strLatitude);
        }
    }
}

常駐させ、緯度経度高度を取得するCode

Startボタンで位置情報取得と表示を常駐させます。
Stopボタンでサービスと位置情報取得を破棄します。
レッツゴー!

MainActivity
public class MainActivity extends AppCompatActivity {

    // Serviceから参照するUI部品をstaticに
    static MainActivity activity;
    static TextView textAlutitude;
    static TextView textLatitude;
    static TextView textLongitude;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // オブジェクトを取得!
        activity = MainActivity.this;
        textAlutitude = findViewById(R.id.textAltitude);
        textLatitude = findViewById(R.id.textLatitude);
        textLongitude = findViewById(R.id.textLongitude);
        Button btnStart= findViewById(R.id.btnStart);
        Button btnStop = findViewById(R.id.btnStop);


        // StartボタンでForagroundServiceスタート
        // StopボタンでForegroundServiceストップ
        //    ForegroundServiceストップでMyService.onDestroy()でFusedLocationProviderClientも破棄
        final Intent intent = new Intent(activity,MyService.class);
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startForegroundService(intent);
            }
        });

        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopService(intent);
            }
        });

    }
}

MyService
public class MyService extends Service {

    // オブジェクトをグローバルに
    MainActivity activity;
    TextView textAltitude;
    TextView textLatitude;
    TextView textLongitude;
    FusedLocationProviderClient fusedLocationClient;
    LocationRequest locationRequest;
    LocationCallback locationCallback;

    @Override
    public void onCreate() {
        super.onCreate();

        // オブジェクトを取得!
        activity = MainActivity.activity;
        textAltitude = MainActivity.textAlutitude;
        textLatitude = MainActivity.textLatitude;
        textLongitude = MainActivity.textLongitude;
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

        // LocationRequestを設定しときます
        // 5秒ごとに詳細な位置情報を取得するように設定
        locationRequest = LocationRequest.create();
            locationRequest.setInterval(1000*5);
            locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    public int onStartCommand(Intent intent, int flags, int startId){
        // 常駐させたいので通知を出します

        // 通知コンテンツ、重要度、チャンネルを設定
        Notification notification;
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this,"id");
        Intent pIntent = new Intent(this,MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,pIntent,0);
        notification = builder.setSmallIcon(R.drawable.notification_icon)
                              .setContentTitle("NotificationTitle")
                              .setContentText("NotificationText")
                              .setContentIntent(pendingIntent)
                              .build();
        int importance = NotificationManager.IMPORTANCE_LOW;
        NotificationChannel channel = new NotificationChannel("id","name",importance);
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(channel);

        // 5秒以内に startForeground !! 急げ!
        startForeground(1,notification);


        // LocationCallback()は、requestLocation,Update,removeで使うので、ここで作成しとく
        locationCallback = new LocationCallback(){
            @Override
            public void onLocationResult(LocationResult locationResult){
                super.onLocationResult(locationResult);
                String alti = String.valueOf(locationResult.getLastLocation().getAltitude());
                String lati = String.valueOf(locationResult.getLastLocation().getLatitude());
                String longi = String.valueOf(locationResult.getLastLocation().getLongitude());
                textAltitude.setText(alti);
                textLatitude.setText(lati);
                textLongitude.setText(longi);
            }
        };

        checkLocationPermission();
        return START_STICKY;
    }

    // 位置情報を取得
    private void locationUpdate(){
        fusedLocationClient.requestLocationUpdates(locationRequest,locationCallback,null);
    }

    // permission ACCESS_FINE_LOCATION はユーザーデンジャラスな設定なので許可を確認します
    public void checkLocationPermission(){
        if(ActivityCompat.checkSelfPermission(activity,Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
            // ACCESS_FINE_LOCATIONの許可がなければダイアログでユーザーに確認
            ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.ACCESS_FINE_LOCATION},100);
        }else{
            locationUpdate();
        }
    }

    // checkLocationPermission()のrequestPermissionsでユーザに確認した内容を処理します
    public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults){
        if(requestCode == 100){
            if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                // 許可が得られたら
                locationUpdate();
            }else{
                // 許可が得られなかったら
                textAltitude.setText("No permission");
            }
        }
    }

    // Serviceが破棄されるときに、FusedLocationProviderClientも破棄
    @Override
    public void onDestroy() {
        super.onDestroy();
        fusedLocationClient.removeLocationUpdates(locationCallback);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

今回も、かなり時間を掛けました。位置情報を常駐的に取得するのって、僕にはハードル高すぎました。このコードを作るまでに約2週間。行ったり来たりを繰り返して、やっとでした~。

今回も亀の歩みですね。

亀の歩みでも、諦めないでもがき続けると、前進できるんだなって感じたよ。
無理かな無理かなと思っても、前進できるんだね。
まだまだ全然実力不足なんだけれど、面白い。すごく楽しい!

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2