Kotlin 从入门到实战(二)

Kotlin协程入门与实战指南 Kotlin 协程作为 Android 异步编程的革命性解决方案,确实正在改变开发者处理并发任务的方式。 阅读详情

上一篇文章的地址是:http://blog.youkuaiyun.com/callmesp/article/details/74372582

这次带来的是一个 当当网 的爬虫app。
先放出GitHub链接:

https://github.com/CallMeSp/DangDang

先看一下结构分析图:
这里写图片描述

功能不复杂,但是有Activity、Adapter、自定义view、MVP结构、SQLite等一个app有的几乎一切。
功能实现过程说明:

  • 在搜索框中输入要搜索的书籍名称

  • 点击搜索后,首先判断数据库中是否有缓存

  • 有缓存则默认加载数据库中的缓存

  • 否则通过Jsoup去爬取

  • 爬取的结果会存入新建的一张对应搜索名字的表

  • 上拉加载更多:会将新加载的书籍列表存入对应数据库

  • 下拉刷新:将已有的数据库中的缓存清空,重新爬取,并将数据写入数据库

下面进入正文:
首先这是一个爬虫,要先建立我们的model类

class Bookitem (var map:MutableMap<String,Any?>){
    var name: String by map

    var detail: String by map

    var price: String by map

    var author: String by map

    var imgurl: String by map

    var publisher: String by map

    var detailurl:String by map
    constructor(name :String,detail:String,price:String,author:String,imgurl:String,publisher:String,detailurl:String):this(HashMap()){
        this.name=name
        this.detail=detail
        this.price=price
        this.author=author
        this.imgurl=imgurl
        this.publisher=publisher
        this.detailurl=detailurl
    }
}

本来单纯建立这样一个类是并不需要这个Map的,但是这里面是为了后面的Sqlite操作做铺垫,anko框架中数据库存储和读取值得时候的映射就是要通过这个Map来实现的,要注意的是这里面每个变量的名字要和数据库Table中对应的列的名字一样。

createTable(tbname,true,"id" to INTEGER+ PRIMARY_KEY+ AUTOINCREMENT,
        "name" to TEXT,
        "price" to TEXT,
        "author" to TEXT,
        "publisher" to TEXT,
        "detail" to TEXT,
        "imgurl" to TEXT,
        "detailurl" to TEXT)

下面就来看一下关键的几个功能实现:
1.根据书名和页码获取列表

 fun getBookListByNameAndPage(bookname: String, page: Int, ismore: Boolean) {
    Observable.just(bookname)
            .observeOn(Schedulers.newThread())
            .map { s ->
                mainpresenter.showPB()
                val bookitemArrayList = ArrayList<Bookitem>()
                val doc = Jsoup.connect("http://search.dangdang.com/?key=$s&act=input&page_index=$page").get()
                Log.e("..",doc.toString())
                val elements = doc.select("ul.bigimg").select("li")
                for (element in elements) {
                    val name = element.select("a").attr("title")
                    val detailurl = element.select("a").attr("href")
                    val imgurl = if (!element.select("a").select("img").attr("src").startsWith("http"))
                        element.select("a").select("img").attr("data-original")
                    else
                        element.select("a").select("img").attr("src")
                    val detail = element.select("p.detail").text()
                    val price = element.select("p.price").select("span.search_now_price").text()
                    val author = element.select("p.search_book_author").select("span")[0].select("a").attr("title")
                    val publisher = element.select("p.search_book_author").select("span")[2].select("a").text()
                    val bookitem = Bookitem(name ,detail,price,author,imgurl,publisher,detailurl)
                    Log.e(TAG,"detailurl:"+detailurl)
                    bookitemArrayList.add(bookitem)
                }
                bookitemArrayList
            }
            .subscribe(object : DisposableObserver<ArrayList<Bookitem>>() {
                override fun onNext(bookitems: ArrayList<Bookitem>) {
                    if (!ismore) {
                        mainpresenter.updateUI(bookitems)
                    } else {
                        mainpresenter.addMoreList(bookitems)
                    }
                }

                override fun onError(e: Throwable) {
                    mainpresenter.updateUI(ArrayList<Bookitem>())
                    mainpresenter.hidePB()
                }

                override fun onComplete() {
                    mainpresenter.hidePB()
                }
            })
}

2.书籍详情界面获取procontent

