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週間。行ったり来たりを繰り返して、やっとでした~。
今回も亀の歩みですね。
亀の歩みでも、諦めないでもがき続けると、前進できるんだなって感じたよ。
無理かな無理かなと思っても、前進できるんだね。
まだまだ全然実力不足なんだけれど、面白い。すごく楽しい!