#关于Android新语言Kotlin初识
2017年5月的IO大会上,Google宣布,kotlin成为开发Android的一级编程语言.
Kotlin不仅可以再JVM上运行,还可以直接将Kotlin源代码转换成JavaScript,理论上将,kotlin可以在任何支持JavaScript的环境中运行,例如WEB应用,reactNatice(Android和IOS),微信公众号,微信小程序,Node.js等,另外,还有一些地方是JavaScript做不到的,如开发本地应用,但kotlin可以做到,kotlin不仅仅可以生成JavaScript代码,还可以直接编译成本地代码,如Windows的exe文件,iOS APP等,这样一来,kotlin几乎和可以开发所有类型的应用.
##kotlin的优势:
- 容易学习
kotlin是一门包含很多函数式编程思想的面相对象编程语言
- 轻量级
相比其他编程语言,kotlin的函数库更小,小于7000个
- 高度可互操作性:
kotlin可以和其他java类库友好且简单的进行互操作
- 继承Androidstudio及gradle
专门的插件,和工具
- 其他语法层面的特性,如数据模型类,空类型安全,扩展函数等
kotlin能做什么
- 服务端开发
kotlin是基于JVM的编程语言,自然可以使用所有基于JVM的服务端框架 例如:spring
- 以JavaScript方式运行
kotlin提供了生成JavaScript源代码的能力
- 开发Android App
kotlin和java源代码可以在同一个工程中,可以联合进行调试
##kotlin的基本语法
-
定义变量
// 定义变量 var m: Int? = 0 // 自动推导变量的数据类型 var n = 10 // 定义常量 val f = 30 // 定义数组 var arr1 = arrayOf(1, 2, 3, 4) var arr3: IntArray? = null // 定义集合 var list1 = arrayListOf<String>() var list2: ArrayList<String>? = null // 定义字符串 var s1 = "Hello \nworld" var s2: String = "世界\n 你好" var s3 = """ hello world I love you. """ -
定义方法
// 定义普通方法 fun test() { } // 定义带返回值方法 fun test1(): String { return "返回值" } -
get,set方法
var address: String = "" get() { println("get Method is run") return if (field.contains("北京市")) { field.replace("北京市", "许昌市") } else { field } } set(value) { println("setMethod is run") field = value } -
定义构造器
// 一级构造器 class KotlinBean constructor() { } // 二级构造器 class KotlinBean constructor() { constructor(index: Int) : this() } -
数值类型
| 数据类型 | 占用字节数 |
|---|---|
| Double | 8 |
| Float | 4 |
| Long | 8 |
| Int | 4 |
| Short | 2 |
| Byte | 1 |
-
字符串模板
// 字符串模板 添加占位符 $ override fun toString(): String { return "KotlinBean(m=$m, n=$n, f=$f, arr1=${Arrays.toString(arr1)}, arr3=${Arrays.toString(arr3)}, list1=$list1, list2=$list2)" } -
if语句
// 定义带返回值方法 fun test1(): String { return if (n > 10) { "ssss" } else { "返回值" } } -
when语句(替代原来的switch语句)
rg_navigation.setOnCheckedChangeListener { group, checkedId -> when (checkedId) { R.id.rb_home -> { switchTab(0) } R.id.rb_mirror -> { switchTab(1) } R.id.rb_bbs -> { switchTab(2) } R.id.rb_mine -> { switchTab(3) } } } fun test2(n: Int) { when (n) { 1 -> { } 2 -> { } // 使用in关键字 in 3..5 -> { } // 使用函数表达式的返回值 test3() -> { } else -> { } } } -
for循环
fun test4() { // 强循环,直接遍历 list1.forEach { t: String? -> println(t) } for (s: String in list1) { println(s) } // 根据角标遍历 for (i in list1.indices) { println(list1[i]) } // 根据角标遍历,同时获取角标和对应的元素值 for ((index, value) in list1.withIndex()) { println(index) println(value) } } -
修饰符
- private 仅在类的内部可以访问
- protected 类似private,但在子类中也可以访问
- internal 任何在模块内部类都可以访问
- public 任何类都可以访问
-
类的继承
与Java不同,kotlin类的继承需要使用冒号(:),冒号后面需要调用父类的构造器
需要注意的是kotlin默认是的class是final的,也就是说默认class不允许继承,需要显示的使用open关键字允许继承class
open class KotlinBean constructor() { constructor(index: Int) : this() } -
重写方法 & 重写属性
在kotlin中,不仅类默认是不可继承的,连方法默认也是不可重写的.因此,如果要在子类中重写方法,就需要在父类相应的方法前面加open关键字,而且要在子类重写的方法前面加override关键字
-
接口
kotlin中的接口与Java中的类似,使用interface关键字声明.一个类可以实现多个接口,并且接口中的属性和方法都是open的
interface KotlinInterfaceTest { fun getUserInfo() // 在kotlin中,允许接口的方法包含默认的方法体 fun setUserInfo(name: String) { val userBean = UserBean() userBean.name = name }
}
-
null 值安全性
在java中饱受null异常的困扰,在kotlin中可以试图解决这个问题
kotlin中导致NPE情况
- 明确调用throw NullPointerException()
- 使用!! 操作符
- 外部的Java代码导致
- 初始化过程中存在某些数据不一致
-
开始使用kotlin进行Android开发
- 创建一个应用
勾选kotlin选项,点击Next,知道项目创建完成



- 编写XML文件

- 在activity中设置UI显示

- 创建一个应用
-
其他示例
class HomeFragment @SuppressLint("ValidFragment") private constructor() : Fragment() { var value: HomeFragment? = null var imageList = arrayListOf<Int>() lateinit var mContext: Context lateinit var mHomeAdapter: HomeAdapter private object Holder { val INSTANCE = HomeFragment() } companion object Factory { fun getInstance(): HomeFragment { return Holder.INSTANCE } } override fun onAttach(context: Context?) { super.onAttach(context) mContext = context!! } override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_home, container, false) } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initView() initData() } private fun initView() { banner.setImageLoader(GlideImageLoader()) imageList.add(R.mipmap.ic_driver) imageList.add(R.mipmap.ic_police) imageList.add(R.mipmap.ic_trance) banner.setImages(imageList).start() rv_top_lines.setHasFixedSize(true) rv_top_lines.layoutManager = LinearLayoutManager(mContext) } class MyLinear2(context: Context?) : LinearLayoutManager(context) { override fun canScrollVertically(): Boolean { return false } } private fun initData() { mHomeAdapter = HomeAdapter() rv_top_lines.adapter = mHomeAdapter HttpManager.getInstance().getRetrofit().create(IApiServices::class.java).getHostList() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnNext { t: BaseBean<List<MovieBean>>? -> if (t != null) { val data = t.data data.forEach { t -> println(t.toString()) } } }.subscribe(object : Observer<BaseBean<List<MovieBean>>> { override fun onSubscribe(d: Disposable) { println("onSubscribe") } override fun onError(e: Throwable) { println("onError" + e.message) Toast.makeText(mContext, e.message, Toast.LENGTH_SHORT).show() } override fun onNext(t: BaseBean<List<MovieBean>>) { println("onNext" + t.toString()) mHomeAdapter.setNewData(t.data) } override fun onComplete() { println("onComplete") Toast.makeText(mContext, "onComplete", Toast.LENGTH_SHORT).show() } }) } override fun onStart() { super.onStart() //开始轮播 banner.startAutoPlay() } override fun onStop() { super.onStop() //结束轮播 banner.stopAutoPlay() } }
- home页tab切换
package com.bidostar.kotlindemo.activity import android.os.Bundle import android.support.v7.app.AppCompatActivity import com.bidostar.kotlindemo.R import com.bidostar.kotlindemo.fragment.BbsFragment import com.bidostar.kotlindemo.fragment.HomeFragment import com.bidostar.kotlindemo.fragment.MineFragment import com.bidostar.kotlindemo.fragment.MirrorFragment import kotlinx.android.synthetic.main.activity_home.* class HomeActivity : AppCompatActivity() { // 1. lateinit 延迟加载 // 2.lateinit 只能修饰, 非kotlin基本类型 lateinit var mHomeFragment: HomeFragment lateinit var mMirrorFragment: MirrorFragment lateinit var mBbsFragment: BbsFragment lateinit var mMineFragment: MineFragment override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) initView() val transaction = supportFragmentManager.beginTransaction() transaction.add(R.id.fl_content, mHomeFragment, "home") transaction.add(R.id.fl_content, mMirrorFragment, "mirror") transaction.add(R.id.fl_content, mBbsFragment, "bbs") transaction.add(R.id.fl_content, mMineFragment, "mine") transaction.commitAllowingStateLoss() rg_navigation.setOnCheckedChangeListener { group, checkedId -> when (checkedId) { R.id.rb_home -> { switchTab(0) } R.id.rb_mirror -> { switchTab(1) } R.id.rb_bbs -> { switchTab(2) } R.id.rb_mine -> { switchTab(3) } } } switchTab(0) } private fun initView() { mHomeFragment = HomeFragment.getInstance() mMirrorFragment = MirrorFragment.getInstance() mBbsFragment = BbsFragment.getInstance() mMineFragment = MineFragment.getInstance() } private fun switchTab(index: Int) { val transaction = supportFragmentManager.beginTransaction() when (index) { 0 -> { transaction.show(mHomeFragment) .hide(mMirrorFragment) .hide(mBbsFragment) .hide(mMineFragment) .commitAllowingStateLoss() } 1 -> { transaction.show(mMirrorFragment) .hide(mHomeFragment) .hide(mBbsFragment) .hide(mMineFragment) .commitAllowingStateLoss() } 2 -> { transaction.show(mBbsFragment) .hide(mHomeFragment) .hide(mMirrorFragment) .hide(mMineFragment) .commitAllowingStateLoss() } 3 -> { transaction.show(mMineFragment) .hide(mHomeFragment) .hide(mMirrorFragment) .hide(mBbsFragment) .commitAllowingStateLoss() } } } } - adapter使用
package com.bidostar.kotlindemo.adapter import com.bidostar.kotlindemo.R import com.bidostar.kotlindemo.bean.MovieBean import com.bumptech.glide.Glide import com.chad.library.adapter.base.BaseQuickAdapter import com.chad.library.adapter.base.BaseViewHolder /** * @author zsh27 * @date 2017/12/14. * description . * @since 0 */ class HomeAdapter(layoutResId: Int) : BaseQuickAdapter<MovieBean, BaseViewHolder>(layoutResId) { init { } constructor() : this(R.layout.home_movie_item) override fun convert(helper: BaseViewHolder?, item: MovieBean?) { if (item != null) { helper?.setText(R.id.tv_name, item.movieName) helper?.setText(R.id.tv_desc, item.name) Glide.with(mContext) .load(item.img) .into(helper?.getView(R.id.iv_cover)) } } }
项目实战
kotlin&java混编
-
项目配置
project级的gradle配置 dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.tencent.bugly:tinker-support:1.1.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } module级的gradle配置 apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') testImplementation "junit:junit:$rootProject.dependVersion.junit" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "com.android.support:design:$rootProject.dependVersion.support" implementation "com.android.support.constraint:constraint-layout:$rootProject.dependVersion.constraint_layout" } -
常量类
object NetConstant { /** * 连接超时 */ const val DEFAULT_CONNECT_TIME_OUT = 15L /** * 读取超时 */ const val DEFAULT_READ_TIME_OUT = 15L /** * 写入超时 */ const val DEFAULT_WRITE_TIME_OUT = 30L } -
单例类
class HttpManager private constructor() { /** * 构造方法私有 */ init { Logger.d("HttpManager:" + "init:" + "_debug:" + Debug) initRetrofit() } companion object { /** * 单例对象 */ var BaseUrl: String? = null var BaseResourceUrl: String? = null var Debug = false var InterceptorList: List<Interceptor>? = null var NetworkInterceptorList: List<Interceptor>? = null /** * 获取HttpManager * * @return HttpManager实例 instance */ val instance: HttpManager get() { return Holder.INSTANCE } fun paramsBuild(): HttpManager.ParamsBuild { return HttpManager.ParamsBuild() } /** * 自定义Retrofit示例 * 可以设置一些自定义参数,通过buildRetrofit()构建Retrofit实例 * 或者通过buildApiService()直接构建一个网络请求接口 * * @return 自定义RetrofitBuilder http builder . builder */ fun newBuilder(): HttpBuilder.Builder { return HttpBuilder.Builder() } } } 使用方法 1. java文件中使用 HttpManager.Companion.getInstance() .create(IAppServices.class) .getDeviceActivateStatus() .compose(RxSchedulers.INSTANCE.<com.bidostar.netlibrary.BaseResponse<DeviceStatusBean>>applyIoSchedulers()) .compose(this.<com.bidostar.netlibrary.BaseResponse<DeviceStatusBean>>bindToLifecycle()) 2. kotlin文件中使用 HttpManager.instance .create(IAppServices::class.java) .deleteDevicePic(mDeviceCode, bean.id.toLong()) .compose(RxSchedulers.applyIoSchedulers()) .compose(this.bindToLifecycle()) -
view控件
-
findViewById()
var mTvTitle = findViewById(R.id.tv_title) var mIvBack = findViewById(R.id.iv_back) -
kotlin注解方式
import kotlinx.android.synthetic.main.activity_mirror_album_detail.* mAdapter = AlbumPagerAdapter(mContext) view_pager.adapter = mAdapter view_pager.addOnPageChangeListener(this) mAdapter!!.setNewData(mItemList) view_pager.currentItem = mIndex mIvBack.setOnClickListener { finish() } tv_delete.setOnClickListener(this) tv_download.setOnClickListener(this)
-
-
mvp使用
M层 class DeviceCaptureModelImpl : BaseModel(), DeviceCaptureContract.IDeviceCaptureModel { override fun getLastPicture(alarmId: Int, deviceCode: Long, alarmDay: String, pageSize: Int, respType: String): Observable<BaseResponse<List<MirrorAlbumBean>>> { return HttpManager.instance .create(IAppServices::class.java) .getDeviceAlarm(alarmId, deviceCode, alarmDay, pageSize, respType) .compose(RxSchedulers.applyIoSchedulers()) } override fun getDeviceStatus(deviceCode: Long, rousing: Boolean): Observable<BaseResponse<Int>> { return HttpManager.instance.create(IAppServices::class.java) .deviceStatus(deviceCode, rousing) .compose(RxSchedulers.applyIoSchedulers()) } override fun devicecapture(deviceCode: Long): Observable<BaseResponse<CaptureBean>> { return HttpManager.instance.create(IAppServices::class.java) .deviceCapture(deviceCode) .compose(RxSchedulers.applyIoSchedulers()) } override fun sendMessage(deviceCode: Long, content: String): Observable<BaseResponse<String>> { return HttpManager.instance.create(IAppServices::class.java) .sendTextToDevice(deviceCode, content) .compose(RxSchedulers.applyIoSchedulers()) } } P层 class DeviceCapturePresenterImpl : BasePresenter<DeviceCaptureContract.IDeviceCaptureView, DeviceCaptureModelImpl>(), DeviceCaptureContract.IDeviceCapturePresenter { override fun getDeviceStatus(deviceCode: Long, rousing: Boolean) { m.getDeviceStatus(deviceCode, rousing) .compose(RxSchedulers.applyIoSchedulers()) .subscribe(object : BaseObserver<Int>() { override fun handleResult(response: BaseResponse<Int>) { when (response.resultCode) { BaseResponse.RET_HTTP_STATUS_OK -> { when (response.data) { BaseResponse.RET_HTTP_STATUS_OK -> { v.onLine() } BaseResponse.RET_MIRROR_STATUS_NO -> { v.offLine() } BaseResponse.RET_MIRROR_STATUS_YES -> { v.onStarting() } } } BaseResponse.RET_TIMEOUT_CODE -> { v.timeOut() } else -> { v.onError() v.showErrorTip(response.errorMsg) } } } }) } } V层 @Route(path = ARouterConstant.DEVICE_CAPTURE) class DeviceCaptureActivity : BaseMvpActivity<DeviceCapturePresenterImpl>(), DeviceCaptureContract.IDeviceCaptureView, Handler.Callback, DeviceConnectDialog.onMirrorConnectClickListener { private lateinit var mTvTitle: TextView private lateinit var mIvBack: ImageView private var mCar: Car? = null private var mHandler: WeakRefHandler? = null private var mIsCapture = false private var mContent = "" private var mConnectDialog: DeviceConnectDialog? = null override fun newPresenter(): DeviceCapturePresenterImpl { return DeviceCapturePresenterImpl() } override fun getLayoutView(savedInstanceState: Bundle?): Int { return R.layout.activity_device_capture } override fun initView(savedInstanceState: Bundle?) { mIvBack = findViewById(R.id.iv_back) mTvTitle = findViewById(R.id.tv_title) mIvBack.setOnClickListener { finish() } mTvTitle.text = "后视镜远程操控" mIvBack.setOnClickListener { finish() } ivCapture.setOnClickListener { mIsCapture = true if (mCar?.deviceType != 1) { DialogUtils.showBindDialog(mContext) return@setOnClickListener } ivCapture.isClickable = false showMirrorConnectDialog() p.getDeviceStatus(mCar!!.deviceCode, true) } ivAlbum.setOnClickListener { if (mCar?.getDeviceType() != 1) { DialogUtils.showBindDialog(mContext) return@setOnClickListener } ARouter.getInstance().build(ARouterConstant.DEVICE_CAPTURE_ALBUM) .navigation() } tvSendMessage.setOnClickListener { mIsCapture = false if (mCar!!.deviceType != 1) { DialogUtils.showBindDialog(mContext) return@setOnClickListener } mContent = etMessage.text.toString().trim() if (TextUtils.isEmpty(mContent)) { showToast("消息内容不能为空") return@setOnClickListener } tvSendMessage.isClickable = false showMirrorConnectDialog() p.getDeviceStatus(mCar!!.deviceCode, true) } } override fun initData() { mCar = ApiCarDb.getCar(mContext) mHandler = WeakRefHandler(this) if (mCar?.deviceType == 1) { p.getLastPicture(207, mCar!!.deviceCode, "", 1, "list") } } } -
java文件转换成kotlin
java文件 public class AlbumPagerAdapter extends PagerAdapter { private List<MirrorAlbumItemBean> mDataList; private Context mContext; private final LayoutInflater mInflater; public AlbumPagerAdapter(Context context) { mContext = context; mInflater = LayoutInflater.from(mContext); if (mDataList == null) { mDataList = new ArrayList<>(); } } public void setNewData(List<MirrorAlbumItemBean> list) { if (mDataList == null) { mDataList = new ArrayList<>(); } mDataList.clear(); mDataList.addAll(list); notifyDataSetChanged(); } @Override public int getCount() { return mDataList.size(); } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return object == view; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { MirrorAlbumItemBean bean = mDataList.get(position); View inflate = mInflater.inflate(R.layout.device_album_viewpager_item_layout, container, false); ImageView imageView = inflate.findViewById(R.id.pv_image); TextView tvLocation = inflate.findViewById(R.id.tv_location); TextView tvTime = inflate.findViewById(R.id.tv_time); tvLocation.setText(TextUtils.isEmpty(bean.getLocation()) ? "位置位置" : bean.getLocation()); tvTime.setText(bean.getAlarmTime()); Glide.with(mContext) .load(bean.getUrl()) .into(imageView); container.addView(inflate); return inflate; } @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { container.removeView((View) object); } }

class AlbumPagerAdapter(private val mContext: Context) : PagerAdapter() {
private var mDataList: MutableList<MirrorAlbumItemBean>? = null
private val mInflater: LayoutInflater = LayoutInflater.from(mContext)
init {
if (mDataList == null) {
mDataList = ArrayList()
}
}
fun setNewData(list: List<MirrorAlbumItemBean>) {
if (mDataList == null) {
mDataList = ArrayList()
}
mDataList!!.clear()
mDataList!!.addAll(list)
notifyDataSetChanged()
}
fun removeData(position: Int) {
if (mDataList!!.size > position) {
mDataList!!.removeAt(position)
}
notifyDataSetChanged()
}
override fun getCount(): Int {
return mDataList!!.size
}
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return `object` === view
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val bean = mDataList!![position]
val inflate = mInflater.inflate(R.layout.device_album_viewpager_item_layout, container, false)
val imageView = inflate.findViewById<ImageView>(R.id.pv_image)
val tvLocation = inflate.findViewById<TextView>(R.id.tv_location)
val tvTime = inflate.findViewById<TextView>(R.id.tv_time)
tvLocation.text = if (TextUtils.isEmpty(bean.location)) "未知位置" else bean.location
tvTime.text = bean.alarmTime
Glide.with(mContext)
.load(bean.url)
.into(imageView)
container.addView(inflate)
return inflate
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(`object` as View)
}
override fun getItemPosition(`object`: Any): Int {
return PagerAdapter.POSITION_NONE
}
}