fun getDetail(url:String){
    Observable.just(url)
            .observeOn(Schedulers.newThread())
            .subscribe(object :DisposableObserver<String>(){
                override fun onError(e: Throwable?) {

                }

                override fun onComplete() {

                }

                override fun onNext(t: String?) {
                    val detailurl = url
                    Log.e(TAG, "url?:" + t)


                    val doc = Jsoup.connect(detailurl).get()
                    Log.e("??", doc.toString())
                    var elements_content = doc.select("div.t_box").select("div.t_box_left")
                    var procontent = doc.select("div.t_box").select("div.t_box_left").select("div.pro_content").select("ul").select("li")
                    var contents = doc.select("div.section")
                    var catalog = doc.select("div.section")

                    var stb:StringBuffer= StringBuffer()
                    //procontent加入换行符
                    for (str:Element in procontent){
                        stb.append(str.text())
                        stb.append("\n")
                    }

                    detailpresenter.setProContent(stb.toString())
                    Log.e("procontent",stb.toString())
                }
            })

}

3.DbHelper

class DbHelper(context:Context):ManagedSQLiteOpenHelper(context, DB_NAME,null, DB_VERSION){
    val mcontext=context
    @Volatile var TB_NAME:String=""
    companion object {
        val DB_NAME="history.db"
        val DB_VERSION=1
        val TAG="DbHelper"
        //val instance by lazy{ DbHelper()}
    }
    override fun onCreate(db: SQLiteDatabase?) {
        Log.e(TAG,"onCreate")
    }

    fun insertNewTable(tbname:String,booklist:ArrayList<Bookitem>){
        DbHelper(mcontext).use {
            /*createTable(tbname,true,"id" to INTEGER+ PRIMARY_KEY+ AUTOINCREMENT,
                    "bookname" to TEXT,
                    "price" to TEXT,
                    "author" to TEXT,
                    "publisher" to TEXT,
                    "detail" to TEXT,
                    "imgurl" to TEXT,
                    "detailurl" to TEXT)*/
            execSQL("create table if not exists "+tbname+"(it INTEGER primary key AUTOINCREMENT, " +
                    "name text," +
                    "detail text," +
                    "price text," +
                    "author text,"
                    +"imgurl text," +
                    "publisher text," +
                    "detailurl text)")
            Log.e(TAG,"create table:"+tbname)
            booklist.forEach { insert(tbname,
                    "name" to it.name,
                    "price" to it.price,
                    "author" to it.author,
                    "publisher" to it.author,
                    "detail" to it.detail,
                    "imgurl" to it.imgurl,
                    "detailurl" to it.detailurl)
                Log.e(TAG,"insert into table:"+tbname+": "+it.name)}
        }
    }
    fun addToOldTable(tbname:String,booklist:ArrayList<Bookitem>) {
        DbHelper(mcontext).use {
            booklist.forEach {
                insert(tbname,
                        "name" to it.name,
                        "price" to it.price,
                        "author" to it.author,
                        "publisher" to it.author,
                        "detail" to it.detail,
                        "imgurl" to it.imgurl,
                        "detailurl" to it.detailurl)
                Log.e(TAG,"insert into table:"+tbname+": "+it.name)
            }
        }
    }

    fun getListFormDb(bookname:String)= DbHelper(mcontext).use {
        select(bookname).parseList {
            Bookitem(HashMap(it))
        }
    }

    fun dropTable(bookname: String)=DbHelper(mcontext).use {
        clear(bookname)
    }
    fun IshaveTB(bookname:String):Boolean=DbHelper(mcontext).use {
        var result:Boolean=false
        var cursor=rawQuery("select count(*) as c from sqlite_master where type ='table' and name ="+"\'"+bookname+"\';",null)
        if (cursor.moveToNext()){
            var count:Int=cursor.getInt(0)
            if (count>0){
                result=true
            }
        }
        result
    }
    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        db!!.dropTable(TB_NAME,true)
        onCreate(db)
    }
    fun <T : Any> SelectQueryBuilder.parseList(parser: (Map<String, Any?>) -> T): List<T> =
            parseList(object : MapRowParser<T> {
                override fun parseRow(columns: Map<String, Any?>): T = parser(columns)
            })
    fun <T : Any> SelectQueryBuilder.parseOpt(parser: (Map<String, Any?>) -> T): T? =
            parseOpt(object : MapRowParser<T> {
                override fun parseRow(columns: Map<String, Any?>): T = parser(columns)
            })
    fun SQLiteDatabase.clear(tableName: String) {
        execSQL("delete from $tableName")
    }
}

4.上拉下拉监听:

