Android 服务(Service)

一、服务的解释

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

注意:服务并不是运行在一个独立的进程当中,而是依赖于创建服务时所在的应用程序进程。当某个应用程序被杀死时,所有依赖于该进程的服务也会停止运行。

服务并不会自动开启线程,所有的代码都是默认运行在主线程中的。我们在服务的内部手动创建了子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。

二、Android多线程编程

1、线程的基本用法

这部分与Java相同,线程创建的两种方式。

2、在子线程中更新UI

与其他许多的GUI库一样,Android的UI也是不安全的。也就是说,如果想要更新应用程序的UI元素,则必须在主线程中进行,否则就会出现异常。

实践:新建AndroidThreadTest项目:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zhj.androidthreadtest.MainActivity">

    <Button
        android:id="@+id/change_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Change Text"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Hello world"
        android:textSize="20sp"/>
</RelativeLayout>

一个按钮,一个TextView,显示出Hello world,目标效果:当我们点击按钮时,会把Hello world 改变成 Nice to meet you。

我们开始写MainActivity.java

package com.zhj.androidthreadtest;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView text;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text =(TextView)findViewById(R.id.text);
        Button changeText = (Button)findViewById(R.id.change_text);
        changeText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                switch(v.getId()){
                    case R.id.change_text:
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                text.setText("Nice to meet you");
                            }
                        }).start();
                        break;
                    default:break;
                }
            }
        });
     }
}

逻辑非常简单。我们在 按钮的点击事件中开启了一个子线程,然后在子线程中调用TextView的setText()方法将显示的字符串改成Nice to meet you。我们运行之后,点击按钮按钮发现程序崩溃了。

查看logcat日志:可以看出由于在子线程中更新UI造成的。


由此证实,An'd'roid确实不允许在子线程中进行UI操作的。但是有些时候我们必须在子线程中去执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件。

对于这种情况,Android 提供了一套异步消息处理机制,完美解决了在子线程中进行UI操作的问题。我们先来了解一下异步消息处理的使用方法,

修改MainActivity的代码:

package com.zhj.androidthreadtest;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    
    public static final int UPDATE_TEXT = 1; //表示更新TextView这个动作
    
    private TextView text;
    
    private Handler handler = new Handler(){//新增Handler对象,并重写handleMessage()方法,对具体的Message进行处理,
        public void handleMessage(Message msg){//如果发现Message的what字段的值等于UPDATA_TEXT,就将TextView显示的内容改成Nice to meet you
            switch (msg.what){
                case UPDATE_TEXT ://在这里进行UI操作
                    text.setText("Nice to meet you");
                    break;
                default:break;
            }
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text =(TextView)findViewById(R.id.text);
        Button changeText = (Button)findViewById(R.id.change_text);
        changeText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                switch(v.getId()){
                    case R.id.change_text:
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                              Message message = new Message();//创建一个Message(android.os.Message)对象,并将他的what字段设置为UPDATE_TEXT
                              message.what = UPDATE_TEXT;
                              handler.sendMessage(message);//将Message对象发送出去
                            }
                        }).start();
                        break;
                    default:break;
                }
            }
        });
     }
}

来观察按钮的点击事件中的代码,我们并没有在子线程里直接进行UI操作,而是创建了一个Message(android.os.Message)对象,并将它的what字段指定为UPDATE_TEXT,然后调用Handler的sendMessage()方法将这条Message发送出去。Handler会收到这条Message,并在handlerMessage()方法中对它进行处理,注意此时的handlerMessage()方法中的代码就是在主线程中运行的了,所以我们放心在这里进行UI操作。

运行之后,我们会发现内容被替换成Nice to meet you 。

3、解析异步消息处理机制

Android中的异步消息处理主要分为4个部分:Message、Handler、MessageQueue和Looper。

    (1)Message:Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据,可以使用Message的what字段,还可以使用arg1和arg2字段来携带一些整型数据,使用obj字段携带一个Object对象。

    (2)Handler:处理者的意思,主要用于发送和处理消息的,发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage()方法中。

    (3)MessageQueue:消息队列,主要用于存放所有通过Handler发送的消息。这部分消息会一直存放在消息队列中,等待被处理。每个线程中只有一个MessageQueue对象。

    (4)Looper:每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handlerMessage()方法中。每个线程中也只有要给Looper对象。

我们把异步消息处理的整个流程再梳理一遍。首先需要在主线程中创建一个Handler对象,并重写handlerMessage()方法,这个方法里写你的逻辑。当主线程中需要进行UI操作时,就创建一个Message对象,并通过Handler把这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handlerMessage()方法中。由于Handler是在主线程中创建的,所以此时handlerMessage()方法中的代码也会在主线程中运行,于是我们在这里可以进行UI操作了。


一条Message经过这样一个流程的辗转调用后,也就从子线程进入到了主线程,从不能更新UI变成了更新UI,整个异步消息处理的核心思想就是如下。

4、使用AsyncTask


猜你喜欢

转载自blog.csdn.net/weixin_39241397/article/details/79912073