質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Q&A

解決済

2回答

463閲覧

AsyncTaskを使って位置情報を取りたい

yomogi259

総合スコア7

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

0グッド

0クリップ

投稿2017/11/15 14:46

###前提・実現したいこと
Androidで位置情報をGoogleMapに描画するアプリを作成しています。その時に、LocationManagerから位置情報をAsyncTaskを用いて非同期的に取得したいと思っています。ここのサイトを参考にプログラムを作成したのですが、エラーが発生してしまいました。同期処理であればlocationmanagerを使って位置情報を取得できています。どうかご教授お願い致します。

###発生している問題・エラーメッセージ

E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #1 Process: test.com.myapplication, PID: 6305 java.lang.RuntimeException: An error occurred while executing doInBackground() at android.os.AsyncTask$3.done(AsyncTask.java:318) at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354) at java.util.concurrent.FutureTask.setException(FutureTask.java:223) at java.util.concurrent.FutureTask.run(FutureTask.java:242) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) at java.lang.Thread.run(Thread.java:761) Caused by: java.lang.SecurityException: "gps" location provider requires ACCESS_FINE_LOCATION permission. at android.os.Parcel.readException(Parcel.java:1683) at android.os.Parcel.readException(Parcel.java:1636) at android.location.ILocationManager$Stub$Proxy.getLastLocation(ILocationManager.java:725) at android.location.LocationManager.getLastKnownLocation(LocationManager.java:1205) at test.com.myapplication.Asyn.doInBackground(Asyn.java:61) at test.com.myapplication.Asyn.doInBackground(Asyn.java:16) at android.os.AsyncTask$2.call(AsyncTask.java:304) at java.util.concurrent.FutureTask.run(FutureTask.java:237) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)  at java.lang.Thread.run(Thread.java:761)

###該当のソースコード

java

