Kotlin server多线程编程详细讲解
change_fate 人气:0service 是什么
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()方法得到执行
加载全部内容