> 技术文档 > android studio(NewsApiDemo)100%kotlin

android studio(NewsApiDemo)100%kotlin


api接口地址:https://newsapi.org/docs/get-started

项目成品地址:https://github.com/RushHan824/NewsApiDemo

项目效果展示:

 MVVM数据

 UML图


本系列文章将带你从零实现一个新闻列表App,适合零基础读者。一步步来,力求让每个人都能跟着做出来。

第一步:分析API和JSON,写好数据类。

1. 打开API网址,获取返回的JSON数据。

        

2. 观察JSON结构,分析每个字段。

        这里注意会有null的字段 所以kotlin中创建变量的时候需要注意

3. 用Kotlin写出对应的数据类,为后续网络请求和数据展示打好基础。

        创建一个数据类 data class

        然后根据上图的json结构 把数据类一层一层构建出来 还是比较简单的 结果如下图

        当然要注意上面说到的null字段问题 而kotlin中val搭配问好?比较好

                比如:val title: String? 就表示可能会null 安全且规范


第二步:配置Retrofit,完成网络请求

        1.添加依赖

                在build.gradle中添加Retrofit和Gson等依赖。

                如下图 注意是app的.gradle文件 依赖可以一起添加了

//实现网络请求的第一步:加上retrofit和gson的依赖 implementation(\"com.squareup.retrofit2:retrofit:2.9.0\") implementation(\"com.squareup.retrofit2:converter-gson:2.9.0\") //livedata viewmodel依赖 implementation(\"androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2\") implementation(\"androidx.lifecycle:lifecycle-livedata-ktx:2.6.2\") //Glide 图片加载 implementation(\"com.github.bumptech.glide:glide:4.16.0\") annotationProcessor(\"com.github.bumptech.glide:compiler:4.16.0\")

        2.定义api接口

                创建接口,声明请求方法和返回类型。