1package test.com.myapplication; 2 3import android.Manifest; 4import android.app.Activity; 5import android.content.Context; 6import android.content.pm.PackageManager; 7import android.location.Criteria; 8import android.location.Location; 9import android.location.LocationListener; 10import android.location.LocationManager; 11import android.os.AsyncTask; 12import android.os.Bundle; 13import android.support.v4.app.ActivityCompat; 14 15 16public class Asyn extends AsyncTask<Void, Void, Void> implements LocationListener { 17 18 public Location mlocation; 19 double latitude, longitude; 20 private Activity MainActivity; 21 private Context ContextAsync; 22 public LocationManager locManAsyn; 23 String pro; 24 25 public Asyn(Context context) { 26 this.ContextAsync = context.getApplicationContext(); 27 } 28 29 @Override 30 protected Void doInBackground(Void... voids) { 31 locManAsyn = (LocationManager) ContextAsync.getSystemService(ContextAsync.LOCATION_SERVICE); 32 Criteria criteria = new Criteria(); 33 criteria.setAccuracy(Criteria.ACCURACY_COARSE); 34 criteria.setCostAllowed(false); 35 criteria.setPowerRequirement(Criteria.NO_REQUIREMENT); 36 pro = locManAsyn.getBestProvider(criteria, false); 37 if (locManAsyn.isProviderEnabled(LocationManager.GPS_PROVIDER)) { 38 pro = LocationManager.GPS_PROVIDER; 39 } else if (locManAsyn.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { 40 pro = LocationManager.NETWORK_PROVIDER; 41 /*AlertDialog.Builder alert = new AlertDialog.Builder(this); 42 alert.setTitle("GPS is disabled in the settings!"); 43 alert.setMessage("It is recomended that you turn on your device's GPS and restart the app so the app can determine your location more accurately!"); 44 alert.setPositiveButton("OK", null); 45 alert.show();*/ 46 } else if (locManAsyn.isProviderEnabled(LocationManager.PASSIVE_PROVIDER)) { 47 pro = LocationManager.PASSIVE_PROVIDER; 48 //Toast.makeText(ContextAsync, "Switch On Data Connection!!!!", Toast.LENGTH_LONG).show(); 49 } 50 51 if (ActivityCompat.checkSelfPermission(this.ContextAsync, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this.ContextAsync, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 52 // TODO: Consider calling 53 // ActivityCompat#requestPermissions 54 // here to request the missing permissions, and then overriding 55 // public void onRequestPermissionsResult(int requestCode, String[] permissions, 56 // int[] grantResults) 57 // to handle the case where the user grants the permission. See the documentation 58 // for ActivityCompat#requestPermissions for more details. 59 //return TODO; 60 } 61 mlocation = locManAsyn.getLastKnownLocation(pro); 62 if (mlocation == null) { 63 latitude = mlocation.getLatitude(); 64 longitude = mlocation.getLongitude(); 65 } 66 return null; 67 } 68 69 @Override 70 public void onLocationChanged(Location location) { 71 72 if (ActivityCompat.checkSelfPermission(this.ContextAsync, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this.ContextAsync, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 73 // TODO: Consider calling 74 // ActivityCompat#requestPermissions 75 // here to request the missing permissions, and then overriding 76 // public void onRequestPermissionsResult(int requestCode, String[] permissions, 77 // int[] grantResults) 78 // to handle the case where the user grants the permission. See the documentation 79 // for ActivityCompat#requestPermissions for more details. 80 return; 81 } 82 locManAsyn.requestLocationUpdates(pro, 0, 0, this); 83 } 84 85 @Override 86 public void onStatusChanged(String s, int i, Bundle bundle) { 87 88 } 89 90 @Override 91 public void onProviderEnabled(String s) { 92 93 } 94 95 @Override 96 public void onProviderDisabled(String s) { 97 98 } 99 100 @Override 101 protected void onPreExecute() { 102 103 super.onPreExecute(); 104 } 105 106 @Override 107 protected void onPostExecute(Void aVoid) { 108 super.onPostExecute(aVoid); 109 onLocationChanged(mlocation); 110 } 111} 112

###補足情報(言語/FW/ツール等のバージョンなど)
AndroidSrudio3.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

アクセス許可がないということでエラーになっているようですね。

Java

1 if (ActivityCompat.checkSelfPermission(this.ContextAsync, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && 2 ActivityCompat.checkSelfPermission(this.ContextAsync, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

という処理で許可を確認するまでは良いのですが、肝心の「なければ許可を得る」という処理がありませんね。そのためにエラーになっているのではないでしょうか。また、上記のコードは論理積(&&)になっていますが、「どちらか一方の許可がなければ」という論理和(||)にした方が良さそうです。

許可を得るには、

static final int REQUEST_PERMISSION = 1000; static final String[] mPermission = { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }; if (ContextCompat.checkSelfPermission(this.ContextAsync, mPermission[0]) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this.ContextAsync, mPermission[1]) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( this.ContextAsync, mPermission, REQUEST_PERMISSION); }

のような感じにすればいいのではないでしょうか。

ActivityCompat.requestPermissionsを呼ぶと、汎用のAndroidアプリを初めて実行したときによく見るような「この端末の位置情報へのアクセスを許可しますか?」のようなダイアログが出ます。ここで「許可しない」を選ばれてしまうと結局エラーになってしまうので、そういう場合にはアプリを終了する処理を入れるとか、汎用性を持たせるためにはもっと細かい処理は必要になりますが、上記ではとりあえず「そういうことはしない!」と割りきっています。

なお、REQUEST_PERMISSIONはonRequestPermissionsResultというコールバックメソッドに渡される値になるので、これを用いなければ適当な数値で構いません。

checkSelfPermissionは本来はContextCompatの持つクラスメソッドなので、上記のコードではそのように書き換えています。ActivityCompatはContextCompatを継承しているので、元のままでも支障はないのですが、気分的な問題です(まあどちらでもいいでしょう)。

ただこの処理、AsyncTaskの中でも通ったかな?実証していないのでそこはちょっと自信がありません。もし蹴られるようなら、この作業をActivityのコードの、AsyncTaskをnewする以前の場所に移してみましょう。


「時間稼ぎ」の部分の回答のコードとして

Java

1// onProgressUpdateでLocationを受け取るので、真ん中をVoidではなくLocationにする 2public class Asyn extends AsyncTask<Void, Location, Void> implements LocationListener { 3 4 private Context mContextAsync; // 頭が大文字の変数名は好ましくないのでmを付けた 5 6 public Asyn(Context context) { 7 // getApplicationContext()を呼ばずそのまま保持する 8 // この変数を用いていた他の処理はそのままでも問題ないはず 9 mContextAsync = context; 10 } 11 12 @Override 13 protected Void doInBackground(Void... voids) { 14 15 // 中略 16 17 if (ActivityCompat.checkSelfPermission(mContextAsync, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || 18 ActivityCompat.checkSelfPermission(mContextAsync, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { 19 20 boolean loop = true; 21 22 // 繰り返し処理 23 while (loop) { 24 mlocation = locManAsyn.getLastKnownLocation(pro); 25 if (mlocation != null) { 26 latitude = mlocation.getLatitude(); 27 longitude = mlocation.getLongitude(); 28 // onProgressUpdate()にmlocationを渡す 29 publishProgress(mlocation); 30 } 31 32 SystemClock.sleep(1000); // 1000ミリ秒の時間稼ぎ 33 34 if (this.isCancelled()) { 35 loop = false; 36 } 37 } 38 } 39 40 return null; 41 } 42 43 @Override 44 protected void onProgressUpdate(Location... values) { 45 // このメソッドはUIスレッドで動くのでTextViewなどを操作できる 46 TextView textView1 = ((Activity) mContextAsync).findViewById(R.id.textView1); 47 TextView textView2 = ((Activity) mContextAsync).findViewById(R.id.textView2); 48 textView1.setText(String.valueOf(values[0].getLatitude())); 49 textView2.setText(String.valueOf(values[0].getLongitude())); 50 } 51 52 @Override 53 protected void onCancelled() { 54 locManAsyn.removeUpdates(this); 55 } 56 57 // 中略 58 59}

投稿2017/11/15 15:27

編集2017/11/16 16:53
keicha_hrs

総合スコア6768

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yomogi259

2017/11/15 22:39

丁寧な解答ありがとうございます。ただ今外出中で試せていないのですが、permissionの関連の文(permission.ACCESS_FINE_LOCATION)等はmanifestに記載してありますが、今回の場合コードの中から新しく許可をもらう必要があると言うことでしょうか?また、図々しいのですが、getLastKnownLocation()がどのような振る舞いをするのかも教えて頂けないでしょうか?
keicha_hrs

2017/11/16 02:24

AndroidではシステムパーミッションがNormalとDengerousの2種類に分けられています。どちらもAndroidManifest.xmlへの宣言記述は必要ですが、API 23以降ではDengerousに属するパーミッションはそれに加えて実行時にユーザーのコードによるリクエストが必要になりました。この回答はそれによるものです。 それから、位置情報は基本的にはAndroidフレームワークが裏で常時取得していて、内容が変化したときにユーザーに通知する仕組みになっています。そのときに呼ばれるのがonLocationChangedなどのメソッドです。getLastKnownLocationは、フレームワークが最後に取得した情報をユーザー任意のタイミングで取得するためのものです。
yomogi259

2017/11/16 08:11

返信が遅れてしまい申し訳ありません。教えていただいた作業をAsyncTask内で行ったところthis.ContextAsynをActivityにキャストするように赤線が出てしまいました。Activityにキャストして実行するとandroid.app.Application cannot be cast to android.app.Activityとエラーが出てしまいます。MainActivityのAsyncTaskを呼び出す前に上記の作業を移してみましたが、ContextAsyncが赤文字になってしまいました。何度も申し訳ないのですがお力を貸していただけないでしょうか?
keicha_hrs

2017/11/16 10:13 編集

requestPermissions()の第1引数がActivity型だから、確かにエラーになっちゃいますね。それはコンストラクターの受け取り方の改善で何とかなるのですが、それだけだとやっぱりそのままだと問題ありますね。 AsyncTaskの中でrequestPermissionsを実行すること自体は問題ないようですが、それで許可を得られた以降にgetLastKnownLocationが実行される流れにしないと、やはり当初の問題と同じエラーになってしまいそうです。requestPermissionsはブロックされるメソッドではないので、そのまま流れてしまいます。 許可を得る処理はActivity側に実装して、onRequestPermissionsResultで結果を受け取って、許可を得られた時点でAsynを生成するという流れにしないといけないかも。 許可を得るまでの流れは https://akira-watson.com/android/gps-permission.html が参考になるかもしれません。この例では許可を得られた時点でlocationActivityというローカルメソッドを呼んで次のActivityを呼び出していますが、その代わりにAsynを作る処理を実装すれば良さそうに思えます。 やっぱりこういうのは実証実験してから回答しないと不確実になっちゃうなあ・・・。
keicha_hrs

2017/11/16 10:25 編集

上記の参考サイトのActivityをそのまま模倣してこれを開始Activityとし、Intentで現状の開始Activityを呼び出すようにすれば、そのままいけちゃうかもしれませんね。Activityを2枚にしたくなければ工夫が必要ですが。
yomogi259

2017/11/16 13:20

御返事ありがとうございます。Activityが二つになってしまうのは少し検討したいです。また、Android5.0の端末でパーミッションを無視して動かしてみたところ、mlocation = locManAsyn.getLastKnownLocation(pro);の値がLogで確認したところnullとなっている(上記コードでは mlocation == nullとなっていましたが !=に直しました)ようでした。どのように修正したらよいでしょうか?またどうにかしてdoInBackground()内でonLocationChanged()を呼び出せないでしょうか?お願いいたします。
keicha_hrs

2017/11/16 14:27

今さらで申し訳ないのですけど、これ構造がいろいろおかしいですね(汗)この流れだと、getLastKnownLocation()の処理に到達する前に測位が開始されませんから、getLastKnownLocation()から有効な値を得ることはできないように思えます。 対策を考える前に、このAsyncTaskはどのように呼び出しているのでしょうか?今の構造だと、doInBackground()が一度スルッと流れたらそれで終わりです。もしgetLastKnownLocation()で繰り返し位置情報を取得することを考えているのなら、どこかに繰り返し処理が必要ですが、それは呼び出し側ですでに実装済みなのでしょうか? どういう構造をお考えなのかによって、組み立て直しが必要そうです。
yomogi259

2017/11/16 14:34

ありがとうございます。今、getLastKnownLocation()で位置情報を取ることに成功しました。繰り返しは未実装ですが、getlatitude(),getlongitude()で取得した変数をメインのActivityに渡し続けたいと考えています。この場合、doInBackground()から、変数をメインスレッドに渡す処理をWhileで無限ループさせればよいのでしょうか...?
keicha_hrs

2017/11/16 14:56

何かUIを書き換えたいなら、doInBackground()の中でpublishProgress()を呼べばonProgressUpdate()が呼ばれます。onProgressUpdate()はUIスレッド上で動作するので、この中ならTextViewのテキストを書き換えるなどの処理ができます。位置情報を取得し続けるなら、getLastKnownLocation()を呼んで、publishProgress()を呼んで、時間稼ぎをするまでの部分を繰り返し処理にして、アプリ終了時には脱出できるような仕組みを作ればいいかと思います。
yomogi259

2017/11/16 15:27

すみません。時間稼ぎとはどのようなことでしょうか?また、textview等を操作するためにmainActivityのインスタンスを作成し、そこから操作しようと考えたのですがどうもうまくいきません。どうしたらよいのでしょうか?
keicha_hrs

2017/11/16 16:50

時間稼ぎの部分とTextViewを操作するための処理を、大雑把ですが回答欄のコードで示してみました。あまり動作確認していないので自信ないけど・・・。 doInBackground()の中でループを作り、SystemClock.sleep(1000)で1秒のウェイトをおきながらぐるぐる回しています。終了するには、Activity側でAsyncTask#cancel()というメソッドを呼べば、isCancelled()がtrueになるので、このループから脱出できます。 getLastKnownLocation()でLocationを得たら、publishProgress()でそれを渡します。これがonProgressUpdate()の引数に渡ります。onProgressUpdate()の引数をLocation型とするために、AsyncTaskの継承のところで<Void, Location, Void>のように真ん中をLocationにしています。 onProgressUpdate()の中はUIを操作できるスレッドで動くので、TextViewを直接操作できます。コードの例では、2つのTextViewに緯度と経度をそれぞれ出力しています。 後はコメントで説明を入れているので、そのへんを参照していただければ。
yomogi259

2017/11/17 00:02

すみません。自己解決のところに投稿してしまいました。
guest

0

何から何までありがとうございます。今無事に動きました!最後に質問なのですが、呼び出しもと(MainActivityの)変数に直接latitudeとlongitudeを代入するには、インスタンスを作ってMainActivity.latitudeとアクセスすればよいのでしょうか?

投稿2017/11/16 23:47

yomogi259

総合スコア7

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

keicha_hrs

2017/11/17 20:16 編集

ActivityのインスタンスはAndroidフレームワークが作成するもので、ユーザーが勝手に作成してはいけませんし、作成しても意図通りには動きません。稼働しているMainActivityに何か渡したいのであれば、すでに作成されているインスタンスを参照しなければなりません。Asynのコンストラクターで受け取ったContext型の値を用いれば、それを実現することができます。例えば public class MainActivity extends AppCompatActivity { private double latitude, longitude; // 中略 public void recvLocation(Location location) { this.latitude = location.getLatitude(); this.longitude = location.getLongitude(); } // 以下略 のようなメソッドを作っておいて、Asyn側の処理では ((MainActivity) mContextAsync).recvLocation(mLocation); のように呼びだせば渡すことができるでしょう。質問当初のコードでは、AsyncのコンストラクターでgetApplicationContext()を使っていましたが、これを介さず直接受け取っておけば、こうした使い方もできます。 MainActivityで public class MainActivity extends AppCompatActivity { public static double latitude, longitude; のように宣言していれば、Asyncで MainActivity.latitude = mLocation.getLatitude; のように渡すことも可能ではありますが、こういう方法は推奨されません(むしろタブーに近い)。
keicha_hrs

2017/11/17 20:17 編集

コンストラクターでContext型を得るクラスはたくさんあります。例えばViewを継承したクラスを自分で作るときとかも public MyView extends View { Private Context mContext; public MyView(Context context) { this.mContext = context; } みたいにして受け取っておけば、このmContextを使って生成元のActivityと情報交換することができます。 参考にされたStackOverflowの質問でgetApplicationContext()を使った意図はよくわからないのですが、こうしてしまうと上記のような使い方ができなくなってしまいます。Contextというのはアプリケーションやアクティビティの状態管理を司るものなのですが、「アプリケーション全体を指す」ものと「アクティビティごとを指す」ものの2種類があります。利便性を考えれば、保持するのは後者にするべきです。 最初に回答したrequestPermissionsの処理で、第1引数がエラーになってしまうという件も、直接受け取っておけば if (ContextCompat.checkSelfPermission(this.ContextAsync, mPermission[0]) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this.ContextAsync, mPermission[1]) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions( (Activity) this.ContextAsync, mPermission, REQUEST_PERMISSION); } のようにキャストで解決できたんですよね。getApplicationContext()の結果を保持してしまうと、これができなくなってしまいます。こうしたところでも不便になってしまうんですよね。
yomogi259

2017/11/17 14:59

返信遅れてしまってすみません。無事に変数を渡すことができました。このような初心者に付き合っていただきありがとうございます。最後に、contextやpermission等の所謂、書店で売っている開発本等にはあまりのっていないような知識は何処で学習したらよいでしょうか?今回はcontextやActivityに多く苦しめられたので学習したいと思っています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問