مستندات جامع اندروید
نسخه 1.0.3
با استفاده از REST API میتوانیم از وبسرویسهای ارائه شده توسط زیرساخت نقشه نشان استفاده کنیم. لیست این وبسرویسها و ساختار درخواست و پاسخی که از سرور دریافت میشود در لینک زیر آمده است:
فهرست مطالب این صفحه
برای استفاده از REST API در اندروید کتابخانههای مختلفی وجود دارد، که در این بخش، به معرفی و نحوه استفاده از آن برای دریافت آدرس یک موقعیت جغرافیایی توسط وبسرویس «تبدیل موقعیت به آدرس» خواهیم پرداخت.
توضیح نحوه استفاده از این وبسرویس را میتوانید از اینجا مطالعه کنید.
اگر بخواهیم آدرس موقعیت جغرافیایی زیر را به دست آوریم:
درخواست ما باید از نوع GET و با آدرس زیر باشد:
پاسخ دریافت شده از سمت سرور:
{
"status": "OK",
"neighbourhood": "طرشت",
"municipality_zone": "2",
"state": "استان تهران",
"city": "تهران",
"in_traffic_zone": false,
"in_odd_even_zone": false,
"route_name": "عزیزی",
"route_type": "secondary",
"place": null,
"district": "بخش مرکزی شهرستان تهران",
"formatted_address": "تهران، بزرگراه جناح، عزیزی، بین آزادی و سلطان محمدی",
"village": null
}
در این پروژه میخواهیم آدرس نقطهای که در وسط نقشه است و توسط ImageView مشخص میشود را از وبسرویس «تبدیل نقطه به آدرس» به دست آوریم و این آدرس را در بالای صفحه نمایش دهیم.
activity_api_retrofit.xml
:
در این فایل که رابط کاربری کلی این بخش از پروژه است، علاوه بر المان نقشه نشان، شامل این المان ها نیز می شود: یک ImageView
که مشخص کننده وسط نقشه است. یک TextView
جهت نمایش آدرس دریافت شده از سرور و یک ProgressBar
که در زمان انتظار برای دریافت آدرس از سرور نمایش داده می شود.
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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=".activity.ApiRetrofitActivity">
<org.neshan.mapsdk.MapView
android:id="@+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_marker" />
<androidx.cardview.widget.CardView
android:id="@+id/cvAddressHolder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="top"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="24dp"
android:clickable="true"
android:focusable="true"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="8dp">
<ProgressBar
android:id="@+id/progressBar"
style="@style/Widget.AppCompat.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:scaleX="0.5"
android:scaleY="0.5" />
<TextView
android:id="@+id/addressTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:ellipsize="end"
android:gravity="right"
android:includeFontPadding="false"
android:textSize="16sp"
android:textStyle="bold" />
</androidx.cardview.widget.CardView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
build.gradle (Module: app)
:
برای استفاده از کتابخانه Retrofit دو خط زیر را به لیست وابستگیهای پروژه در این فایل اضافه میکنیم.
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
AndroidManifest.xml
:
برای استفاده از وبسرویسها، دسترسی اینترنت را به لیست دسترسیهای اپلیکیشن خود در این فایل اضافه کنید:
<uses-permission android:name="android.permission.INTERNET" />
NeshanAddress.kt
:
در این مرحله، مدل داده پاسخ سرور را میسازیم. دو نوع پاسخ ممکن است از سمت سرور دریافت شود، یک نمونه آن – زمانی که وبسرویس به درستی میتواند آدرس موقعیت جغرافیایی مورد نظر را به دست آورد. حالت دیگری نیز وجود دارد که وبسرویس نمیتواند آدرس مناسبی برای محل مورد نظر ما پیدا کند. فرض کنید درخواست ما درخواست زیر باشد:
در این حالت پاسخ سرور به صورت زیر است:
{
"status": "ERROR",
"code": 470,
"message": "Invalid argument!"
}
برای این که هر دو حالت پاسخهای سرور را بتوانیم مدیریت کنیم، مدلی میسازیم که تمامی متغیرهای مربوط به هر دو پاسخ را داشته باشد.
در این کلاس، ابتدا تمامی متغیرهای مربوط به پاسخ در هر دو حالت تعریف شده است و سپس برای هر حالت یک متد سازنده تعریف شده است.
package org.neshan.kotlinsample.model.address
import com.google.gson.annotations.SerializedName
import java.io.Serializable
class NeshanAddress : Serializable {
// when address is found
@SerializedName("neighbourhood")
var neighbourhood: String? = null
@SerializedName("formatted_address")
var address: String? = null
@SerializedName("municipality_zone")
var municipality_zone: String? = null
@SerializedName("in_traffic_zone")
var in_traffic_zone: Boolean? = null
@SerializedName("in_odd_even_zone")
var in_odd_even_zone: Boolean? = null
@SerializedName("city")
var city: String? = null
@SerializedName("state")
var state: String? = null
// when address is not found
@SerializedName("status")
var status: String? = null
@SerializedName("code")
var code: Int? = null
@SerializedName("message")
var message: String? = null
constructor(
neighbourhood: String?,
address: String?,
municipality_zone: String?,
in_traffic_zone: Boolean?,
in_odd_even_zone: Boolean?,
city: String?,
state: String?
) {
this.neighbourhood = neighbourhood
this.address = address
this.municipality_zone = municipality_zone
this.in_traffic_zone = in_traffic_zone
this.in_odd_even_zone = in_odd_even_zone
this.city = city
this.state = state
}
constructor(status: String?, code: Int?, message: String?) {
this.status = status
this.code = code
this.message = message
}
}
RetrofitClientInstance.kt
:
برای انجام درخواستهای تحت شبکه به یک REST API با استفاده از Retrofit، با استفاده از کلاس Retrofit.Builder
یک شی از میسازیم و BASE_URL
– که آدرس پایه وبسرویس است را به آن میدهیم.
package org.neshan.kotlinsample.network
import retrofit2.Retrofit
import okhttp3.OkHttpClient
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClientInstance {
private const val BASE_URL = "https://api.neshan.org/"
private var retrofit: Retrofit? = null
val retrofitInstance: Retrofit?
get() {
val client = OkHttpClient.Builder()
.build()
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
}
return retrofit
}
}
ReverseService.kt
:
نقاط پایانی (درخواستهای مختلف به وبسرویسهای مختلف نشان) را در این interface تعریف میکنیم.
در تعریف هر نقطه پایانی (Endpoint)، در خط اول کلید API خود را در سرآمد درخواست قرار دادهایم. در خط دوم مشخص کردهایم که درخواست از نوع GET است. در خط سوم، نام متد، مقدار بازگشتی آن – که از نوع مدل دادهای است که در مراحل قبل ساختیم – و ورودی متد – که آدرس کامل درخواست ما است – را میدهیم.
package org.neshan.kotlinsample.network
import retrofit2.http.GET
import org.neshan.kotlinsample.model.address.NeshanAddress
import retrofit2.Call
import retrofit2.http.Headers
import retrofit2.http.Query
interface ReverseService {
// TODO: replace "YOUR_API_KEY" with your api key
@Headers("Api-Key: service.kREahwU7lND32ygT9ZgPFXbwjzzKukdObRZsnUAJ")
@GET("/v2/reverse")
fun getReverse(@Query("lat") lat: Double?, @Query("lng") lng: Double?): Call<NeshanAddress?>?
}
APIRetrofitActivity.kt
:
آدرس دریافت شده از یک موقعیت توسط وب سرویس، در متغیر neshanAddress
ذخیره میشود.
در ادامه نیز المانهای رابط کاربری تعریف شده است.
private lateinit var addressTitle: TextView
هنگامی که نقشه جابجا شود، ابتدا progressBar
نمایش داده شده و سپس متد getReverseApi
با استفاده از موقعیت فعلی نقشه صدا زده می شود که جلوتر این متد توضیح داده خواهد شد.
// Initializing map
private fun initMap() {
// Setting map focal position to a fixed position and setting camera zoom
map.moveCamera(LatLng(35.767234, 51.330743), 0f)
map.setZoom(14f, 0f)
map.setOnCameraMoveFinishedListener { i: Int ->
runOnUiThread { progressBar.visibility = View.VISIBLE }
getReverseApi(map.cameraTargetPosition)
}
}
در اینجا، علاوه بر دریافت المان نقشه، سایر المانهای موجود در صفحه نیز دریافت میشود.
// We use findViewByID for every element in our layout file here
private fun initViews() {
map = findViewById(R.id.mapview)
addressTitle = findViewById(R.id.addressTitle)
progressBar = findViewById(R.id.progressBar)
}
برای اجرای یک درخواست، متدی که برای آن درخواست در GetDataService
نوشته شده است صدا زده شده است و خروجی آن در متغیر call
ذخیره میشود. برای بررسی پاسخ دریافتی از سرور، بر روی این متغیر متد enqueue
صدا زده میشود. در متد getReverseApi
متد getReverse
از getDataService
را فراخوانی کرده و متد enqueue
را بر روی آن صدا میزنیم و یک <Callback<NeshanAddress
جدید به عنوان ورودی به آن داده میشود و دو متد onResponse
و onFailure
در آن Override میشود.
در متد onResponse
اطلاعات محله و آدرس در صورت وجود در addressTitle
نمایش داده میشود و در غیر این صورت عبارت «معبر بی نام» نمایش داده خواهد شد. و درآخر progressBar
مخفی خواهد شد.
private fun getReverseApi(currentLocation: LatLng) {
getDataService.getReverse(currentLocation.latitude,currentLocation.longitude).enqueue(object : Callback<NeshanAddress> {
override fun onResponse(call: Call<NeshanAddress>, response: Response<NeshanAddress>) {
val address: String? = response.body()!!.address
if (address != null && !address.isEmpty()) {
addressTitle.text = address
} else {
addressTitle.text = "معبر بینام"
}
progressBar.visibility = View.INVISIBLE
}
override fun onFailure(call: Call<NeshanAddress>, t: Throwable) {
addressTitle.text = "معبر بینام"
progressBar.visibility = View.INVISIBLE
}
})
}