interface Top_ApiService { @GET(\"top-headlines\") suspend fun get_topNews( @Query(\"country\") country: String, @Query(\"apiKey\") apiKey: String ):News_Items}//我们访问一个网页需要网址 组成可以是baseurl+路径+参数//这里@GET就是一种请求方式 表示从网页里拿数据 括号里\"top-headlines\"可以表示具体的路径//suspend挂起 和kotlin中协程一起搭配 不会阻塞主线程 执行网络请求这样的耗时操作的时候 可以避免界面卡顿//接下去两个Query就是两个参数//最后返回类型是数据类//返回类型通常是我们根据JSON结构定义的数据类(如News_Items),Retrofit会自动帮我们把JSON解析成这个类的对象

        3.配置RetrofitClient                

object RetrofitClient {//object关键字声明了一个单例对象 这样RetrofitClient在全局只有一个实例 方便全局调用 不用每次都新建 private val client = OkHttpClient.Builder()//OkHttpClient 是Retrofit底层用来发网络请求的库 .addInterceptor(Interceptor { chain -> val request = chain.request().newBuilder() .header(\"User-Agent\", \"Mozilla/5.0 (Android)\") .build() chain.proceed(request)//表示继续执行请求 })//这里用Builder()自定义了一个OkHttpClient 加了一个拦截器Interceptor //拦截器可以在请求发出前、响应返回后做一些统一处理 //这里的拦截器给每个请求都加上了一个User-Agent请求头(模拟浏览器或App身份,有些API会校验这个) 对于现在用的api 不加这个拦截器不行 .build() val api: Top_ApiService by lazy {//懒加载的属性 只有第一次用到时才会初始化 节省资源 Retrofit.Builder() .baseUrl(\"https://newsapi.org/v2/\")//设置api基础网址 .addConverterFactory(GsonConverterFactory.create())//用gson把json自动转换成kotlin对象 .client(client) .build()//创建retrofit对象 .create(Top_ApiService::class.java)//创建API接口的实现对象 }}

第三步:MVVM架构与数据流转

        1.Repository层

class Repository(private val api: Top_ApiService){//数据仓库 //作用是在这里写一下api调用方法的具体实现 suspend fun getTopNews(country: String,apiKey: String):News_Items{ return api.get_topNews(country,apiKey) }}//在MVVM架构中 Repository负责从网络或本地数据库获取数据 并将数据提供给ViewModel 这样可以让ViewModel只关注数据的展示逻辑 而不用关心数据是怎么来的//getTopNews方法:调用API接口获取新闻数据,参数和返回值都和API接口保持一致。用suspend修饰,表示要在协程中调用,避免阻塞主线程

            2.ViewModel层

    class NewsViewModel(private val repository: Repository):ViewModel(){ private val _newsList = MutableLiveData() val newsList:LiveData = _newsList fun fetchTopNews(country:String,apikey:String){ viewModelScope.launch{ try{ val result=repository.getTopNews(country,apikey) _newsList.value=result }catch(_:Exception){ } } }}//ViewModel:用于管理界面数据,保证数据在界面旋转等情况下不会丢失。// 比如你在看新闻列表,突然手机横屏了,Activity会被销毁再重建。// 如果你把数据直接写在Activity里,界面重建后,数据会丢失,需要重新请求。//ViewModel的作用就是:即使界面重建,ViewModel里的数据还在,不会丢失,这样用户体验更好。//LiveData/MutableLiveData:实现数据的观察者模式,界面可以自动感知数据变化并刷新。//viewModelScope.launch:在ViewModel自带的协程作用域中启动协程,进行异步操作(如网络请求),不会阻塞主线程。//异常捕获:防止网络请求失败导致程序崩溃,可以在catch里加上错误提示

              3.Activity层            

      class NewsFeedActivity : AppCompatActivity() { private lateinit var viewModel: NewsViewModel private lateinit var adapter: NewsAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_news_feed) val recyclerView = findViewById(R.id.newsRecyclerView) recyclerView.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)//设置布局管理器 瀑布流 adapter = NewsAdapter()//创建适配器 recyclerView.adapter = adapter//绑定适配器 val repository = Repository(RetrofitClient.api)//创建数据仓库对象 viewModel = NewsViewModel(repository)//延迟初始化viewmodel viewModel.newsList.observe(this) { newsItems -> adapter.submitList(newsItems.articles) }// 通过 observe 订阅 newsList 数据// 当新闻数据发生变化时 自动调用 lambda 表达式 把新数据提交给 Adapter 刷新界面 viewModel.fetchTopNews(\"us\", \"08928b7fed414010820d9af990c05f89\") }}

      第四步:RecyclerView展示新闻列表

              1.编写Adapter

      class NewsAdapter : RecyclerView.Adapter() { private var articles: List
      = emptyList() fun submitList(list: List
      ) {//提交 articles = list notifyDataSetChanged()//通知recyclerview 数据发生变化 刷新一下 } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_news, parent, false) return NewsViewHolder(view) }//复制粘贴 override fun onBindViewHolder(holder: NewsViewHolder, position: Int) { holder.bind(articles[position]) }//当前位置数据绑定 override fun getItemCount(): Int = articles.size//告诉recyclerview有多少条数据 class NewsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {// private val titleTextView: TextView = itemView.findViewById(R.id.newsTitle) private val imageView: ImageView = itemView.findViewById(R.id.newsImage) fun bind(article: Article) {//将article数据绑定到控件上 titleTextView.text = article.title Glide.with(itemView.context).load(article.urlToImage).into(imageView)//Glide高效加载网络图片,防止界面卡顿 } }}//NewsAdapter:是 RecyclerView 的“桥梁”,负责把新闻数据展示到每一行 item 上//onCreateViewHolder:每当需要一个新的 item 行时,加载 item_news.xml 布局,生成 ViewHolder//onBindViewHolder:把对应位置的数据绑定到 ViewHolder 上//NewsViewHolder:用来缓存和管理 item_news.xml 里的控件,避免重复查找

          2.编写item布局