四大组件——服务(Service)
@author lisiwen
@createTime 2021/08/08
1.认识服务
服务(Service)是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
2.多线程编程
java多线程如果不太熟悉的话可以看看我之前的文章:----先熟悉一下java的多线程编程
2.1 使用多线程进行ui更新
和其他的gui库一样,android的ui也是现场不安全的,也就是说,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。
首先就先使用新线程来更新UI试一下,在页面新建一个button和text,点击button更新text内容,代码如下:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/change_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Change text" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hello World!" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
findViewById(R.id.change_text).setOnClickListener(this);
}
@Override
public void onClick(View view) {
new Thread(() -> {
textView.setText("Nice to meet you!");
}).start();
}
}
这样一个简单的功能就完成了,当你点击按钮的时候,app很正常的闪退了,并没有将text更新为"Nice to meet you!",看一下控制台的报错
E/AndroidRuntime: FATAL EXCEPTION: Thread-2
Process: com.example.servicetest, PID: 18306
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views
可以看出是子线程更新ui导致的程序崩溃,由此可以看出来子线程是不可以更新ui的,但是有时候有一些耗时的更新ui的操作那怎么办呢,在前台进行更新,用户体验就很差,这时候使用异步消息处理机制来处理,先用服务来实现更新ui,代码如下:
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView textView;
public static final int UPDATE_TEST = 1;
//定义一个服务
private Handler handler = new Handler() {
/**
* 重写handleMessage接收消息进行定制化处理
* @param msg
*/
@Override
public void handleMessage(@NonNull Message msg) {
// 对于消息类型进行判断
switch (msg.what) {
case UPDATE_TEST:
// 进行更新操作
textView.setText("Nice to meet you ");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text);
findViewById(R.id.change_text).setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.change_text:
new Thread(() -> {
// 创建一个Message对象 并将what字段赋值为更新类型,然后将该对象传给服务
Message msg = new Message();
msg.what = UPDATE_TEST;
handler.sendMessage(msg);
}).start();
break;
}
}
}
}
运行以上代码之后,再次点击按钮“Change text”就会正常的更新会将屏幕中的字符更新为Nice to meet you了,如下图:
![](https://img-blog.csdnimg.cn/60c6d8bc7780490cac6660795a9a46c4.jpg)
![](https://img-blog.csdnimg.cn/9e7af511840942ef84143d778716bdc7.jpg)
到这里就简单的使用了一下多线程编程了,接下来介绍一下android异步消息处理机制具体是怎么工作的
2.2 解析异步消息处理机制
Android的异步消息处理主要由四部分组成:Message,Handler,MessageQueue和Looper
-
Message
Message是在线程之间传递的消息,他内部可以携带少量的信息,用于在不同线程之间交换数据。之前就是在Message的what字段中携带了消息类型的信息,除此之外还有arg1和arg2字段可以携带额外的整形信息,使用obj携带对象类型信息。
-
Handler
Handler看起来就知道是处理消息的类,他主要是发送和处理消息的。发送消息一般是使用sendMessage()方法,通过内部一系列的处理,之后会走到handleMessage()方法中。
-
MessageQueue
MessageQueue是消息队列,用于存放所有通过Handler发送过来的消息,在队列中的消息等待着被Handler处理,每个线程只会有一个MessageQueue对象。
-
Looper
Looper是MessageQueue队列的管家,调用Looper的loop()方法后,就会进入到一个无线的循环当中,然后每当发现MessageQueue中存在一条消息,就会将他取出,并且传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。
了解了基本概念之后,再来讲一下异步消息的处理的流程。首先需要在主线程中创建一个Handler对象,并且需要重写handleMessage()方法。然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。折后这条消息就会添加到MessageQueue中,Looper会从MessageQueue取出消息并且交给Handler的handleMessage()方法进行处理。由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也是在主线程中运行的,所以可以安心的进行更新ui的操作了。
2.3 使用AsyncTask
不过为了更加方便我们在子线程中对UI进行操作,Android还提供了另外一些好用的工具,就比如说是AsyncTask。借助AsyncTask,技术对异步消息处理机制并不完全了解,也可以十分简单的从子线程切换到主线程。
3.服务的基本用法
了解了Android多线程编程的技术之后,接下来开始进入正题,开始正式接触服务
3.1 定义一个服务
首先新建一个服务,点击com.example.servicetest右键新建->new->Service->Service然后类名使用默认的类名MyService,这样就创建了一个服务,默认的服务重写了onBind()这是一个抽象的方法,是必须重写的。然后也是需要在AndroidManifest.xml进行注册,因为是使用的ide创建的,所以已经自动注册完了。看一下代码:
AndroidManifest.xml
...
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
...
这既然是一个服务,自然我们需要在服务处理一些事情,此时我们就需要重写服务中的另外一些方法了,可以重写onCreate(),onStartCommand(),onDestroy()这三个方法,onCreate是创建服务的时候调用,可以对一些服务使用的资源进行初始化,onStartCommand是启动服务的时候调用,可以执行一些启动服务就立刻执行的动作,onDestroy是销毁服务的时候调用,可以用来回首一些不再使用的资源。
3.2 启动和停止服务
启动服务和停止服务也是使用Intent来实现的,所以说四大组件和Intent息息相关,接下来来实现一下启动和停止服务。
在之前代码的基础上新加一个活动ServiceActivity并且在这个活动上新建两个按钮来启动和停止服务,在再MainActivity中新增一个按钮来启动ServiceActivity活动,在再MyService中的onCreate(),onStartCommand(),onDestroy()打印日志以方便观察服务的状态,代码如下:
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView