一、服务的解释
服务(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"><Buttonandroid:id="@+id/change_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Change Text"/><TextViewandroid: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;@Overrideprotected 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() {@Overridepublic void onClick(View v) {switch(v.getId()){case R.id.change_text:new Thread(new Runnable() {@Overridepublic 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 youswitch (msg.what){case UPDATE_TEXT ://在这里进行UI操作text.setText("Nice to meet you");break;default:break;}}};@Overrideprotected 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() {@Overridepublic void onClick(View v) {switch(v.getId()){case R.id.change_text:new Thread(new Runnable() {@Overridepublic void run() {Message message = new Message();//创建一个Message(android.os.Message)对象,并将他的what字段设置为UPDATE_TEXTmessage.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