助けて欲しいです。何日も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
import android.os.Build import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.widget.Button import android.widget.TextView import android.widget.Toast import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import retrofit2.Retrofit import java.time.LocalDateTime class MainActivity : AppCompatActivity() { // Retrofit本体 private val retrofit = Retrofit.Builder().apply { baseUrl("http://10.0.2.2:3000/") }.build() val get = service.getRawResponseForPosts() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val TV : TextView = findViewById(R.id.TextView1) // ボタンを押した時の処理 val btn : Button = findViewById(R.id.button1) // ボタンを連打したときに何度も処理してしまうのを防ぐため。jobを設定する var job: Job? = null btn.setOnClickListener { var nowTime = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { LocalDateTime.now() } else { TODO("VERSION.SDK_INT < O") } TV.text = nowTime.toString() //下記にすることで連打したときに何度も処理されることを防げる。coroutinesが複数起動しなくなるということ。 if (job?.isActive == true) return@setOnClickListener job = lifecycleScope.launch { //result内のスコープがIOスレッドで実行される withContext(Dispatchers.IO) { val post = service.getRawResponseForPosts().also { it.enqueue(object : Callback<List<Post>> { override fun onFailure(call: Call<List<Post>>, t: Throwable) { Toast.makeText(this@MainActivity, "通信エラー", Toast.LENGTH_SHORT).show() Log.e("TEST", "通信エラー", t) } override fun onResponse(call: Call<List<Post>>, response: Response<List<Post>>) { myViewModel.result.value = response.body().toString() } }) } } //Main内のスレッドがIOスレッド内でメインスレッドも実行可能となる。 withContext(Dispatchers.Main) { TV.text = ⭐️ } } } } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MyApplication"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/TextView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/TextView1" /> </androidx.constraintlayout.widget.ConstraintLayout>
build.gradle(:app)
dependencies { implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' def retrofit_version = "2.9.0" implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:converter-moshi:$retrofit_version" implementation "com.squareup.okhttp3:logging-interceptor:4.9.0" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0" testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' }
これより下3つのファイルは冒頭のサンプルURLを参考にして作成したファイルです。どう使うのか、そして今回僕がしたいことに必要なのか僕には理解が届きませんでしたがとりあえず作成しました。
MyService.kt
import android.telecom.Call import okhttp3.RequestBody import okhttp3.ResponseBody import retrofit2.http.* import retrofit2.Call as Call1 //URLのサブディレクトリやパスの設定 interface MyService{ @GET("posts") fun getRawResponseForPosts(): Call<ResponseBody> @POST("posts") fun postRawRequestForPosts(@Body body:RequestBody):Call<ResponseBody> @PUT("posts/{id}") fun putRawRequestForPosts(@Path("id") id:String, @Body body:RequestBody):Call<ResponseBody> @DELETE("posts/{id}") fun deletePathParam(@Path("id") id:String ):Call<ResponseBody> }
自分が送信したい情報はIDと時間なのでここは書き換えました。⬇️
Post.kt
data class Post( val id: Int = 1111, val time: String )
Util.kt
object Util { fun createJson(id:Int,time:String):String ="{" + " \"id\": \"${id}\"," + " \"time\": \"${time}\"," + "}" }
最後にAPI接続先のphpファイルです。これはここではざっくりとこんな感じという風に書かせていただきます。try catch やデータベース情報などこの質問に不必要なものは外しています。
php
<?php $id=$_POST['id']; $time=$_POST['time']; データベース接続の処理 データベース登録の処理 登録成功なら echo'〇〇様登録完了';
以上です。
長々とした質問ですみません。数日戦っても組み方に見当がつかないのでどなたかご教授よろしくお願いします。
なお、自分の質問にミスがあればすぐに訂正致しますので気軽にコメント下さい。
まだ回答がついていません
会員登録して回答してみよう