服务是什么
服务(Service)是Android中实现程序后台运行的解决方案。它非常适合去执行一些不需要和用户交互而且还要求长期运行的任务。
服务并不是运行在独立的进程中,而是依赖创建服务时所在的应用程序进程
服务并不会自动开启线程,所有的代码都是默认运行在主线程中。
正常情况下,我我们都需要手动创建子线程,并在子线程中执行具体的任务,否则就有可能出现主线程被阻塞住的情况。
线程的开启方法
- 继承Thread
新建一个类继承Thread,然后重写父类的run()方法,在run()方法里面写耗时逻辑。
public class MyThread extends Thread {
@Override
public void run() {
// 处理具体的逻辑
}
}
然后new出一个MyThread实例,调用它的start()方法,这样run()方法中的代码就会在子线程中运行。
new MyThread().start();
- 实现Runnable接口
继承的耦合性太高,我们可以通过实现Runnable接口的方式来定义一个线程。
public class MyThread implements Runnable {
@Override
public void run() {
// 处理具体的逻辑
}
}
启动线程的方式如下:
MyThread myThread = new MyThread();
new Thread(myThread).start();
- 匿名类
如果不想去专门再定义一个类去实现Runnable接口,那么也可以使用匿名类的形式,这种方式更加常见。
new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
}
}).start();
在子线程中跟新UI
如果想更新应用程序的UI必须在主线程中进行,否则出现异常
对于在子线程更新UI,Android提供了一套异步消息处理机制。
异步消息处理机制:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final int UPDATE_TEXT = 1; // 定义个整型常量,用于表示Handler中某个动作
private TextView mText;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
//在这里进行UI操作
mText.setText("Nice to meet you");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView) findViewById(R.id.text);
Button changeText = (Button) findViewById(R.id.change_text);
changeText.setOnClickListener(this);
}
@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.what = UPDATE_TEXT;
mHandler.sendMessage(message); //将Message对象发出去
}
}).start();
break;
default:
break;
}
}
}
解析异步消息处理机制
Android的异步消息处理机制由四部分组成:Message,Handler,MessageQueue,Looper。
1.Message
Message是线程中传递的消息。
2.Handler
处理者,用于发送和处理消息。
一般消息是Handler的sendMessage()方法把消息传出到Handler的handleMessage()。
3.MessageQueue
存放所有通过Handler发送的消息
MessageQueue是消息队列的意思,存放所有通过Handler发送的消息,这部分消息会一直存放在消息队列里面,等待处理。每个线程只有一个MessageQueue。
4.Looper
Looper是MessageQueue的管家,调用Looper的loop()后会进行一个无线循环,每当发现MessageQueue中有新的消息就会把他取出,并传递到Handler的handleMessage()方法中,每个线程只有一个Looper。
使用AsyncTack
AsyncTask是Android帮我们封装好的对异步消息处理的工具,背后实现的原理也是异步消息处理机制。
AsyncTask是一个抽象类,需要创建一个类去继承它。在继承时可以为AsyncTask类指定3个泛型参数:
第一个参数Params。可用于在后台任务中使用
第二个参数Progress。后台任务执行时,如果需要在界面上显示当前进度,则使用这里指定的泛型作为进度单位
第三个参数Result。当任务执行完毕,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型
class DownloadTask extends AsyncTask <Void,Integer,Boolean>{
/**
* 在后台任务开始之前调用
* 用于一些界面的初始化操作,例如显示一个进度条对话框
* 主线程中运行
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
//显示进度对话框
progressDialog.show();
}
/**
*在这里处理所有的耗时操作
* 如果需要更新UI元素,例如反馈当前任务的执行进度,可以调用publishProgress(Progress...)
* 如果任务完成可以通过return语句来将任务执行的结果返回,返回类型是AsyncTask中的第三参数
* 如果AsyncTask的第三个参数是Void,那么就可以不返回任务执行的结果
* 子线程中运行
* @param voids
* @return
*/
@Override
protected Boolean doInBackground(Void... voids) {
try {
while (true) {
int downloadPercent = doDownload(); //这是一个虚构的方法
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
/**
* 当doInBackground()中调用了publishProgress(Progress...),则此方法会很快被调用
* 该方法中携带的参数就是在后台任务中传递多来的
* 可以对UI进行操作,利用参数中的值可以对界面上的元素进行更新
* 主线程中运行
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
//在这里更新下载进度
progressDialog.setMessage();
}
/**
* 当doInBackground()中调用了return语句时,这个方法会很快被调用
* 返回的数据作为参数传递到此方法中
* 可以利用返回的数据来进行一些UI操作,比如提醒任务执行的结果或关闭掉进度条对话框等
* 主线程中运行
* @param aBoolean
*/
@Override
protected void onPostExecute(Boolean aBoolean) {
//关闭对话框
progressDialog.dismiss();
if (aBoolean) {
Toast.makeText(MainActivity.this, "Download succeeded", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Download failed", Toast.LENGTH_SHORT).show();
}
}
}
服务的基本用法
定义一个基本服务
可以通过Android Studio的快捷键来创建:
public class MyService extends Service {
public MyService() {
}
/**
* 在服务创建的时候调用
*/
@Override
public void onCreate() {
super.onCreate();
}
/**
* 在每次服务启动的时候调用
* @param intent
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* 在服务销毁的时候调用,一般用于回收那些不再使用的资源
*/
@Override
public void onDestroy() {
super.onDestroy();
}
}
启动和停止服务
启动和停止Service的方法都是借助Intent来实现的。
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent); // 启动服务
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent); // 停止服务
break;
}
}
onCreate()与onStartCommand()的区别:
onCreate()只在服务第一次创建的时候使用
onStartCommand()是在服务每次启动的时候都会调
活动和服务的通信
onbind让活动控制服务
public class Myservice extends Service
{
class DownloadBinder extends Binder
{
public void startDownload(){
Log.d("Myservice",:startDownload executed);
}
public int getpress(){
Log.d("Myservice","startDownload executed);
return 0;
}
}
private DownloadBinder mBinder=new DownloadBinder();
pubic IBinder onBind(Intent intent)
{
return mBinder;
}
}
在活动中解除和绑定活动
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.DownloadBinder mDownloadBinder;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mDownloadBinder = (MyService.DownloadBinder) service;
mDownloadBinder.startDownload();
mDownloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bindService = (Button) findViewById(R.id.bind_service);
Button unbindService = (Button) findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind_service:
Intent bindIntent = new Intent(this, MyService.class);
//BIND_AUTO_CREATE 表示在活动和服务进行绑定后自动创建服务
bindService(bindIntent, mConnection, BIND_AUTO_CREATE); // 绑定服务
break;
case R.id.unbind_service:
Intent unbindIntent = new Intent(this, MyService.class);
unbindService(mConnection); // 解绑服务
break;
default:
break;
}
}
}
服务的生命周期
服务的生命周期主要有以下两种路径:
启动服务
该服务在其他组件调用startService()时创建,并回调onStartCommand()方法。如果这个服务之前没有创建过,onCreate()方法会先于onStartCommand()方法执行。直到调用stopService()或stopSelf()方法服务才会停止。注意:虽然每调用一次startService()方法onStartCommand()都会执行一次,但实际上每个服务都只会存在一个实例。
绑定服务
该服务在其他组件调用bindService()时创建,并回调服务中的onBind()方法。如果服务之前没有创建过,onCreate()方法会优于onBind()方法执行。客户端获取到onBind()方法里面返回的IBinder对象的实例,就能与服务端进行通信了。
当我们对服务同时进行了startService()和bindService()时,这时候我们需要同时调用stopService()和unbindService()方法,服务才会被销毁,onDestory()方法才会执行。
Service两种情况下生命周期图如下:
服务的使用技巧
使用前台服务
服务的优先级比较低,容易被回收,如果希望服务一直运行,不会因内存不足被系统回收就要使用前台服务。前台服务和普通服务最大的区别就是,他会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息。
创建前台服务修改Myservice的代码
public void onCreate() {
super.onCreate();
Log.d("MyService","onCreate executed");
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(MyService.this,"default")
.setContentTitle("This is content title")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.small_icon)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.large_icon)).build();
startForeground(1,notification);
}
只需要修改onCreate()的代码,构建notification对象,并使用startForeground()方法(使用会让后台服务变成前台服务)显示出来。
使用intentService
在本章一开始的时候我们就已经知道,服务中的代码都是默认运行在主线程当中的,如果直接在服务里去处理一些耗时的逻辑,就很容易出现ANR(Application Not Responding)的情况。
所以,这个时候就需要用到Android多线程编程的技术,我们应该在服务的每个具体的方法里开启一个子线程,然后在这里去处理那些耗时的逻辑。因此,一个比较标准的服务就可以写成如下形式:
public class MyService extends Service {
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//处理具体逻辑
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
...
}
但是,这种服务一旦启动之后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来。所以,如果想要实现让一个服务在执行完毕后自动停止的功能,就可以这样写:
public class MyService extends Service {
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//处理具体逻辑
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
...
}
虽说这种写法并不复杂,但是总会有一些程序员忘记开启线程,或者忘记调用stopSelf()方法。为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类,这个类就很好地解决了前面所提到的两种尴尬。