美文网首页
《第一行代码》第十章 后台默默的劳动者 探究Service

《第一行代码》第十章 后台默默的劳动者 探究Service

作者: 番茄tomato | 来源:发表于2020-04-28 15:03 被阅读0次
    • 本篇参考资料《第一行代码 第三版》 2020.4月出版
    • 本篇文章只是本人看书的理解和整理的笔记,更完整的内容还在书上
    • 尊重原作者 请购买正版图书

    一开始就知道有四大组件,结果学习Android开发这么久,一直还只用到了Activity,这里需要进行恶补,从Service开始

    四大组件之一 Service

    Service是Android中实现程序后台运行的解决方案,它非常适合执行那些不需要和用户交互而且还要求长期运行的任务。Service的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,Service仍然能够保持正常运行。

    另外,不要被Service的后台概念所迷惑,实际上Service并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在Service的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况。

    一. Android多线程编程

    当我们需要执行一些耗时操作,比如文件的读写,网络请求等等,为了避免主线程被阻塞影响用户使用,一般会放到子线程中执行,
    这里推荐学习框架Rxjava https://www.jianshu.com/p/b002d8ea2648
    郭神在书上为了方便理解只讲了最简单的多线程实现方式,以下为书中内容笔记

    1.1 线程的基本用法

    比如我们想打印1~10 每次打印间隔1秒
    方式一:使用继承Thread方式:

    fun main(){
        val myThread=MyThread()
        myThread.start()
    }
    class MyThread:Thread(){
        override fun run() {
            super.run()
            for(i in 0..10){
                println(i)
                sleep(1000)
            }
        }
    }
    

    方式二:使用实现接口Runnable方式:

    fun main() {
        val myThread= MyThread()
        Thread(myThread).start()
    }
    
    class MyThread : Runnable {
        override fun run() {
            for (i in 0..10) {
                println(i)
                sleep(1000)
            }
        }
    }
    

    可以看到这里是Thread的构造函数接收了一个Runnable参数,创建了一个Thread对象,再调用start方法,就开始了子线程
    方式三:使用Lambda表达式 (不需要再专门定义一个类)

    fun main() {
        Thread{
            for (i in 0..10) {
                println(i)
                sleep(1000)
            }
        }.start()
    }
    

    以上三种再java中也是同样的用法,但是接下来,Kotlin为我们提供了一种更简单的开启子线程的方式
    方式四:使用thread函数

    fun main() {
        thread{
            for (i in 0..10) {
                println(i)
                sleep(1000)
            }
        }
    }
    

    可以看到连start也不需要调用了,thread函数内部全部帮我们处理好了

    1.2 尝试子线程中更新UI

    Android的UI是线程不安全的,也就是说,如果想要更新应用程序里的UI元素,必须在主线程中进行,否则就会出现异常。对于这种情况,Android提供了一套异步消息处理机制,完美地解决了在子线程中进行UI操作的问题。

    接下来我们试试在子线程中更新UI,点击按钮,启动一个子线程,在子线程中设置TextView的内容:
    布局:

        <Button
            android:id="@+id/changeTextBtn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Change Text"
            />
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="25sp"
            android:text="Hello World!"
       />
    

    Activity内

    import kotlinx.android.synthetic.main.activity_main.*
    class MainActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            changeTextBtn.setOnClickListener {
                thread {
                    textView.text = "Nice to meet you !"
                }
            }
        }
    }
    

    注意!!:这里没有使用findViewById(),这是Kotlin一个非常优秀的设计,可以直接
    import kotlinx.android.synthetic.main.activity_main.*
    导入这个界面对应的布局layout文件,View的id就可以作为对象直接操作

    当然你非要用findViewById()也可也

    private lateinit var button:Button
    ...
    button = findViewById(R.id.button)
    

    使用 Kotlin Android Extensions 直接生成对应的 View 作为属性。不需要 findViewById,不需要定义变量,直接使用。使用时需要注意访问的 View 属于哪个 Layout,因为智能提示的候选项会提供所有布局中的 View 供你选择,然后帮你 import 对应包以便你访问这个 View;假如 import 的多个同一层级的 layout 中具有相同的 id,则这个 id 对应的 View 将无法访问。

    接下来我们运行这个app,并点击按钮,可以看到程序崩溃了,出现了一条报错信息:
    Only the original thread that created a view hierarchy can touch its views.
    这就证明Android不可以在子线程中操作UI,那么对于这种情况我们,Android提供了一套异步消息处理机制,完美的解决了在子线程中修改UI的问题

    1.3 Android 异步消息处理机制

    使用handler接收异步处理的信息,可以在handler中修改ui,将上边的代码改成如下:

    class MainActivity : AppCompatActivity() {
       //msg识别码
       val upDataText=10011
       val handler=object:Handler(){
           //接收信息
           override fun handleMessage(msg: Message) {
    
               super.handleMessage(msg)
               //判断信息识别码 根据不同的识别码进行不同动作
               when(msg.what){
                   //修改ui
                   upDataText-> {
                       //读取参数对象 并使用as关键字转强制换为Student1类型
                       //msg.obj可能为null 注意使用?进行非空判断
                       var result:Student1?=msg.obj as? Student1
                       //在ui中显示
                       //如果是空的 则显示null
                       textView.text=result?.sex?:"null"
                   }
               }
           }
       }
    
       override fun onCreate(savedInstanceState: Bundle?) {
           super.onCreate(savedInstanceState)
           setContentView(R.layout.activity_main)
           changeTextBtn.setOnClickListener {
               thread {
                   val msg=Message()
                   //信息识别码
                   msg.what=upDataText
                   //msg传递一个参数 对象
                   msg.obj=Student1("girl")
                   //发出信息
                   handler.sendMessage(msg)
               }
           }
       }
    }
    
    

    在以上的代码中,我们将异步线程处理结果,封装成一个message传递到handler。message主要属性第一个是msg.what是msg的识别码,用户handler判断不同信息并采取不同动作。msg.obj可以传递一些对象参数,msg.arg1可以传递一些整型参数。在handler中,使用as关键字将msg.obj强制转换为具体的类型,这样就可以进行操作了。(注意使用?非空判断)

    1.4 使用AsyncTask来进行异步操作

    https://www.jianshu.com/p/9724355bf01b

    二. Service的使用

    相关文章

      网友评论

          本文标题:《第一行代码》第十章 后台默默的劳动者 探究Service

          本文链接:https://www.haomeiwen.com/subject/heexwhtx.html