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

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

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

Q&A

解決済

1回答

1026閲覧

Android開発でネットワーク通信がうまくいかない。

mokimokio

総合スコア53

0グッド

0クリップ

投稿2022/04/27 02:31

編集2022/04/27 05:31

助けて欲しいです。何日もkotlinでネットワーク通信をしたいと取り組んでいるのですがうまくいきません。
参考にしているサイトは
https://qiita.com/naoi/items/5036adc8d33638911deb#buildgradleapp%E3%81%B8%E3%81%AE%E8%BF%BD%E8%A8%98

です。

やりたいことは
Android画面でボタンを押したらその時の時刻と、IDを送信する。
⬇️
ネットワーク上でphpを組んで、そこでIDと時刻をデータベースに登録する。
⬇️
そしてデータベースで登録ができたら、phpで「〇〇様登録完了」と出す。
⬇️
phpの「OO様登録完了」文字をAndroidのtextviewに表記させる。

といった流れです。いろいろ試しに試してコードがぐちゃぐちゃになったので一度スッキリさせてここにまとめておきます。

もはや組み方が分かりません。可能であれば上記の流れを達成するためのコードを下記コードに正解のコードを書き足していただけませんか?それ見て勉強していきたいです><

自分のわからないところをピックアップしました。(これで足りているか不安ですが、、、)
①そもそもサイトには【GETメソッドでHTTP通信する実装方法】のところにMyService.ktと書かれているがPOSTの場合でもこれが必要なのか。
②(上記が必要な場合)MyService.ktの定義の中身を今回の実装したいものではどのように組めばいいのかわからない。
③val postの中身の組み方がわからない。(わからないのでval post内はサイトからコピペした状態のままにしている)、そしてval postを非同期の中で宣言してみたが、この場所でいいのかわからない
④⭐️部分は非同期処理内でネットワーク通信を終えた後にTV.textに呼び出したいのだが、なんて書けばいいのかわからない。

MainActivity.kt

