# 川哥餓了 ```abc X:1 T:DoRaAMon M:4/4 L:1/8 C:test by tron chen C:go-go-go C:this his third line C:ABC notation is cool! K:G |A,|c'4 c3 c|__D,4 ^^D4 |ee^f^g =g=f_e_d ccde edd2| |:G^A _Bc de dB|de dB de dB|^^c2ec __B2dB|c2A2 A2BA| GABc dedB|dedB dedB|c2ec B2dB|A2F2 G4:| |:g2gf gdBd|g2f2 e2d2|c2ec B2dB|A8 | g2gf g2Bd|g2f2 e2d2|c2ec B2dB|A2F2 G4:| ``` 我都看了些甚麼 0.0 *川餓* *川很餓* *川超餓* > [name=TronChen] ~~健康餐~~ 認真認真 選午餐 > 人生好難 > 錯~~~~~~~~ > 人生不難 >~~ 錯~~ > 安安 > 錯 * > pick one guys * ~~鳳姐鳳姐~~ 超讚 - [x] 鳳姐 | | | | | | 麥當勞+鳳姐 | 鳳姐 + 肯德基 | 給力盒子+鳳姐 | 7-11+ 鳳姐 | > []~~~~ 鳳姐|:---:| --- | --- | --- | --- | ----------- |:-------------:|:-------------:|:----------:| > [] | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 大 | 穿 | 超 | 餓 | 問 怎麼開始 錯 回家 問 午餐 > [name=TronChen] 錯 吃飯嗎 > [name=TronChen] 健康餐嗎 還是鳳姐 > [name=TaiyiLin] > [name=林群倫] > > [reference link] > [TOC] Emil +1 > > [什麼意思](/JkdxkJECTNCuRlys8L3lrw) > > > [time=Thu, Oct 29, 2020 12:20 PM] > |Tron Yo | Eric Hey | Ty No? | | -------- | -------- | -------- | | 問 | 錯 | 錯 | - [ ] 來 - [ ] 首先 - [ ] 我們 - [ ] 來 > 這裡是開心舒服底部 下面要認真 --- **1. How to achieve Coroutines? A coroutine is basically a call chain of suspend funs. Suspension is totally under your control: you just have to call suspendCoroutine. ``` GlobalScope.launch { <---- (A) val y = loadData() <---- (B) // suspend fun loadData() println(y) <---- (C) delay(1000) <---- (D) println("completed") <---- (E) } ``` 1. At (A), Kotlin starts executing the coroutine in the next available free thread (Say Thread01). 2. At (B), Kotlin stops executing the current thread, and starts the suspending function loadData() in the next available free thread (Thread02). 3. When (B) returns after execution, Kotlin continues the coroutine in the next available free thread (Thread03). 4. (C) executes on Thread03. 5. At (D), the Thread03 is stopped. 6. After 1000ms, (E) is executed on the next free thread, say Thread01. [](https://medium.com/jastzeonic/kotlin-coroutine-那一兩件事情-685e02761ae0/) ![](https://i.imgur.com/xGtsAOe.png) 在圖中我們可以理解IO Thread為在Main Tread之外的Coroutine 當處理suspend function A時,可以將之交給IO Tread處理完後還給Main Tread ![](https://i.imgur.com/3PMQIfB.png) ```kotlin= // Create a Coroutine scope using a job to be able to cancel when needed private var viewModelJob = Job() // the Coroutine runs using the Main (UI) dispatcher private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main) ``` ```kotlin= // Job()裡面有cancel()的選項可以停止coroutine override fun onCleared() { super.onCleared() viewModelJob.cancel() } ``` ```kotlin= private fun getMarketingHotsResult(isInitial: Boolean = false) { //coroutine coroutineScope.launch { if (isInitial) _status.value = LoadApiStatus.LOADING val result = stylishRepository.getMarketingHots() _homeItems.value = when (result) { is Result.Success -> { _error.value = null if (isInitial) _status.value = LoadApiStatus.DONE result.data } is Result.Fail -> { _error.value = result.error if (isInitial) _status.value = LoadApiStatus.ERROR null } is Result.Error -> { _error.value = result.exception.toString() if (isInitial) _status.value = LoadApiStatus.ERROR null } else -> { _error.value = getString(R.string.you_know_nothing) if (isInitial) _status.value = LoadApiStatus.ERROR null } } _refreshStatus.value = false } } ``` > [name=TronChen] 問 ```kotlin= object StylishRemoteDataSource : StylishDataSource { //口素這邊有suspend捏 override suspend fun getMarketingHots(): Result<List<HomeItem>> { if (!isInternetConnected()) { return Result.Fail(getString(R.string.internet_not_connected)) } return try { // this will run on a thread managed by Retrofit val listResult = StylishApi.retrofitService.getMarketingHots() listResult.error?.let { return Result.Fail(it) } Result.Success(listResult.toHomeItems()) } catch (e: Exception) { Logger.w("[${this::class.simpleName}] exception=${e.message}") Result.Error(e) } } ``` 釐清: 1.Scope 、CoroutineContext 、Job 三者的關係 *IO thread = 在Main Thread 之外處理 suspend fun 的 Thread 只是文章叫他IO,可能想讓他說是處理Input/Output的東東 *Main thread = 1. Main(thread)Scope 2. viewModel scope *suspend function = 支援可以中斷、再回復中斷前狀態執行的 function。但~~若以coroutine則不需要宣告為 suspend fun,因為改以副線程執行該funtion,不須中斷。~~ > [name=TronChen] > 可以理解為因Sample Code的dispatcher為Main Tread,所以在Main Tread以外調度的fun都需要加上suspend嗎? > 剛剛又聽Gary解釋了一次,以下的方式可能較恰當: 在跑coroutine的時候會以suspend跑,所以執行的fuction就不用suspend了 suspen fun 一定要在coroutine裡,但coroutine不一定要suspend fun (defined by your design) ![](https://i.imgur.com/YwnNKGA.png) suspend function 應該就是如字面上所說他可以慢慢處理的東西? 可以理解為“先不要”的function suspend function :+1: :+1: :+1: ![](https://i.imgur.com/X1fPeRr.jpg) *coroutine builders = **coroutines 提供了幾個 coroutine builders : a. runblock:啟動一個協程並 block 此線程直到協程完成 b. launch:在不阻塞當前線程的情況下啟動一個協程並返回一個 Job c. withContext:不會建立新的協程,而是在指定的協程上運行 d. async:在不阻塞當前線程的情況下啟動一個協程並返回一個叫 Defered 的 class,需要與 await 一起使用 *GlobalScope.launch 會回傳一個 Job class? *Scope = **Scope 指得是範圍,Coroutines 可以作用的範圍,管理多了Coroutine. *Job = ** Job 具體指的到底是甚麼?若以 Scope 來說,Scope 可以操作它旗下所有的 Coroutines ,但反過來說,假設我 cancel 了 Scope ,它底下所有的 Coroutines 也都取消了,這樣直接錯殺一百的方式好像不太妥當,那是不是有甚麼方式可以直接控制該 Coroutine 本身呢? 那其實可以注意到,如果調用 Scope 的 Extension launch 回傳的是一個 Job。 是的,就是 Job,Job 就是 Coroutines,更確切來說,Job 指的是 單一個 Coroutines 的生命週期。 *Thread = A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently. Every thread has a priority. When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). *dispatcher : Kotlin Coroutines 預先準備好供開發者使用的 CoroutineContext **dispatcher(四種調度員) a. Dispatchers.Default:使用的 Thread 取決於調用時當下的 Thread,最大並行的協程數量則依據系統的 cpu 核心數量,最少為兩個??? 原則上是會開另一個 Thread,通常不會跑在 Main thread 上 b. Dispatchers.Main:即跑在 Main Thread 上的包裝 c. Dispatchers.IO:會開一條 Worked Thread,並在上面執行,適合用在 IO 之類的耗時操作 :+1: :+1: :+1: d. Dispatchers.Unconfined:會跑在開啟時當下的 Thread ,但是一旦在 suspend 後再被恢復,可能會執行在其他線程上 --- 2. How to use Jetpack Navigation? 比較舊版本和新版本 jetpack: Jetpack is a suite of libraries to help developers follow best practices, reduce boilerplate code, and write code that works consistently across Android versions and devices so that developers can focus on the code they care about. [https://medium.com/@wsrew2000/android-navigation-%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-%E4%B8%80-%E5%9F%BA%E7%A4%8E%E4%BD%BF%E7%94%A8-3c1607ce4d38](https://) [https://medium.com/@wsrew2000/android-navigation-%E4%BA%8C-safeargs-f238f97617f4](https://) *Navigation Graph: *Destination: Navigation Graph中的畫面節點,可以是Activity或是Fragment *NavHostFragment: 實現單Activity多Fragment,而不是多Activity的APP > [name=TronChen] > 我們會把NavHostFragment assign 給 Mainactivity讓他去調度其他fragment的navigation > 所以在fragment與fragment之間的navigaion我們只能用findNavController去求MainActivity做navigation > ![](https://i.imgur.com/p37RaB2.jpg) *NavController: *Action: *Pop Action: 是不是要管理navigation後,back stack的狀態呀?因為back鍵總是會回到back stack的最上層,所以乾脆把back stack的最上層pop掉,剩下想要的?? 呢 問:SafeArgs. 以往如果要給pass資料給Fragment,通常要為他寫一個Instance function定義arguments,不是很直覺。Navigation Safe Args這個Gradle Plugin可以幫助你解決這個問題,在用Navigation navigate時就可以用自動產生的Class來傳遞資料,而且保證型態安全 ///問:那可以用SharedPrefferece存取再拿出ㄇ////問:什麼是SharedPreffernce/////UserManager.token記得ㄇ SharedPreference原本是用來儲存使用者的手機偏好設定,資料以字串儲存在手機的xml檔中。 問:什麼是popUpToInclusive 因為android會把每個造訪過的destination存進Back Stack中,當按下back鍵時目前的destination會被pop掉,回到上一個destination,例子: A -> B -> C -> A,按下back pop掉A,變為 A -> B -> C,所以畫面回到C。。改為app:popUpTo,是把stack中的destinations都pop掉,直到最後的Destination跟指定id一樣時。以剛才的case來講就是stack為: A -> B -> C,按下button後pop掉B、C,所以回到A。 popUpInclusive 的意思是pop到A,且把A destination也pop掉。 ```kotlin= <action android:id="@+id/action_c_to_a" app:destination="@id/destination_a" app:popUpTo="@id/destination_a" app:popUpToInclusive="true" /> ``` --- 3. How to implement RecyclerView & Adapter? > *difference(sample code) = ? *ViewHolder : > ViewHolder 就是我們儲存 View reference 的地方,大家也可以把它當成一個儲存 View 的 class。 *onBindViewHolder: > 因為 ViewHolder 會重複使用,我們要在這個 function 依據 position,把正確的資料跟 ViewHolder 綁定在一起。 > *onCreateViewHolder : > 建立 ViewHolder 的地方,如果有同時支援多種 layout 的需求,可以複寫 getItemViewType function,這個 function 就可以拿到不同的 viewType 以供我們識別。 > *getItemCount(新版本已可以不用override) >回傳整個 Adapter 包含幾筆資料。 補充: payload(在adapter裡面) 整個流程由以下 RecyclerView、RecyclerView.Adapter、RecyclerView.ViewHolder 三個 class 合力呈現。 ```kotlin= @BindingAdapter("images") fun bindRecyclerViewWithImages(recyclerView: RecyclerView, images: List<String>?) { images?.let { recyclerView.adapter?.apply { when (this) { is DetailGalleryAdapter -> { submitImages(it) } } } } } ``` ```kotlin= <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_detail_gallery" android:layout_width="0dp" android:layout_height="@dimen/height_detail_gallery" android:orientation="horizontal" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:images="@{viewModel.product.images}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> ``` ```kotlin= class DetailGalleryAdapter : RecyclerView.Adapter<DetailGalleryAdapter.ImageViewHolder>() { private lateinit var context: Context // the data of adapter private var images: List<String>? = null class ImageViewHolder(private var binding: ItemDetailGalleryBinding): RecyclerView.ViewHolder(binding.root) { fun bind(context: Context, imageUrl: String) { imageUrl.let { binding.imageUrl = it // Make sure the size of each image item can display correct val displayMetrics = DisplayMetrics() (context as Activity).windowManager.defaultDisplay.getMetrics(displayMetrics) binding.imageDetailGallery.layoutParams = ConstraintLayout.LayoutParams(displayMetrics.widthPixels, context.resources.getDimensionPixelSize(R.dimen.height_detail_gallery)) binding.executePendingBindings() } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder { context = parent.context return ImageViewHolder(ItemDetailGalleryBinding.inflate( LayoutInflater.from(parent.context), parent, false)) } /** * Replaces the contents of a view (invoked by the layout manager) */ override fun onBindViewHolder(holder: ImageViewHolder, position: Int) { images?.let { holder.bind(context, it[getRealPosition(position)]) } } override fun getItemCount(): Int { return images?.let { Int.MAX_VALUE } ?: 0 } private fun getRealPosition(position: Int): Int = images?.let { position % it.size } ?: 0 /** * Submit data list and refresh adapter by [notifyDataSetChanged] * @param images: [List] [String] */ fun submitImages(images: List<String>) { this.images = images notifyDataSetChanged() } } ``` --- 4. What's Data Binding? What's the difference between Data Binding and View Binding? [https://zhuanlan.zhihu.com/p/88762604](https://) ** Data Binding = Binding data (from code) to views + ViewBinding (Binding views to code) * ** View Binding = Only binding views to code. * View binding is enabled on a module by module basis. If view binding is enabled for a module, a binding class is generated for each XML layout file that the module contains.(翻譯蒟蒻) (TAT)/問 View Binding 是一项使你能更轻松地编写与视图交互的代码的功能. 在模块中启用 View Binding 后, 它会为该模块中存在的每一个 XML 文件生成一个对应的绑定类(binding class). 绑定类的实例包含了对应布局中所有具有 ID 的 view 的直接引用.大多数情况下, View Binding 可以替换 findViewById. *The main advantages of viewbinding are speed and efficiency. It has a shorter build time because it avoids the overhead and performance issues associated with databinding due to annotation processors affecting databinding's build time. In short, there is nothing viewbinding can do that databinding cannot do (though at cost of longer build times) and there are a lot databinding can do that viewbinding can"t **Data Binding vs View Binding** * 只有佈局文件的根標籤是 <layout> 時, Data Binding 才會生成對應的 binding class, View Binding 沒有這樣的要求 * Data Binding 會影響構建的速度. Data Binding 底層其實是通過 annotation processor 實現的, 對構建速度是有負面影響的. 而 View Binding 並不是通過 annotation processor 實現的, 因此解決了 Data Binding 的性能問題. * Data Binding可以自定義binding的類名,viewbinding不可以。 * Data Binding可以將view和界面上的數據進行雙向綁定, View Binding不行 * View Binding只是節省了你findviewbyid的過程,避免了findviewbyid會由於當前綁定xml中沒有找到view造成空指針的問題,以及查找是強轉時類型錯誤問題 ```kotlin= //DataBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main) as ActivityMainBinding //ViewBinding binding = ActivityMainBinding.inflate(layoutInflater) ``` --- 5. What's the difference between each passing data case in the sample code? track [StylishRepository.getMarketingHots]: -> [DefaultStylishRepository] : [StylishRepository] -> [StylishRemoteDataSource] : [StylishDataSource] Wayne怎麼傳遞資料 (fragment 傳 fragment,??? HomeFragment pass data to DetailFragment (navigate(it)) ```kotlin= viewModel.navigateToDetail.observe(viewLifecycleOwner, Observer { it?.let { findNavController().navigate(NavigationDirections.navigateToDetailFragment(it)) viewModel.onDetailNavigated() } }) ``` DetailFragment 接收 data 在navigation.xml 存取argument: productKey ```kotlin= <fragment android:id="@+id/detailFragment" android:name="app.appworks.school.stylish.detail.DetailFragment" android:label="DetailFragment" tools:layout="@layout/fragment_detail"> <argument android:name="productKey" app:argType="app.appworks.school.stylish.data.Product" app:nullable="false"/> </fragment> ``` argument Type 為 database: ProductTable ```kotlin= @Entity(tableName = "products_in_cart_table", primaryKeys = ["product_id", "product_selected_color_code", "product_selected_size"]) @TypeConverters(StylishConverters::class) @Parcelize data class Product( @ColumnInfo(name = "product_id") val id: Long, @ColumnInfo(name = "product_title") val title: String, @ColumnInfo(name = "product_description") val description: String, @ColumnInfo(name = "product_price") val price: Int, @ColumnInfo(name = "product_texture") val texture: String, @ColumnInfo(name = "product_wash") val wash: String, @ColumnInfo(name = "product_place") val place: String, @ColumnInfo(name = "product_note") val note: String, @ColumnInfo(name = "product_story") val story: String, @ColumnInfo(name = "product_colors") val colors: List<Color>, @ColumnInfo(name = "product_sizes") val sizes: List<String>, @ColumnInfo(name = "product_variants") val variants: List<Variant>, @ColumnInfo(name = "product_main_image") @Json(name = "main_image") val mainImage: String, @ColumnInfo(name = "product_images") val images: List<String> ) : Parcelable { val stocks: Int get() { var value = 0 for (variant in variants) { value += variant.stock } return value } @Embedded var selectedVariant: Variant = Variant("", "", -1) var amount: Long? = null } ``` 在fragment拿取productKey ```kotlin= class DetailFragment : Fragment() { /** * Lazily initialize our [DetailViewModel]. */ private val viewModel by viewModels<DetailViewModel> { getVmFactory(DetailFragmentArgs.fromBundle(requireArguments()).productKey) } } ``` 由專門處理Product類型的ViewModelFactory處理拿到的資料交給ViewModel ```kotlin= @Suppress("UNCHECKED_CAST") class ProductViewModelFactory( private val stylishRepository: StylishRepository, private val product: Product ) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>) = with(modelClass) { when { isAssignableFrom(DetailViewModel::class.java) -> DetailViewModel(stylishRepository, product) isAssignableFrom(Add2cartViewModel::class.java) -> Add2cartViewModel(stylishRepository, product) else -> throw IllegalArgumentException("Unknown ViewModel class: ${modelClass.name}") } } as T } ``` ViewModel拿到資料後assign value給內部的 MutableLiveData ```kotlin= class DetailViewModel( private val stylishRepository: StylishRepository, private val arguments: Product ) : ViewModel() { // Detail has product data from arguments private val _product = MutableLiveData<Product>().apply { value = arguments } } ``` --- 6. What's annotation in Kotlin? What annotations are used in the sample code? annotation:聲明註解 Kotlin中聲明主角用 annotation class 關鍵字,註解也是一種class,編譯器同樣可以對註解類型在編譯器進行類型檢查 元註解(Meta - annotation) | 元註解名 | 功能說明 | | -------- | -------- | | @Target |指定這個註解可被用於以下元素(在public enum class AnnotationTarget 中定義了枚舉)類Class、註解類ANNOTATION_CLASS、泛型參數TYPE_PARAMETER,函數FUNCTION、屬性PROPERTY、成員變量FIELD、局部變量LOCAL_VARIABLE、VALUE_PARAMETER、CONSTRUCTOR、PROPERTY_GETTER、PROPERTY_SETTER,接口描述類、接口、enum的TYPE,表達式EXPRESSION,文件FILE,類型別名TYPEALIAS | | @Retenion |指定這個註解的信息是否被保存到編譯後的class文件中,以及在運行時是否可以通過反射訪問到它。可取值有三個:SOURCE(註解數據不存儲在二進制輸出中),BINARY(註解的數據存儲到二進制輸出中,但是反射不可見。),RUNTIME(註解數據存儲在二進制輸出中,可用於反射,默認值是這個) | | @Repeatable |允許在單個元素上多次使用同一個註解 | | @MustBeDocumented |表示這個註解是公開API的一部分,在自動生成API文檔的類或者函數簽名中,應該包含這個註解信息 | | | | 來源: https://github.com/JustinSDK/JavaSE6Tutorial/blob/master/docs/CH17.md Annotation 的目的在對編譯器或分析工具說明程式的某些資訊,您可以在套件、類別、方法、資料成員等加上 Annotation,每一個 Annotation 對應於一個實際的 Annotation 型態。您可以從 java.lang.Override(@Override) 這個標準的 Annotation 型態開始瞭解 Annotation 的作用 java.lang.Override 是 J2SE 5.0 中標準的 Annotation 型態之一,它對編譯器說明某個方法必須是重新定義父類別中的方法,編譯器得知這項資訊後,在編譯程式時如果發現被 @Override 標示的方法並非重新定義父類別中的方法,就會回報錯誤。 (----------以下範例而已----------) 舉個例子來說,如果您在定義新類別時想要重新定義 Object 類別的 toString() 方法,您可能會寫成這樣: ```java= public class CustomClass { public String ToString() { return "customObject"; } } ``` 在撰寫 toString() 方法時,您因為打字錯誤或其它的疏忽,將之打成 ToString() 了,您編譯這個類別時並不會出現任何的錯誤,編譯器不會知道您是想重新定義 toString() 方法,只會當您是定義了一個新的 ToString() 方法。 您可以使用 java.lang.Override 這個 Annotation 型態,在方法上加上一個 @Override的Annotation,這可以告訴編譯器您現在定義的這個方法,必須是重新定義父類別中的同名方法。 ```java= public class CustomClass { @Override public String ToString() { return "customObject"; } } ``` (----------以上範例而已----------) ![](https://miro.medium.com/max/875/1*uBlt_Hq5Q9vkl5jzAPUU6w.jpeg) ~~A. annotation组成成分 a. Annotation : 在 Java 中有定義一個 Annotation 的 interface,其他 annotation 都要實作 Annotation。在 Kotlin,則是簡化成一個關鍵字 annotation 來表示某個 class 是 annotation b. ElementType (Java), AnnotationTarget (Kotlin) : 用來表示這個 annotation 可以用在什麼地方 c. RetentionPolicy (Java) / AnnotationRetention (Kotlin) : 照字面上的意思,可以解讀成 Annotation 要如何被保留???~~ ```kotlin= class CatalogFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { FragmentCatalogBinding.inflate(inflater, container, false).apply { lifecycleOwner = viewLifecycleOwner viewpagerCatalog.let { tabsCatalog.setupWithViewPager(it) it.adapter = CatalogAdapter(childFragmentManager) it.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabsCatalog)) } return@onCreateView root } } } ``` --- 7. How to handle RESTful API? 進化過程 A -> 參數化 -> 進化 B -> 實體化 ![](https://i.imgur.com/XTyOr60.gif) 泛型:<T> 泛型就是參數化(數碼寶貝化)類型,將類別參數化。讓你在定義類別、方法、介面時先不用決定型別,等到要實體化時再決定型別。 > [name=TronChen] 問:將object參數化的東東? ```kotlin= /** * Created by Wayne Chen in Jul. 2019. * * A generic class that holds a value with its api status. * @param <T> */ sealed class Result<out R> { data class Success<out T>(val data: T) : Result<T>() data class Fail(val error: String) : Result<Nothing>() data class Error(val exception: Exception) : Result<Nothing>() object Loading : Result<Nothing>() override fun toString(): String { return when (this) { is Success<*> -> "Success[result=$data]" is Fail -> "Fail[error=$error]" is Error -> "Error[exception=${exception.message}]" Loading -> "Loading" } } } ``` RESTful Api https://www.jianshu.com/p/7687365aa946 @Path:所有在网址中的参数(URL的问号前面),如: >http://102.10.10.132/api/Accounts/{accountId} @Query:URL问号后面的参数,如: >http://102.10.10.132/api/Comments?access_token={access_token} @QueryMap:相当于多个@Query @Field:用于POST请求,提交单个数据 @Body:相当于多个@Field,以对象的形式提交 Tips Tips1 使用@Field时记得添加@FormUrlEncoded Tips2 若需要重新定义接口地址,可以使用@Url,将地址以参数的形式传入即可。 ```kotlin= @GET Call<List<Activity>> getActivityList( @Url String url, @QueryMap Map<String, String> map); ``` ```kotlin= Call<List<Activity>> call = service.getActivityList( "http://115.159.198.162:3001/api/ActivitySubjects", map); ``` RESTful API 主要由三種元件組成: 1. Image for post Nouns 名詞:定義資源位置的 URL,每個資源在網路上都會有唯一的位置,就如每戶人家都有唯一的地址一樣。 2. Verbs 動詞:對資源要做的動作。 3. Content Types 資源呈現方式:API 資源可以以多種方式表現,最常用的是 JSON,較輕,也較好處理。 一般的 API,可能會是這樣: ``` 獲得資料GET /getData 新增資料POST /createData 刪除資料DELETE /deleteData/1 ``` 不同公司,不一樣的工程師,設計的名稱都會不一樣,沒有統一的命名方式,造成在引用各家 API 時,都需要詳讀 API 文件,理解所有設計命名規則後,才可使用。 若以 RESTful API 風格開發的話: ``` 獲得資料GET /data 新增資料POST /data 刪除資料DELETE /data/1 ``` 就是用一個唯一的 URL 定位資源,將動作藏在 HTTP 的 method 裡面。 所以使用 RESTful 風格設計的 API,就有了以下幾種優點及限制: 1. 有唯一的URL表示資源位置,統一的 API 接口。(Uniform Interface) 2. 無狀態。(Stateless) --- 8. What's MVVM architecture? What are M, V and VM in the sample code?** M-V-VM= Model View ViewModel ![](https://i.imgur.com/dPwFgMo.png) MVVM有助於將圖形化使用者介面的開發與業務邏輯或後端邏輯(資料模型)的開發分離開來,以便輕鬆管理和呈現物件。 批評 對這種模式的批評來自MVVM的創造者John Gossman本人。他指出,實現MVVM的開銷對於簡單的UI操作是「過度的」。他說,對於更大的應用來說,推廣ViewModel變得更加困難。而且,他說明了非常大的應用程式中的資料繫結會導致相當大的記憶體消耗。 **Model = 資料庫? **View = fragment & layout? **View Model = 資料處理 和 function? ```kotlin= ``` | Column 1 | Column 2 | Column 3 | | -------- | -------- | -------- | | Text | Text | Text |