
Kotlin 语言相对 Java 有很多优势,比如官网介绍的简洁、安全,例子见 Kotlin 中文站 首页,部分示例解析见下文。Kotlin 具有现代(也有称下一代的)静态编程语言的很多特点,如类型推断、多范式支持、可空性表达、扩展函数、DSL 支持等。另外对于安卓开发还提供了 Kotlin 安卓扩展和 Anko 库,参见 Kotlin 用于 Android 。关于与 Java 互 *** 作,尤其是 Java 调用 Kotlin 是大家普遍觉得坑的地方,除了默认 final 外,还有一个主要原因应该就是名字修饰,解决方式可以按照它修饰后名字去引用,或者在 Kotlin 端使用 @JvmName 注解来生成便于 Java 使用的名字。具体参见 Java 中调用 Kotlin
```
/
author:CQ
Date:2020-04-09
Description:调拨出入库类型选择d出框
/
class AllotTypeDialog : Dialog {
private var cox: Context =null
private var allotTitle: TextView =null// 标题
private var allotRecyclerView: RecyclerView =null // 列表
private var allotSure: TextView =null // 确定按钮
private var allotTypeDialogAdapter: AllotTypeDialogAdapter =null // 适配器
private var list: MutableList =null// list
private var onSelectTypeListener: OnSelectTypeListener =null // 选择类型监听
private var selectIn: String =""
public fun SetOnSelectTypeListener(onSelectTypeListener: OnSelectTypeListener) {
thisonSelectTypeListener = onSelectTypeListener
}
constructor(context: Context) :super(context) {
cox = context
initView()
}
constructor(context: Context, themeStyle: Int, select: String) :super(context, themeStyle) {
cox = context
thisselectIn = select
initView()
}
private fun initView() {
list =mutableListOf()
onInitData()
}
private fun onInitData() {
val selectList =selectInsplit(",")
list!!run {
add(AllotTypeBean(0, "全部", selectListsize ==9))
add(AllotTypeBean(1, "渠道调拨", selectListcontains("1")))
add(AllotTypeBean(2, "渠道调退", selectListcontains("2")))
add(AllotTypeBean(3, "店铺配货", selectListcontains("3")))
add(AllotTypeBean(4, "店铺配退", selectListcontains("4")))
add(AllotTypeBean(5, "批发销货", selectListcontains("5")))
add(AllotTypeBean(6, "批发退货", selectListcontains("6")))
add(AllotTypeBean(9, "仓库移仓", selectListcontains("9")))
add(AllotTypeBean(10, "门店横调", selectListcontains("10")))
add(AllotTypeBean(30, "越级调拨", selectListcontains("30")))
}
}
override fun onCreate(savedInstanceState: Bundle) {
superonCreate(savedInstanceState)
var dialogWindow =window
dialogWindowsetGravity(GravityCENTER)
setContentView(Rlayoutdialog_allot_type)
allotTitle = findViewById(Riddialog_allot_type_title)
allotRecyclerView = findViewById(Riddialog_allot_type_recycleView)
allotSure = findViewById(Riddialog_allot_type_sure)
allotSure!!setOnClickListener{
var select =""
for (iin 1 until list!!size) {
if (list!![i]isCheck)
select +="${list!![i]type},"
}
if (select =="") {
(cox as AllotAbsActivity)toastMsg("请选择类型")
}else {
select = selectsubstring(0, selectlength -1)
if (onSelectTypeListener !=null) {
onSelectTypeListener!!onSelect(select)
}
dismiss()
}
}
allotTypeDialogAdapter = AllotTypeDialogAdapter(cox!!, list!!)
allotRecyclerView!!layoutManager = LinearLayoutManager(cox)
allotRecyclerView!!adapter =allotTypeDialogAdapter
allotTypeDialogAdapter!!setOnClickItemListener(object : AllotTypeDialogAdapterOnClickItemListener {
override fun onClickItem(position: Int) {
if (position ==0) {
list!![0]isCheck = !list!![0]isCheck
for (iin 1 until list!!size) {
list!![i]isCheck =list!![0]isCheck
}
}else {
list!![position]isCheck = !list!![position]isCheck
var selectAll =true
for (iin 1 until list!!size) {
if (!list!![i]isCheck)
selectAll =false
}
list!![0]isCheck = selectAll
}
allotTypeDialogAdapter!!setOnRefresh(list!!)
}
})
var windowManager = (cox as Activity)windowManager
var display = windowManagerdefaultDisplay
var lp =windowattributes
lpwidth = displaywidth 4 /5
windowattributes = lp
setCanceledOnTouchOutside(true)
}
/
获取显示类型
/
public fun getTypeShow(): String {
var show =""
for (iin 1 until list!!size) {
if (list!![i]isCheck)
show +="${list!![i]name},"
}
if (show =="")
return show
return showsubstring(0, showlength -1)
}
public interface OnSelectTypeListener {
fun onSelect(select: String)
}
}
1、来了老弟?
2、映射为List: map{},mapIndexed{},mapNotNull{},mapIndexedNotNull{}
val numbers = setOf(1, 2, 3)
println(numbersmap { it 3 })
//[3, 6, 9]
println(numbersmapIndexed { idx, value -> value idx })
//[0, 2, 6]
3、双路合并
(1)zip()将两个集合中具有相同位置的元素构建配对。
val list1 = listOf(1, 2, 3)
val list2 = listOf("a", "b", "c")
val list = list1zip(list2)
println(list)
//[(1, a), (2, b), (3, c)]
val list3 = list1zip(list2) { l1, l2 -> "${l1}----${l2}" }
println(list3)
//[1----a, 2----b, 3----c]
(2)反向转换unzip(),listunzip()得到一个Pair对象
val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
println(numberPairsunzip())
//([one, two, three, four], [1, 2, 3, 4])
4、关联
根据集合元素和与其关联的某些值构建 Map。
(1) Map associateWith() 。其中原始集合的元素是键,值来自给定的转换函数。 如果两个元素相等,则仅保留最后一个。
val numbers = listOf("one", "two", "three", "four")
val map = numbersassociateWith { itlength }
println(map)
//{one=3, two=3, three=5, four=4}
(2) associateBy()。 键来自给定的转换函数,原始集合中的元素是值。也可以使用keySelector和valueTransform设置键和值。两个元素相等,则仅保留最后一个。
val numbers = listOf("one", "two", "three", "four")
println(numbersassociateBy { itfirst()toUpperCase() })
//{O=one, T=three, F=four}
println(numbersassociateBy(keySelector = { itfirst()toUpperCase() }, valueTransform = { itlength }))
//{O=3, T=5, F=4}
(3)associate()。键和值都是根据集合元素生成的。associate() 会生成临时的 Pair 对象,这可能会影响性能。
data class FullName(val firstName: String, val lastName: String)
fun parseFullName(fullName: String): FullName {
val nameParts = fullNamesplit(" ")
if (namePartssize == 2) {
return FullName(nameParts[0], nameParts[1])
} else throw Exception("Wrong name format")
}
val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell")
println(namesassociate { name -> parseFullName(name)let { itlastName to itfirstName } })
//{Adams=Alice, Brown=Brian, Campbell=Clara}
5、打平
flatMap()可以打平集合中对象的某个类型为list的字段的值。
data class StringContainer(val values: List<String>)
val containers = listOf(
StringContainer(listOf("one", "two", "three")),
StringContainer(listOf("four", "five", "six")),
StringContainer(listOf("seven", "eight"))
)
println(containers)
//[StringContainer(values=[one, two, three]), StringContainer(values=[four, five, six]), StringContainer(values=[seven, eight])]
println(containersflatMap { itvalues })
//[one, two, three, four, five, six, seven, eight]
//即可以打平集合中对象的某个类型为list的字段的值
6、字符串表示
(1)joinToString() 根据提供的参数从集合元素构建单个 String。
val numbers = listOf("one", "two", "three", "four")
println(numbers)
//[one, two, three, four]
println(numbersjoinToString())
//one, two, three, four
println(numbersjoinToString("-", "开始", "结束"))
//开始one-two-three-four结束
println(numbersjoinToString("-", "开始", "结束", 3))
//开始one-two-three-结束
println(numbersjoinToString("-", "开始", "结束", 3, "等等"))
//开始one-two-three-等等结束
println(numbersjoinToString("-", "开始", "结束", 3, "等等", { it -> ittoUpperCase() }))
//开始ONE-TWO-THREE-等等结束
(2)joinTo() 执行相同的 *** 作,但将结果附加到给定的 Appendable (可追加的)对象。
val numbers = listOf("one", "two", "three", "four")
val listString = StringBuffer("The list of numbers: ")
numbersjoinTo(listString, "-", "开始", "结束", 3, "等等", { it -> ittoUpperCase() })
println(listString)
//The list of numbers: 开始ONE-TWO-THREE-等等结束
7、过滤
(1)filter()。只能检查集合中元素的值。
val numbers = listOf("one", "two", "three", "four")
val longerThan3 = numbersfilter { itlength > 3 }
println(longerThan3)
//[three, four]
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMapfilter { (key, value) -> keyendsWith("1") && value > 10 }
println(filteredMap)
//{key11=11}
(2)filterIndexed()。可以检查元素的索引和元素的值。
val numbers = listOf("one", "two", "three", "four")
val filteredIdx = numbersfilterIndexed { index, value -> (index != 0) && (valuelength < 5) }
println(filteredIdx)
//[two, four]
(3) filterNot()。使用否定条件来过滤集合。
val numbers = listOf("one", "two", "three", "four")
val filteredNot = numbersfilterNot { itlength <= 3 }
println(filteredNot)
//[three, four]
(4)filterIsInstance<T>() 返回给定类型的集合元素。
val numbers = listOf(null, 1, "two", 30, "four")
println(numbersfilterIsInstance<String>())
//[two, four]
(5)filterNotNull() 返回所有的非空元素。在一个 List<T> 上被调用时,filterNotNull() 返回一个 List<T: Any>。
val numbers = listOf(null, "one", "two", null)
println(numbersfilterNotNull())
//[one, two]
8、划分
partition() – 根据集合中元素的值进行过滤,并且返回一个Pair<List,List>。第一个list表示符合条件的集合;第二个list表示不符合条件的集合。
val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numberspartition { itlength > 3 }
println(match)
//[three, four]
println(rest)
//[one, two]
9、集合元素检查
(1)
如果至少有一个元素匹配给定谓词,那么 any() 返回 true。
如果没有元素与给定谓词匹配,那么 none() 返回 true。
如果所有元素都匹配给定谓词,那么 all() 返回 true。注意,在一个空集合上调用 all() 始终都会返回 true 。
val numbers = listOf("one", "two", "three", "four")
println(numbersany { itendsWith("e") })
//true
println(numbersnone { itendsWith("a") })
//true
println(numbersall { itendsWith("e") })
//false
println(emptyList<Int>()all { it > 5 })
//true
(2)当any() 和 none()不设置条件时, 它们只是用来检查集合是否为空。 如果集合中有元素(即使是null),any() 返回 true,否则返回 false;none() 则相反
10、集合加减:plus与minus
val numbers = listOf("one", "two", "three", "three", "four")
//集合加和,第二个 *** 作数可以是集合,也可以是元素
val plusList = numbers + "five"
println(plusList)
//[one, two, three, three, four, five]
//如果第二个 *** 作数是一个集合,那么移除其元素在原始集合中的 所有 出现。
val minusList1 = numbers - listOf("three")
println(minusList1)
//[one, two, four]
//如果第二个 *** 作数是一个元素,那么 minus 移除其在原始集合中的 第一次 出现
val minusList2 = numbers - "three"
println(minusList2)
//[one, two, three, four]
11、分组
(1) groupBy()
val numbers = listOf("one", "two", "three", "four", "five")
//根据转换函数得到key,value来自于集合的元素
println(numbersgroupBy { itfirst()toUpperCase() })
//{O=[one], T=[two, three], F=[four, five]}
//根据转换函数得到key,value来自于对原有集合元素的转换结果
println(numbersgroupBy(keySelector = { itfirst() }, valueTransform = { ittoUpperCase() }))
//{O=[one], T=[two, three], F=[four, five]}
(2) grouping()
在分组后,对所有的分组进行一次 *** 作。eachCount() 计算每个组中的元素。还有fold()、reduce()、aggregate()等。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbersgroupingBy { itfirst() }eachCount())
//{o=1, t=2, f=2, s=1}
12、取集合的一部分
(1)slice() 返回给定索引处的集合元素列表。
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbersslice(13))
//[two, three, four]
println(numbersslice(04 step 2))
//[one, three, five]
println(numbersslice(setOf(3, 5, 0)))
//[four, six, one]
(2)take和drop
val numbers = listOf("one", "two", "three", "four", "five", "six")
//从头开始获取指定数量的元素
println(numberstake(3))
//[one, two, three]
//从尾部开始获取指定数量的元素
println(numberstakeLast(3))
//[four, five, six]
//从头开始去除给定数量的元素
println(numbersdrop(1))
//[two, three, four, five, six]
//从尾部开始去除给定数量的元素
println(numbersdropLast(5))
//[one]
(3)takeWhile()和dropWhile()
val numbers = listOf("one", "two", "three", "four", "five", "six")
//不停地获取元素,直到遇到使表达式为false的首个元素。如果首个集合元素便使表达式为false,则结果为空。
println(numberstakeWhile { !itstartsWith('f') })
//[one, two, three]
//从尾部开始,如上
println(numberstakeLastWhile { it != "three" })
//[four, five, six]
//不停地删除元素,直到遇到使表达式为false的首个元素,然后返回剩余的元素。如果首个集合元素便使表达式为false,则返回集合所有的元素。
println(numbersdropWhile { itlength == 3 })
//[three, four, five, six]
//从尾部开始,如上
println(numbersdropLastWhile { itcontains('i') })
//[one, two, three, four]
(4)chunked()
将集合分为指定大小的子集
val numbers = (013)toList()
println(numberschunked(3))
//[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13]]
val numbers = (013)toList()
//将集合切割成指定大小的子集后,对子集元素进行转换
println(numberschunked(3) { itsum() })
//[3, 12, 21, 30, 25]
(5)windowed()
val numbers = listOf("one", "two", "three", "four", "five")
//检索指定大小的所有可能子集
println(numberswindowed(3))
//[[one, two, three], [two, three, four], [three, four, five]]
//设置步长
println(numberswindowed(3, 2))
//[[one, two, three], [three, four, five]]
//partialWindows为true时,即使剩余的元素数量小于窗口长度,也会返回结果
println(numberswindowed(3, 2, true))
//[[one, two, three], [three, four, five], [five]]
//对得到的所有子集进行转换
println(numberswindowed(3, 2, true, { list -> listmap { ittoUpperCase() } }))
//[[ONE, TWO, THREE], [THREE, FOUR, FIVE], [FIVE]]
(6)zipWithNext()
val numbers = listOf("one", "two", "three", "four", "five")
//从头开始,返回所有的相邻元素对(Pair)
println(numberszipWithNext())
//[(one, two), (two, three), (three, four), (four, five)]
//s1,s2即相邻元素对
println(numberszipWithNext { s1, s2 -> s1length > s2length })
13、取单个元素
(1)elementAt()获取指定索引处的元素
val str = linkedSetOf("one", "two", "three", "four", "five")elementAt(3)
(2)elementAtOrNull()
val numbers = listOf("one", "two", "three", "four", "five")
//越界后返回null
println(numberselementAtOrNull(5))
//null
(3)elementAtOrElse()
val numbers = listOf("one", "two", "three", "four", "five")
//越界后返回lambda表达式的结果
println(numberselementAtOrElse(5) { index -> "The value for index $index is undefined" })
//The value for index 5 is undefined
(4)first()和last()
first()和last()也可以根据表达式查找。但是如果没有查找到目标元素,将会抛出异常。用 firstOrNull() 和 lastOrNull()替代,如果没有找到,会返回null。
可以使用find() 代替 firstOrNull();使用 findLast() 代替 lastOrNull()
val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbersfirst { itlength > 3 })
//three
println(numberslast { itstartsWith("f") })
//five
(5) random()
随机返回一个元素。对于空集合会抛出异常。可以使用randomOrNull()替代,会返回null。
14、排序
(1)sortedWith(comparator: Comparator<in T>)
//compareBy()可根据给定的lambda表达式返回Comparator
println(listOf("aaa", "bb", "c")sortedWith(compareBy { itlength }))
//[c, bb, aaa]
(2) sorted() 和 sortedDescending()
val numbers = listOf("one", "two", "three", "four")
//自然升序
println(numberssorted())
//[four, one, three, two]
//自然降序
println(numberssortedDescending())
//[two, three, one, four]
(3) sortedBy(lambda表达式) 和 sortedByDescending(lambda表达式)
val numbers = listOf("one", "two", "three", "four")
val sortedNumbers = numberssortedBy { itlength }
println(sortedNumbers)
//[one, two, four, three]
val sortedByLast = numberssortedByDescending { itlast() }
println(sortedByLast)
//[four, two, one, three]
(4)reversed()和asReversed()
var numbers = mutableListOf("one", "two", "three", "four")
//返回原有集合的倒序副本,且和原有集合没有关联
println(numbersreversed())
//[four, three, two, one]
//返回原有集合的倒序视图,且和原有集合相关联。即改变原有的集合,已得到的倒序视图会发生改变。
val reversedNumbers = numbersasReversed()
println(reversedNumbers)
//[four, three, two, one]
numbersadd("five")
println(reversedNumbers)
//[five, four, three, two, one]
(5)shuffled()
返回一个以随机顺序排列的含有原有集合元素的新list
val numbers = listOf("one", "two", "three", "four")
println(numbersshuffled())
15、聚合
(1)聚合 *** 作如min()、max()、average()、sum()这些都只是对于数字类型的集合可用。count()计算集合中元素数量。
minBy{}、maxBy{}接收一个表达式;minWith{}、maxWith{}接收一个Comparator对象,可以由compareBy{}产生。
sumBy{}、sumByDouble{}接收一个表达式。前者对int类型的元素求和,并返回int。后者对Double类型的元素求和,并返回Double。
(2)fold()和reduce()。以及OrNull版本。
val numbers = listOf(5, 1, 3, 2)
//第一步的两个 *** 作数分别是集合中第一个和第二个元素。(则第一个元素无法乘以2)
println(numbersreduce { sum, element -> sum + element 2 })
//17
//为sum设置了初始值。每一次的 *** 作数都是sum和当前的集合元素。(可以保证每一个元素都乘以2)
println(numbersfold(0) { sum, element -> sum + element 2 })
//22
(3) reduceRight() 和 foldRight()从尾部开始进行计算。以及OrNull版本。
(4)foldIndexed()和reduceIndexed()将元素索引作为参数进行 *** 作。以及OrNull版本。
val numbers = listOf(5, 2, 10, 4)
println(numbersfoldIndexed(0) { idx, sum, element -> if (idx % 2 == 0) sum + element else sum })
//15
println(numbersreduceIndexed { idx, sum, element -> if (idx % 2 == 0) sum + element else sum })
//15
(5)reduceRightIndexed() 与 foldRightIndexed()从尾部开始进行计算。以及OrNull版本。
16、List *** 作
(1)getOrElse()、getOrNull()
val numbers = listOf(1, 2, 3, 4)
//越界时,返回null
println(numbersgetOrNull(5))
//越界时,返回表达式的结果,it等于index
println(numbersgetOrElse(6, { it }))
(2) indexOf() 返回元素第一个位置。 lastIndexOf() 返回元素最后一个位置。
(3)indexOfFirst() 和 indexOfLast() 可以接收一个表达式。
(4)二分查找 binarySearch()
要求该列表按照一定的顺序(自然排序或函数参数中提供的另一种排序)按升序排序过。
查询失败将返回 (-insertionPoint - 1),其中 insertionPoint 为应插入此元素的索引,以便列表保持排序。
如果存在多个目标元素返回其任一索引。
val numbers = mutableListOf("one", "two", "three", "four")
numberssort()
println(numbers)
//four, one, three, two]
println(numbersbinarySearch("two"))
// 3
println(numbersbinarySearch("z"))
// -5
println(numbersbinarySearch("two", 0, 2))
// -3
(5)通过Comparator使用二分查找
data class Product(val name: String, val price: Double)
val productList = listOf(
Product("WebStorm", 490),
Product("AppCode", 990),
Product("DotTrace", 1290),
Product("ReSharper", 1490))
println(productListbinarySearch(Product("AppCode", 990), compareBy<Product> { itprice }thenBy { itname }))
(6)fill()将集合中所有元素更新为指定值
val numbers = mutableListOf(1, 2, 3, 4)
numbersfill(3)
println(numbers)
//[3, 3, 3, 3]
17、set *** 作
(1)union()获取并集。对于有序集合, *** 作数顺序决定元素顺序。a union b
(2)intersect()获取交集。a intersect b
(3)subtract()获取差集。a subtract b
18、map *** 作
filter()、filterKeys()、filterValues()
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
println(numbersMapfilter { (key, value) -> keyendsWith("1") && value > 10 })
//{key11=11}
println(numbersMapfilterKeys { itendsWith("1") })
//{key1=1, key11=11}
println(numbersMapfilterValues { it < 10 })
//{key1=1, key2=2, key3=3}
前言,因为需要写一个播放器测试,需要读取到手机文件路径以获取到 mp4 格式文件的路径,进而播放,发现网络上的大多有问题,这里写下以作笔记记录。
1存储方式分为三部分:
先理解, 路径、绝对路径/相对路径、规范路径
总结: 路径 包含 绝对路径/相对路径 , 绝对路径 包含 规范路径 ,而 相对路径 不包含 规范路径 。
真实返回:
(待写)
参考 >
2011年出来的。由 JetBrain 的牛人 Dmitry Jemerov 在2011年开始带队开发,主要是解决Java之前被诟病已久的问题,而且积极借鉴了 Scala、Ruby 等新语言在开发效率和简洁性上的优势。由于是全新开发和设计的语言,当然在各方面上都有着其先进性,也就是重新设计的 Java现代版;比起 Scala 更加interoperate with Java,比如说很多 Kotlin 的库都可以一直复用 Java libraries。
Kotlin是JVM和Android的实用编程语言,结合了OO和功能特性,专注于互 *** 作性,安全性,清晰度和工具支持。
作为通用语言,Kotlin 可以在Java工作的地方工作:服务器端应用程序,移动应用程序(Android),桌面应用程序。它适用于所有主要的工具和服务,如
IntelliJ IDEA,Android Studio和Eclipse
Maven,Gradle和Ant
Spring Boot (Kotlin支持今天发布!)
GitHub,Slack甚至Minecraft :)
Kotlin的关键重点之一是混合Java + Kotlin项目的互 *** 作性和无缝支持,使采用更容易,从而减少了样板代码和更多的类型安全性。此外,Kotlin有一个广泛的标准库,使日常任务轻松流畅,同时保持字节码足迹低。当然,也可以在Kotlin中使用任何Java库。反之亦然。
kotlin语言学习教程:网页链接
个人博客:haichenyicom。感谢关注
java中的构造函数是与类名相同即可,kotlin里面的构造函数是用constructor关键字表示。
kotlin里面的构造函数分为主构造函数和次构造函数。 主构造函数只能有一个,次构造函数个数不限制,可以有一个或者多个
啥是主构造方法?啥是次构造方法呢?
我们可以看到主构造方法是没有方法体的,那么,我们需要初始化的数据应该放到哪呢?kotlin提供了init方法,给我们初始化数据。
那么,问题来了,次构造方法有方法体,会执行这个init模块吗?
结论: 不管是什么构造方法,先执行init模块逻辑,后执行构造方法的逻辑
简单的说一下继承,this和super两个关键字,跟java差不多;
this是调用自己的,super是调用父类的
类BBB继承类AAA,其中BBB分别有一个参数的构造方法和两个参数的构造方法;一个参数的构造方法用的this关键字调用自己的两个参数的构造;而两个参数的构造方法用的super关键字调用的父类两个参数的构造方法;这里控制台打印的数据:
说到了这个类,讲一下怎么重写属性的set/get方法
这里一个person类,里面有三个属性:name,age,address;在name和age下面分别写了set,get方法,address没写。
重点:
这里,我就只重写了name和age的set,get方法,没有重写address的set,get方法
这里,我再存名字的时候在名字的后面加上了>
目前很多开发组都用上协程来处理异步任务了,但是有的地方协程提供的原生API还是不足以应付,比方说一些SDK提供了传入Executor的接口(以便复用调用者的线程池来执行异步任务),这时候可以用这JDK提供的线程池,或者封装一下协程也可以满足需求。
协程提供了 DispatchersDefault 和 DispatchersIO 分别用于 计算密集型 任务和 IO密集型 任务,类似于RxJava的 Schedulerscomputation() 和 Schedulersio() 。
但两者有所差异,比如RxJava的 Schedulersio() 不做并发限制,而 Dispatchersio() 做了并发限制:
考虑到当前移动设备的CPU核心数都不超过64,所以可以认为协程的 DispatchersIO 的最大并发为64。
DispatchersDefault 的并发限制为:
考虑到目前Android设备核心数都在2个以上,所以可以认为 DispatchersDefault 的最大并发为 CPU cores。
DispatchersDefault 和 DispatchersIO 是共享协程自己的线程池的,二者可以复用线程。
不过目前这两个Dispatchers 并未完全满足项目中的需求,有时我们需要一些自定义的并发限制,其中最常见的是串行。
RxJava有 Schedulerssingle() ,但这个 Schedulerssingle() 和AsyncTask的 SERAIL_EXECOTOR 一样,是全局串行,不同的任务处在同一个串行队列,会相互堵塞,因而可能会引发问题。
或许也是因为这个原因,kotlin协程没有定义“DispatchersSingle"。
对于需要串行的场景,可以这样实现:
这样可以实现局部的串行,但和协程的线程池是相互独立的,不能复用线程。
线程池的好处:
然彼此独立创建线程池的话,会大打折扣。
如何既复用协程的线程池,又自主控制并发呢?
一个办法就是套队列来控制并发,然后还是任务还是执行在线程池之上。
AsyncTask 就是这样实现的:
用SerialExecutor的execute的任务会先进入队列,当mActive为空时从队列获取任务赋值给mActive然后通过线程池 THREAD_POOL_EXECUTOR 执行。
当然AsyncTask 的SerialExecutor是全局唯一的,所以会有上面提到的各种任务相互堵塞的问题。可以通过创建不同是的SerialExecutor实例来达到各业务各自串行。
在Kotlin环境下,我们可以利用协程和Channel来实现:
添加Log编写测试如下:
执行结果:
第一个任务可以顺利通过send(), 而随后的任务被suspend, 直到前面的任务执行完(执行block),调用recevie(), 然后下一个任务通过send() ……依此类推。
最终,消耗4s完成任务。
如果Channel的参数改成2,则能有两个任务可以通过send() :
最终,消耗2s完成任务。
关于参数可以参考Channel的构造函数:
在前面的实现中, 我们关注UNLIMITED, BUFFERED 以及 capacity > 0 的情况即可:
不过, [DispatchersIO] 本身有并发限制(目前版本是64),
所有对于 ChannelUNLIMITED 和 capacity > 64 的情况,和capacity=64的情况是相同的。
我们可以为不同的业务创建不同的Channel实例,从而各自控制并发且最终在协程的线程池上执行任务。
简要示意图如下:
为了简化,我们假设Dispatchers的并发限制为4。
通过Channel可以实现并发的控制,但是日常开发中有的地方并不是简单地执行个任务,而是需要一个ExecutorService或者Executor。
我们可以通过Channel封装一下:
需要简单地控制并发的地方,直接定义Channel然后调用runBlock即可;
需要Executor的地方,可创建ChannelExecutor来执行。
这章理一下channel,先分享一句学习时候看到的话: Do not communicate by sharing memory; instead, share memory by communicating 。本来好像是用在 go 上的,但也有着异曲同工之妙啊
channel 顾名思义是管道,有入口与出口。因此最底层有 sendChannel&receiveChannel
Produce = Coroutine + Channel
produce 也是产生协程,跟普通的 launch 不同他会返回一个 receiveChannel ,后面会看到 receiveChannel 是一个迭代器,同时会 suspend 在 hasNext和next() 上,因此另一个协程就可以使用 forin 等待接受。
同时, produce 发射完成后是会自己关闭的,省的我们自己关闭信道:
通过 job 的 invokeOnCompletion 实现。
与 produce 相反返回 sendChannel
我们看这里用了 offer 而不是 send ,我们可以把 forin 先简单的写成以下形式:
假设队列里没有东西时, enqueue 一个 receiveHasNext 进行等待。过会解释一下 channel 的原理。现在只要知道,当有 sendersend 时,与 receive 关联的 cont 就会被调用 resume ,那么显而易见,当 action 正在处理时队列中没有 receiver ,而 offer 是不会 suspend 的,因此事件就被抛弃。
这里我们使用 CONFALTED ,即合并所有事件,因此接受者永远处理最近一个。原理如下:
当 offer 失败时需要 suspend 等待,(说明还没有接受者或者人家正忙着),插入 sendBuffered ,同时移除前面已有的 sendBuffered
这样永远是最近一个生效。
其实看 abstractChannel 会先看到一个 queue ,这时候显而易见会把它当做是像 linkedlist 那种塞数据的地方。但其实 queue 是用来放 receive/send node 。当队列为空时, send 时会先从队列取第一个 receiveNode ,取不到就 suspend ,把自己当成 sendNode 放入;不然就把数据直接交给 receiveNode 。
具体channel实现时,例如 ArrayChannel(buffer) ,会多加一个 buffer 队列,当队列为空时, send 时会先从队列取第一个 receiveNode ,取不到就放入 buffer 队列,如果 buffer 队列满了,把自己当成 sendNode 放入就 suspend ;同时把不然就把数据直接交给 receiveNode 。
参考
select 可以等任何一个回来,也可以等 await :
跟 linux 里的 select 其实类似,(能知道是哪个吗?):
能看到 onReceive 是实现 SelectCaluse1 ,同时在 selectBuilderImpl 环境下:
所以会往 queue 中 enqueue 两个 receive节点。 以
同时能看到如果任何一次 select 节点获取数据以后:
会调用 blockstartCoroutineUnintercepted :
之前讲过 startCoroutineUnintercepted 其实就是 functioninvoke() ,所以就调用 blockinvoke(select的completion是自己) ,获得值后通过 uContresume 即可。
这个和 defered 即 job(Support) 搞在一起:
可以看到当任务成功后, select 会被继续进行
首先解决一个问题,一个 sender 多个 receiver 是怎么处理的。
因为是1vs1消费。只有第一个会收到,因为它插在等待队列的第一个。用 broadcast 可以保证大家都收到。它维护一个 subscribe 的 user list ,所有消费者都能收到 channelsend 的 element
可以实现跟 RX 一样的 *** 作符,接受者收到后经过转换再进行发送返回最终新的 receiveChannel
channel 是 hot 的。
Provide abstraction for cold streams
这个todo,后续再说。
Even smarter async with coroutine actors
官方文档
以上就是关于Kotlin 作为 Android 开发语言相比传统 Java 有什么优势全部的内容,包括:Kotlin 作为 Android 开发语言相比传统 Java 有什么优势、使用kotlin自定义dialog详解、kotlin集合高效使用等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)