class SwipeRefreshView(context: Context, attrs: AttributeSet) : SwipeRefreshLayout(context, attrs) {

    private val mScaledTouchSlop: Int

    private val mFooterView: View

    private var recyclerview: RecyclerView? = null

    @Volatile private var condition2: Boolean = false

    private var MonLoadListener:()->Unit={}
    /**
     * 正在加载状态
     */
    private var isLoading: Boolean = false

    internal var layoutInflater: LayoutInflater

    init {
        // 填充底部加载布局
        mFooterView = View.inflate(context, R.layout.view_footer, null)
        // 表示控件移动的最小距离,手移动的距离大于这个距离才能拖动控件
        mScaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
        layoutInflater = LayoutInflater.from(context)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        // recyclerview,设置recyclerview的布局位置
        if (recyclerview == null) {
            // 判断容器有多少个孩子
            if (childCount > 0) {
                // 判断第一个孩子是不是ListView
                if (getChildAt(0) is RecyclerView) {
                    // 创建ListView对象
                    recyclerview = getChildAt(0) as RecyclerView

                    // 设置ListView的滑动监听
                    setRecyclerViewOnScroll()
                }
            }
        }
    }

    /**
     * 在分发事件的时候处理子控件的触摸事件

     * @param ev
     * *
     * @return
     */
    private var mDownY: Float = 0.toFloat()
    private var mUpY: Float = 0.toFloat()

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {

        when (ev.action) {
            MotionEvent.ACTION_DOWN ->
                // 移动的起点
                mDownY = ev.y
            MotionEvent.ACTION_MOVE ->
                // 移动过程中判断时候能下拉加载更多
                if (condition2 && canLoadMore()) {
                    // 加载数据
                    loadData()
                    return false
                }
            MotionEvent.ACTION_UP ->
                // 移动的终点
                mUpY = y
        }
        return super.dispatchTouchEvent(ev)
    }

    /**
     * 判断是否满足加载更多条件

     * @return
     */
    private fun canLoadMore(): Boolean {
        // 1. 是上拉状态
        val condition1 = mDownY - mUpY >= mScaledTouchSlop
        if (condition1) {
            println("是上拉状态")
        }

        // 3. 正在加载状态
        val condition3 = !isLoading
        if (condition3) {
            println("不是正在加载状态")
        }
        return condition1 && condition3
    }

    /**
     * 处理加载数据的逻辑
     */
    private fun loadData() {
        println("加载数据...")
        if (MonLoadListener != null) {
            // 设置加载状态,让布局显示出来
            setLoading(true)
            MonLoadListener()
        }
    }

    /**
     * 设置加载状态,是否加载传入boolean值进行判断

     * @param loading
     */
    fun setLoading(loading: Boolean) {
        // 修改当前的状态
        isLoading = loading
     }


    /**
     * 设置RecyclerView的滑动监听
     */
    private fun setRecyclerViewOnScroll() {

        recyclerview!!.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
            val layoutManager = recyclerview!!.layoutManager
            val linearManager = layoutManager as LinearLayoutManager
            val lastItemPosition = linearManager.findLastVisibleItemPosition()
            if (recyclerview!!.adapter.itemCount - 1 == lastItemPosition) {
                condition2 = true
            } else {
                condition2 = false
            }
        }
    }

    fun setCondition2(isLoading: Boolean) {
        condition2 = isLoading
    }


    /**
     * 上拉加载的接口回调
     */


    fun setMOnLoadListener(listener:()->Unit){
        MonLoadListener=listener
    }

    companion object {

        private val TAG = "SwipeRefreshView"
    }
}

要注意的是这里的DBhelper中的扩展函数。
如:

fun dropTable(bookname: String)=DbHelper(mcontext).use {
        clear(bookname)
    }
  • Kotlin的扩展函数功能使得我们可以为现有的类添加新的函数,实现某一具体功能 。
  • 扩展函数是静态解析的,并未对原类添加函数或属性,对类本身没有任何影响。

  • 扩展属性允许定义在类或者kotlin文件中,不允许定义在函数中。

一文详细讲解CRM系统(附架构图、流程、功能介绍) 专为中小企业量身打造高性价比的数字化转型路径!企业如何用CRM才能真正做到降本增效?今天,就用一文讲清这些问题!读完本文,你将清楚得了解CRM的概念、框架、流程、功能、企业成功案例,文末还有常见问题解答。 阅读详情

相关推荐

MCGS 昆仑通态触摸屏 modbus TCP 数据转发

