Kotlin server多线程编程详细讲解
作者:change_fate
service 是什么
Service是实现程序后台运行的解决方案,适合执行非交互,后台预先的任务,即使用户打开其他应用,Service也能够正常运行
Service需要内部手动创建子线程
多线程编程
用法:
(1) 继承的方式(耦合较高,不推荐)
class MyThread : Thread() { override fun run () { // 编写具体逻辑 } } // 启动 MyThread().start()
(2) Runnable接口定义一个线程
class MyThread : Runnable { override fun run () { // 子线程具体逻辑 } } // 启动 val myThread = MyThread() Thread(myThread).start()
简化写法:如果你不想专门定义一个类去实现Runnable接口, 可以使用Lambda方式
Thread { // 编写具体逻辑 }.start()
更加简化的写法:
thread { // 编写具体的逻辑 }
在子线程中更新UI
Android 的UI 也是线程不安全的, 更新应用程序的UI元素, 必须在主线程中进行, 否则会出现异常
如果在子线程中直接更新UI,会出现崩溃,提示如下错误
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
那子线程如何更新UI呢?
通过异步消息传递给主线程, 在主线程更新UI 修改MainActivity.kt
class MainActivity : AppCompatActivity() { val sign = 1 val handler = object: Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { when (msg.what) { sign -> textView.text = "Nice to meet you 2" } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button.setOnClickListener{ thread { val msg = Message() msg.what = sign handler.sendMessage(msg) } } } }
定义一个Handler对象,重写handleMessage方法
如果Message(android.os.Message)的what字段等于sign,就将UI更新
异步消息处理机制
(1) Message 线程中传递少量消息,使用arg1和arg2携带整型数据, obj字段携带Obejct对象
(2) Handler,用于发送和接收消息
发送: 使用sendMessage() post() 方法
接受: 最终会传递到handleMessage方法中
(3) MessageQueue, 消息队列,存放Handler发送的消息,等待被处理,每个线程只会有一个MessageQueue
(4) Looper, 是每个线程中MessageQueue的管家, 调用Looper的 loop()
方法后,会进入无限循环中,每当发现MessageQueue中存在一条消息,就取出,并传递到Handler的handleMessage方法中,每个线程用一个Looper对象
异步消息处理流程:
1 主线程创建handler对象, 重写handleMessage方法
2 当子线程中需要进行UI操作,就创建一个Message对象,通过Handler 的sandMessage方法将消息发送出去,消息被添加到MessageQueue中等待
3 Looper一直尝试从MessageQueue中取消息,最后分发给Handler的handlerMessage方法中,由于Handler函数中传入了Looper.getMainLooper(), 此时handleMessage() 方法中的代码会在主线程中运行
4 使用AsyncTask
为了方便子线程对UI操作, Android提供了一些好用的工具如AsyncTask,原来也是基于异步消息处理
(1)基本用法:
AsyncTask是一个抽象类,如果想使用它,需要一个子类继承,可以在继承时指定3个泛型参数: params: 可在后台任务中使用 progress :在后台任务执行时, 如果需要在界面上显示的进度,使用泛型作为进度单位 Result 任务执行完后, 对结果进行返回, 返回泛型类型
最简单的形式:
class DownloadTask :AsyncTask<Unit, Int, Boolean> () { }
当前是一个空任务,无任何实际操作,需要重写4个方法:
1 onPreExecute() 在任务执行前调用,用于初始化操作
2 doInBackground(Params…) 在子线程中执行, 执行具体耗时任务
3 onProgressUpdate(Progress…) 后台任务调用,进行UI操作
4 onPostExecute(Result) 后台任务执行完毕并通过return返回时, 收尾工作
Service 基本用法
1.定义一个Service
新建一个ServiceTest项目
右击 com.example.servicetest -> New -> Service -> Service
类名改成MyService, Exported表示将Service暴露给外部访问
Enable表示启用这个Service
生成如下代码:
class MyService : Service() { override fun onBind(intent: Intent): IBinder { TODO("Return the communication channel to the service.") } }
MyService 继承自Service类, 有一个onBind方法,是Service唯一抽象方法,需要在子类实现
重写一些方法:
- onCreate() service创建调用
- onStartCommand() 每次service启动调用
- onDestory() 销毁调用
ps: Service需要在AndroidManifest.xml文件中注册(在创建service中会自动注册)
启动和停止Service
要借助Intent实现,在ServiceTest中启动停止MyService
添加两个按钮:
startServiceBtn.setOnClickListener { val intent = Intent(this, MyService::class.java) startService(intent) // 启动Service } stopServiceBtn.setOnClickListener { val intent = Intent(this, MyService::class.java) stopService(intent) // 停止Service }
startService
和stopService
都定义在Context类中,可以直接调用
另外可以自我停止:
在service内部调用stopSelf()
方法
启动后可以在: 应用 -》显示系统应用 中找到
Android8.0后,应用在前台,service才能稳定运行,否则随时可能被系统回收
Activity 与 Service通信: onBind 方法
查看下载进度:,创建一个专门的Binder对象管理下载功能:
private val mBinder = DownloadBinder() class DownloadBinder : Binder() { fun startDownload() { Log.d("MyService", "startDownload executed") } fun getProgress(): Int{ Log.d("MyService", "getProgress executed") return 0 } } override fun onBind(intent: Intent): IBinder { return mBinder }
当一个Activity 和Service 绑定了之后,就可以调用该Service 里的Binder提供的方法了。
在activity中,修改:
lateinit var downloadBinder: MyService.DownloadBinder private val connection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName, service: IBinder) { downloadBinder = service as MyService.DownloadBinder downloadBinder.startDownload() downloadBinder.getProgress() } override fun onServiceDisconnected(name: ComponentName) { } } ... // 绑定service bindServiceBtn.setOnClickListener { val intent = Intent(this, MyService::class.java) bindService(intent, connection, Context.BIND_AUTO_CREATE) // 绑定Service } // 解绑service unbindServiceBtn.setOnClickListener { unbindService(connection) // 解绑Service }
bindService()方法接收3个参数
第一个是Intent对象
第二个是ServiceConnection的实例
第三个是一个标志位 BIND_AUTO_CREATE 表示在Activity 和Service 进行绑定后
自动创建Service
这会使得MyService 中的onCreate()方法得到执行
到此这篇关于Kotlin server多线程编程详细讲解的文章就介绍到这了,更多相关Kotlin server内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!