1import android.os.Build 2import androidx.appcompat.app.AppCompatActivity 3import android.os.Bundle 4import android.util.Log 5import android.widget.Button 6import android.widget.TextView 7import android.widget.Toast 8import kotlinx.coroutines.Dispatchers 9import kotlinx.coroutines.Job 10import kotlinx.coroutines.delay 11import kotlinx.coroutines.withContext 12import retrofit2.Retrofit 13import java.time.LocalDateTime 14 15class MainActivity : AppCompatActivity() { 16 17 // Retrofit本体 18 private val retrofit = Retrofit.Builder().apply { 19 baseUrl("http://10.0.2.2:3000/") 20 }.build() 21 22 val get = service.getRawResponseForPosts() 23 24 25 override fun onCreate(savedInstanceState: Bundle?) { 26 super.onCreate(savedInstanceState) 27 setContentView(R.layout.activity_main) 28 val TV : TextView = findViewById(R.id.TextView1) 29 30 31 32 // ボタンを押した時の処理 33 val btn : Button = findViewById(R.id.button1) 34 35 // ボタンを連打したときに何度も処理してしまうのを防ぐため。jobを設定する 36 var job: Job? = null 37 38 btn.setOnClickListener { 39 40 var nowTime = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 41 LocalDateTime.now() 42 } else { 43 TODO("VERSION.SDK_INT < O") 44 } 45 TV.text = nowTime.toString() 46 47 //下記にすることで連打したときに何度も処理されることを防げる。coroutinesが複数起動しなくなるということ。 48 if (job?.isActive == true) return@setOnClickListener 49 job = lifecycleScope.launch { 50 51      //result内のスコープがIOスレッドで実行される 52 withContext(Dispatchers.IO) { 53 val post = service.getRawResponseForPosts().also { 54 it.enqueue(object : Callback<List<Post>> { 55 override fun onFailure(call: Call<List<Post>>, t: Throwable) { 56 Toast.makeText(this@MainActivity, "通信エラー", Toast.LENGTH_SHORT).show() 57 Log.e("TEST", "通信エラー", t) 58 } 59 60 override fun onResponse(call: Call<List<Post>>, response: Response<List<Post>>) { 61 myViewModel.result.value = response.body().toString() 62 } 63 }) 64 } 65 } 66 67       //Main内のスレッドがIOスレッド内でメインスレッドも実行可能となる。 68 withContext(Dispatchers.Main) { 69 TV.text = ⭐️ 70 } 71 } 72 } 73 } 74 }

AndroidManifest.xml

1<?xml version="1.0" encoding="utf-8"?> 2<manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.myapplication"> 4 <uses-permission android:name="android.permission.INTERNET" /> 5 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 6 7 <application 8 android:allowBackup="true" 9 android:icon="@mipmap/ic_launcher" 10 android:label="@string/app_name" 11 android:roundIcon="@mipmap/ic_launcher_round" 12 android:supportsRtl="true" 13 android:theme="@style/Theme.MyApplication"> 14 <activity 15 android:name=".MainActivity" 16 android:exported="true"> 17 <intent-filter> 18 <action android:name="android.intent.action.MAIN" /> 19 20 <category android:name="android.intent.category.LAUNCHER" /> 21 </intent-filter> 22 </activity> 23 </application> 24 25</manifest>

activity_main.xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".MainActivity"> 8 9 <TextView 10 android:id="@+id/TextView1" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:text="Hello World!" 14 app:layout_constraintBottom_toBottomOf="parent" 15 app:layout_constraintLeft_toLeftOf="parent" 16 app:layout_constraintRight_toRightOf="parent" 17 app:layout_constraintTop_toTopOf="parent" /> 18 19 <Button 20 android:id="@+id/button1" 21 android:layout_width="wrap_content" 22 android:layout_height="wrap_content" 23 android:text="Button" 24 app:layout_constraintBottom_toBottomOf="parent" 25 app:layout_constraintEnd_toEndOf="parent" 26 app:layout_constraintStart_toStartOf="parent" 27 app:layout_constraintTop_toBottomOf="@+id/TextView1" /> 28 29</androidx.constraintlayout.widget.ConstraintLayout>

build.gradle(:app)

1 2dependencies { 3 4 implementation 'androidx.core:core-ktx:1.7.0' 5 implementation 'androidx.appcompat:appcompat:1.4.1' 6 implementation 'com.google.android.material:material:1.5.0' 7 implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 8 testImplementation 'junit:junit:4.13.2' 9 androidTestImplementation 'androidx.test.ext:junit:1.1.3' 10 androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 11 12 def retrofit_version = "2.9.0" 13 implementation "com.squareup.retrofit2:retrofit:$retrofit_version" 14 implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" 15 implementation "com.squareup.okhttp3:logging-interceptor:4.9.0" 16 17 18 implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0" 19 testImplementation 'junit:junit:4.13.2' 20 androidTestImplementation 'androidx.test.ext:junit:1.1.3' 21 androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 22}

これより下3つのファイルは冒頭のサンプルURLを参考にして作成したファイルです。どう使うのか、そして今回僕がしたいことに必要なのか僕には理解が届きませんでしたがとりあえず作成しました。

MyService.kt

1import android.telecom.Call 2import okhttp3.RequestBody 3import okhttp3.ResponseBody 4import retrofit2.http.* 5import retrofit2.Call as Call1 6 7//URLのサブディレクトリやパスの設定 8interface MyService{ 9 10 @GET("posts") 11 fun getRawResponseForPosts(): Call<ResponseBody> 12 13 @POST("posts") 14 fun postRawRequestForPosts(@Body body:RequestBody):Call<ResponseBody> 15 16 @PUT("posts/{id}") 17 fun putRawRequestForPosts(@Path("id") id:String, @Body body:RequestBody):Call<ResponseBody> 18 19 @DELETE("posts/{id}") 20 fun deletePathParam(@Path("id") id:String ):Call<ResponseBody> 21 22}

自分が送信したい情報はIDと時間なのでここは書き換えました。⬇️

Post.kt

1data class Post( 2 3 val id: Int = 1111, 4 val time: String 5 6)

Util.kt

1object Util { 2 3 fun createJson(id:Int,time:String):String 4 ="{" + 5 " \"id\": \"${id}\"," + 6 " \"time\": \"${time}\"," + 7 "}" 8}

最後にAPI接続先のphpファイルです。これはここではざっくりとこんな感じという風に書かせていただきます。try catch やデータベース情報などこの質問に不必要なものは外しています。

php

1<?php 2 3 4$id=$_POST['id']; 5$time=$_POST['time']; 6 7 8データベース接続の処理 9データベース登録の処理 10 11登録成功なら 12echo'〇〇様登録完了'; 13

以上です。
長々とした質問ですみません。数日戦っても組み方に見当がつかないのでどなたかご教授よろしくお願いします。

なお、自分の質問にミスがあればすぐに訂正致しますので気軽にコメント下さい。

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

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

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

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

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

jimbe

2022/04/27 04:20 編集

何が分からないのでしょうか。「いろいろ試しに試してコードがぐちゃぐちゃになったので一度スッキリ」させ過ぎて何も無くなっています。「うまくいきません」では何も分かりません。 どこかでエラーになるのでしょうか。それともプログラムが終了しないとかでしょうか。 ぐちゃぐちゃになったら一旦今のコードは何処かにしまって、2~3日このプログラムのことは一切考えないで休憩してから一から考え直す・作り直すのもテかと思います。
mokimokio

2022/04/27 04:58

Hoshi-takanori様 Logcatにエラーはたくさん出ていますが、そもそも組み方がわかっていないのでそれ以前の問題だと思うんです。。。(汗 AndroidManifestの内容も本文に追加しました。あっているか確認していただけますか?😭
mokimokio

2022/04/27 05:01

jimbe様 本来のままのコードを追記しました。 そして、自分のわからないところをピックアップしました。(これで足りているか不安ですが、、、) ①そもそもサイトには【GETメソッドでHTTP通信する実装方法】のところにMyService.ktと書かれているがPOSTの場合でもこれが必要なのか。 ②(上記が必要な場合)MyService.ktの定義の中身を今回の実装したいものではどのように組めばいいのかわからない。 ③val postの中身の組み方がわからない。(わからないのでval post内はサイトからコピペした状態のままにしている)、そしてval postを非同期の中で宣言してみたが、この場所でいいのかわからない ④⭐️部分は非同期でネットワーク通信を終えた後にTV.textに呼び出したいのだが、なんて書けばいいのかわからない。 いっぱいあってすみません。
hoshi-takanori

2022/04/27 05:06

AndroidManifest は大丈夫そうですね。 Logcat は確かにいっぱい出てきて最初は意味不明だと思いますが、必ず必要になるので、早めに見方を覚えることをお勧めします。 そして、確認ですが、サーバーはちゃんと動いてますか? Android から繋ぐ前に、ブラウザから繋がることを確認すると良いでしょう。
mokimokio

2022/04/27 05:53

hoshi-takanori様 分かりましたLog catもしっかり見て覚えていきます。 サーバーは動いています!iOSアプリでは接続できているのでそれは間違い無いです!
jimbe

2022/04/27 06:51 編集

http のリクエストは get でしょうか post でしょうか。 サーバからの戻りの Json はどうなっているのでしょうか。
mokimokio

2022/04/27 06:54

jimbe 戻りと言いますか、 phpから帰ってくるのは文字列で '〇〇様登録完了'のみです! jsonで返した方がいい場合は戻しのやり方変更します!
dodox86

2022/04/27 06:59

最初から完成させようとするから分からなくなります。一つ一つやれば良いのに。
mokimokio

2022/04/27 07:06

jimbe様すみません!一つ回答できてませんでした!やりたいのはpostです!
mokimokio

2022/04/27 07:06

dodox86様 ありがとうございます!もうちょっと細分化してやってみます!
guest

回答1

0

ベストアンサー

以下で動作しました。

リクエスト(POST): { "id" : "1111", "time" : "12:34" }
レスポンス: { "message" : "登録完了" }

といった感じにしています。

MainActivity.kt

kotlin

1import androidx.appcompat.app.AppCompatActivity 2import android.os.Bundle 3import android.util.Log 4import android.widget.Button 5import android.widget.TextView 6import android.widget.Toast 7import retrofit2.Call 8import retrofit2.Callback 9import retrofit2.Response 10import retrofit2.Retrofit 11import retrofit2.converter.gson.GsonConverterFactory 12 13class MainActivity : AppCompatActivity() { 14 15 private val retrofit = Retrofit.Builder().apply { 16 baseUrl("http://10.0.2.2:8080/") 17 addConverterFactory(GsonConverterFactory.create()) 18 }.build() 19 private val service = retrofit.create(MyService::class.java) 20 21 override fun onCreate(savedInstanceState: Bundle?) { 22 super.onCreate(savedInstanceState) 23 setContentView(R.layout.activity_main) 24 25 val textView1 : TextView = findViewById(R.id.TextView1) 26 val button1 : Button = findViewById(R.id.button1) 27 28 button1.setOnClickListener { 29 val data = Post(id = "1111", time = "12:34") 30 service.post(data).enqueue( 31 object : Callback<Result> { 32 override fun onResponse(call: Call<Result>, response: Response<Result>) { 33 Toast.makeText(this@MainActivity, "通信完了", Toast.LENGTH_SHORT).show() 34 runOnUiThread { 35 textView1.setText(response.body()?.message) 36 } 37 } 38 override fun onFailure(call: Call<Result>, t: Throwable) { 39 Toast.makeText(this@MainActivity, "通信エラー", Toast.LENGTH_SHORT).show() 40 Log.e("TEST", "通信エラー", t) 41 } 42 }) 43 } 44 } 45}

MyService.kt

kotlin

1import retrofit2.Call 2import retrofit2.http.* 3 4interface MyService { 5 @POST("posts") 6 fun post(@Body post: Post): Call<Result> 7}

Post.kt

kotlin

1data class Post(val id: String, val time: String)

Result.kt

kotlin

1data class Result(val message: String)

投稿2022/04/27 08:37

編集2022/04/27 08:43
jimbe

総合スコア12648

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

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

mokimokio

2022/04/28 04:07

試したらできました!作成していただいたコードを一つ一つ見て勉強を精進していきます! 本当に助かりました!ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問