MCGS触摸屏数据转发modbus TCP数据转发

m0_63451859的博客 2万+

Kotlin Anko 中 parseList,parseOpt,parseSingle 的区别与联系

目录1. 前言2. 正文3. 最后参考 1. 前言 今天学习了 kotlin Anko 库的 sqlite 部分的知识,对于 parseList,parseOpt,parseSingle 三者的区别,花了一点时间搞清楚了。 2. 正文 三者都是用来解析查询结果的。这是它们的共同之处。也就是说,当我们有了某个 Cursor 对象时,可以使用 parseList,parseOpt,parseSingl...

willwaywang6 708

不懂就学之挑战成为WorldQuant Brain顾问的第1天

WorldQuant Brain 是由 WorldQuant 公司开发的一个基于人工智能和机器学习的量化研究平台,旨在帮助量化研究员发现和构建有效的投资策略。WorldQuant 是一家全球领先的量化投资公司,专注于利用数据科学、技术和金融工程开发量化交易策略。

Yanbang1的博客 8384

kotlin学习(九)

从数据库中保存或查询数据前面一个章节中我们讲了关于SQLiteOpenHelper的创建,但是我们需要在必要的时候有方法去保存我们的数据到数据库,或者从我们的数据库中查询数据。另外一个叫ForecastDb类就会做这件事。创建数据库model类但是首先,我们要去为数据库创建model类。你还记得我们之前所见的map委托的方式?我们要把这些属性直接映射到数据库中,反过来也一样。我们先来看下CityF...

weixin_40119478的博客 705

信号转换为图像 时域频域分析

一维信号如何转化为图像?深度学习如何学习 1.当然,直接1d卷积也是可以的。 2. 以信号强度的数值作为纵坐标,以时间为横坐标绘制信号点就可以了。 周霖那个即是 要看看代码是如何做的, 即使是单导联 5000个点,怎么变成50005000的分辨率的 3. 直接把多通道的一维信号合并成二维图像,然后直接按图像卷积的方法去做~~ 2225000这样一个图 ~可以看看语音处理的相关Net,其中比较...

王不留行嘿呦嘿 2万+

〔重庆理工大学〕大学物理实验--变温霍耳效应及其参数测定

每个老师要求不同,实验报告内容和数据仅供参考。

Pretty Boy Fox的博客 3043

Kotlin开发Android笔记13:Android数据库(SQLite)操作

SQLite是一个嵌入app的轻量级数据库,Android使用SQLite作为它的数据库管理系统。 在不使用第三方数据操作框架如GreenDao等的情况下,我们操作Android的操作数据库 的API在Android中是非常原生的。需要编写很多诸如增删除改查的SQL语句以及对象与ContentValues或者Cursors之间的解析处理。现在使用Kotlin和Anko,我们可以大量简化这些。 –

true100的专栏 9398

反应动力学仿真软件:Kintecus_(3).Kintecus安装与配置

在本节中,我们将详细介绍如何安装和配置Kintecus反应动力学仿真软件。Kintecus是一款功能强大的工具,用于模拟和分析化学反应的动力学过程。正确安装和配置软件是开始使用它的第一步。我们将分步骤介绍软件的安装过程,并提供一些基本配置的示例。

kkchenjj的博客 1552

Kotlin入门篇(六),list和map的使用

list的定义和使用fun main(args: Array<String>) { var nameList:MutableList<String> = ArrayList() nameList.add("小明"); nameList.add("小红"); nameList.add("小刚"); for((index,name) in nameList.wit

Chay_Chan的博客 5126

pytorch 51 GroundingDINO模型导出tensorrt并使用c++进行部署,53ms一张图

博主基于https://github.com/hpc203/GroundingDINO-onnxrun项目分享的onnx c++推理代码,修改为GroundingDINO模型的tensorrt推理代码。对于600x400的输入,在3060显卡下,cuda 12.1,TensorRT-10.4.0,window环境下53ms处理完一张图片。

a486259的博客 1220

YOLO26涨点改进 | 检测头Head创新改进篇 | 利用RFAConv感受野注意力卷积改进YOLO26检测头,RFAHead感受野注意力检测头,助力小目标检测高效涨点

YOLO26作为轻量化目标检测模型的新一代标杆,凭借高效的Backbone与Neck结构,在实时检测场景中表现突出,但原生检测头仍存在核心痛点——感受野固定、小目标特征感知薄弱、多尺度目标适配不足,导致在小目标密集、复杂背景遮挡等场景中漏检率偏高、定位精度不足。

qq_36130719的博客 167

kotlin语言学习文档 for Android

《Kotlin for android developers》中文版翻译 错别字、病句、翻译错误等问题可以提issues。请说明错误原因。在线阅读或下载GitBook 在线阅读 希望大家购买正版,建议阅读英文原版:https://leanpub.com/kotlin-for-android-developers Github: https://github.com/wangjiegulu/kot

绝地黑鹰的博客 3789

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Kotlin协程Coroutines入门到实战:(二)Coroutines初体验

Kotlin协程Coroutines入门到实战:(二)Coroutines初体验 上一篇文章中我们对异步回调的本质做了比较深入的探讨,最终得出了**“异步回调就是代码的多线程顺序执行”的结论。接着引出了Kotlin协程可以实现顺序编写异步代码**,自动进行线程切换的作用。既然Kotlin协程那么神奇,那具体该如何使用呢? 1.添加依赖 Kotlin协程不属于Kotlin语言本身,使用之前必须手动引...

NJP_NJP的博客 3450

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

史上最详Android版kotlin协程入门进阶实战(二)

kotlin协程的关键知识点 上一本章节末尾我们提到,将在本章节中对以下知识点做初步讲解,包含上文提到的launch和async函数中的3个参数作用。清单如下: 协程调度器CoroutineDispatcher 协程下上文CoroutineContext作用 协程启动模式CoroutineStart 协程作用域CoroutineScope 挂起函数以及suspend关键字的作用 当然还有一些其他的知识点也是很重要的,比如:CoroutineExceptionHandler、Continuation、.

AI大模型的博客 543

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Kotlin入门到精通(实战)- 流程控制(二)透彻解析 return、break、continue

由于 Kotlin 有 Lambda,匿名函数等概念,使得在 Kotlin 中 return 和 jump 相对于 C 语言稍微复杂点,本篇文章就给大奖讲清楚 Kotlin 中的 return 和 jump 的各种情况的用法。 常见的 return 和 jump,主要包含 return、continue、break。 return 官方对 return 的介绍:By default returns from the nearest enclosing function or anonymous functi

Chiclaim 1791

史上最详Android版kotlin协程入门进阶实战指南,你学废了吗?

Kotlin协程 Kotlin协程是目前很流行的一款用于异步任务处理的库,都知道它处理异步任务特别好用,但是很少人去探究它背后的原理。还有一点,由于它是用于处理异步任务的,很多人将协程与线程做对比,也有一些人将协程与Rxjava做对比。这篇文章将从最简单的用法开始,层层递进的讲解协成知识点。 如何使用使用协程,以及协程中的一些重要概念 首先先介绍一下怎么开启一个协程,在Android开发中,如果是在Activity或者Fragment中,那么可以通过以下这种方式开启一个协程。 class MainActiv

zhireshini233的博客 996

最全Android Kotlin 学习路线(Kotlin 从入门、进阶到实战)

Kotlin 是由 jetBrains 开发的一门现代多平台应用的静态编程语言,Kotlin 代码即可以编译成 Java 字节码,又可以编译成 JavaScript,Kotlin 是开源的,源码在这。Kotlin 包含了大量的语法糖,在编码的时候,会大大的简化我们的代码量及工作效率。且相比传统的 Java 语言,Kotlin 种大量的简写,可以减少很多用Java 必须要写的样板代码,减少大量的 if…else 等嵌套,减少大量接口的实现,代码结构也会更加清晰。

Eqiqi的博客 5867

Kotlin协程入门指南+进阶实战,Kotlin语言真的太香了

2017年,Google在的I/O大会上宣布Kotlin正式成为Android的一级开发语言。这门新语言学习成本低、还可以和Java互相调用、非常人性化,自发布伊始其使用人数就呈爆发式的增长。直至今日,已有**35%**的 Android专业开发人员使用,很多公司在招聘开发人员时,也会要求对方会Kotlin。毋庸置疑的是,。因此,为帮助大家掌握Kotlin这门功能丰富、简洁轻便的编程语言,这里分享一份谷歌技术团队开源的(包含入门教程指南、高级强化实战)如有需要完整版的可点击文末卡片查看获取方式!!

datian1234的博客 439

Kotlin入门到精通(实战)- 学前必读

一、我为什么推出这门课程呢? Google 在 IO 大会上宣布 Kotlin 晋升为开发Android的一级语言了。而且很多关于 Android 的开源项目都是基于 Kotlin 开发的。所以对于 Android 开发来说 掌握 Kotlin 是很多有必要的 Kotlin本身的一些特性如简洁性,可以减少大量的模板代码,大大提高开发者的效率如 安全性,避免了空指针异常;如 Kotlin 和 Java 代码100% 的互操作性让 Kotlin 语言的应用场景更加广阔,能使用 Java 的地方都能使用 Kotl

Chiclaim 2422

最新 Kotlin 资源大全(入门指南+进阶实战),学 Kotlin 看这一篇就够了

在 Android 生态中主要有 C++、Java、Kotlin 三种语言 ,它们的关系不是替换而是互补。其中,C++ 的语境是算法和高性能,Java 的语境是平台无关和内存管理,而 **Kotlin 则融合了多种语言中的优秀特性,带来了一种更现代化的编程方式。** 就在今年的6月9号,Kotlin 发布了1.7.0正式版,并引入了新的 Kotlin K2 编译器的 **Alpha** 版本,旨在加快新语言功能的开发,统一Kotlin支持的所有平台带来性能改进。Kotlin的快速迭代给应用开发带来了更大的

m0_59162559的博客 1037

Kotlin入门到精通(实战)- 总目录

学前必读 Kotlin入门到精通(实战)- 前言 Kotlin入门到精通(实战)- 总目录 第1章 Kotlin 简单入门 在这一章,我将带大家从从最基本的 Kotlin 函数和变量开始,为我们后面的 Kotlin 内容做好铺垫。还会从最基本的表达式和语句的概念开始讲起,让大家明白 Kotlin 的语法糖为什么是这样的,而不用去死记硬背。紧接着还会介绍 Kotlin 中的枚举类,并且还是着重讲解在实际开发中如何使用枚举,让你明白什么时候用枚举什么时候使用整型常量,你会明白枚举的语法虽然很简单。但是在实际开发

Chiclaim 3899

字节+谷歌超全Kotlin学习王炸笔记!Kotlin入门到精通+高级Kotlin强化实战(附Demo)(2)

目前已经更新的部分资料:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)[外链图片转存中…(img-aT45fjGA-1712395392007)][外链图片转存中…(img-x0VMNfdQ-1712395392008)][外链图片转存中…(img-KVV5ntsX-1712395392008)]

2301_79985012的博客 950

最新Android Kotlin开发指南,从入门到实战,一战通关

想必在读此文的各位对Koltin并不陌生,其是一门可以运行在Java虚拟机、Android、浏览器上的静态语言,具有等特征,最主要的是其专注于与Java代码的互操作性,。Kotlin可以很好地和所有现存的Java库和框架一起工作,而且性能水平和Java旗鼓相当,现已成为。相比于Java,Kotlin还具有安全、代码简洁、扩展函数、内联函数、支持线程的并发协同等优势,可以说是有开发经验的程序员跳槽进阶的热门之选。

Eqiqi的博客 1640

【Kotlin】史上超强二合一,Kotlin从入门到精通+高级Kotlin强化实战(附Demo)

Kotlin作为谷歌官方支持的编程语言,目前,不少公司的Android开发工程师岗位表示要有Kotlin基础,同时这也让Kotlin成为了很多人的学习对象。而Kotlin作为Android开发的首选语言,为开发者提供了大量的语法糖和技巧,空指针异常情况也得到了明显改善,使用起来更加顺手,能让开发者更专注于需求开发,而将语言所带来的影响减少到最少。除此之外,Kotlin全面兼容Java,在Android开发中将Kotlin和Java编程语言一起使用,这种互操作性使其具有一个良好的生态。

datian1234的博客 951

最全Android Kotlin入门教程(Kotlin 入门指南、高级Kotlin强化实战、Kotlin协程进阶实战)

Kotlin 是一种新型的静态类型编程语言,有超过 60% 的专业 Android 开发者在使用,它有助于提高工作效率、开发者满意度和代码安全性。不仅可以减少常见代码错误,还可以轻松集成到现有应用中。目前在安卓开发中,当你查看源码的时候,你会发现大量的Kotlin 源码,在使用一些 jetpack 框架的时候,大部分也都是Kotlin 语言。相信不久之后,Kotlin 将会成为 Android 开发的首选语言。

datian1234的博客 1286
上一篇: Kotlin 从入门到实战(一)
下一篇: java反射机制扫盲
CallMeSP
博客等级 码龄10年 48粉丝 39原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值