Service
Service属于Android的四大组件(只要是安卓四大组件就必须在AndroidMainFest中进行注册才可以使用),Service运行不赖于界面,即使退出应用,Service也将运行在后台中。服务在创建后,默认运行在主线程中,所以我们需要自己去开辟子线程,如果不开辟就有可能出现主线程被阻塞。
Android多线程
对于很多耗时操作(比如:发起网络请求,由于网络很慢,服务器不一定立刻响应),我们一般把操作放在子线程中进行。
线程基本用法
- 定义一个线程需要继承Thread类,重写run()方法:
class XXThread extends Thread {
@Override
public void run() {
// 处理具体的逻辑
}
}
启动该线程:
new XXThread().start();
- 使用Runable接口方法来定义一个线程
class XXThread implements Runnable{
@Override
public void run() {
}
}
启动该线程
XXThread mThread = new XXThread();
new Thread(mThread).start();
- 使用匿名类的方法
new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
}
}).start();
异步消息处理机制
我们是不能在子线程中更新UI中的内容,但是有时候我们却需要把子线程的内容更新到UI,Android提供一套异步消息处理机制来解决这个问题。
从图中可以发现,异步消息处理有4部分,分别是Message,Handle,MessageQuery,Looper四部分组成。在线程中我们调用 hanler.setMessage() 方法来发送消息给 MessageQuery,然后通过 Looper 来取出带处理消息,并且传回给 handler,然后调用在 UI 中的 handler.handleMessage() 方法来实现对 UI中的内容更新。
代码示例
TEXT_UPDATE
自己定义的一个常量
- UI线程中的代码
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case TEXT_UPDATE:
tvShow.setText("你改变了TextView的内容");
break;
default:
break;
}
}
};
- 子线程中的代码
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnUpdate:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = TEXT_UPDATE;
handler.sendMessage(message);
}
}).start();
break;
default:
break;
}
}
Service应用
前面讨论这么多关于多线程编程的问题都为在Service的使用上做准备的。先讨论最简单的基本用法
基本用法
Service和Activity一样都拥有 onCreate(),onDestroy()方法,来实现Service的创建和销毁。当然他们也有区别,在Service中我们必须实现 onBind() 方法,该方法用于与UI绑定(先不讨论)。在Service还有一个方法叫 onStartCommand(),这个方法和 onCreate() 区别是:onStartCommand()每次Service启动(包括第一次创建)都会启动这个方法,但是onCreate()只会在第一次创建的时候启动,之后不会再启动该方法
代码示例
- 创建一个Service
public class TestService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
- 启动一个Service
Intent startIntent = new Intent(this, TestService.class);
startService(startIntent);
- 停止一个Service
停止Service需要注意,如果Service不仅被创建了,而且被绑定了,那么如果要停止这个Service,就需要同时执行 stopService() 和 unbindService(),才可以停止Service。
Intent stopIntent = new Intent(this, TestService.class);
stopService(stopIntent);
将Service绑定到UI中
如果要将Service绑定到UI中去,必须在 onBind() 方法中传回一个对象,使得UI可以操作Service中的方法,这个对象就好比一座桥梁连接着UI和Service。
代码示例
- 创建一个Biner对象,来实现Service中的公共方法
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
Log.e(TAG, "开始下载");
}
public int getProgress() {
Log.e(TAG, "获取进度表");
return 0;
}
public void stopDownload() {
Log.e(TAG, "停止下载");
}
}
- 桥梁的建立
@Nullable
@Override
public IBinder onBind(Intent intent) {
//返回一个连接通道给service
return mBinder;
}
到此UI还没有和Service建立真正的桥梁,在UI中必须调用 bindService() 方法来绑定服务,在使用此方法之前
,我们还需要 ServiceConnection的实现,来建立UI与Ser的连接。ServiceConnection 中有两个方法需要实现分别是 onServiceConnected() 和 onServiceDisconnected(),前者在调用bindService() 来建立连接的时候,会去调用此方法,但是在解除绑定的时候,却不会调用onServiceDisconnected(),只要发生系统意外的时候,才会调用此方法。
我们在onServiceConnected()中来调用Service中的公共方法
- ServiceConnection的实现
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (TestService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
//系统发生意外中断的时候才会调用此方法
//客户端取消绑定的时候不会调用
}
};
到此我们还是不能调用Service中的方法,因为我们还没有使用onServiceConnected()来绑定。BIND_AUTO_CREATE 代表自动完成绑定。
- 桥梁的完成
Intent bindIntent = new Intent(this, TestService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
到此我们就可以任意调用Service中的公共方法了
- 桥梁的爆破
解除绑定是非常简单的,只需要调用 unbindService() 方法,需要注意的是:如果已经解除绑定了,再次调用会报错
unbindService(connection);
到这我们就已经学习了Service的创建、删除、绑定、取消绑定。但是这些都是在UI主线程中进行,就像我们前面提到如果遇到了耗时任务,将会被阻塞。所以我们要使用多线程技术。
IntendService的应用
IntendService的使用,可以使得开发者不必去停止服务,系统会在请求结束后自动停止服务。我们只需要在 onHandleIntent() 来完成UI所提出的任务即可,不过我们还需要为IntendService提供一个构造函数
代码示例
public class IntentServiceDemo extends IntentService {
private static final String TAG_INTENT_SERVICE = "IntentService";
public IntentServiceDemo() {
super("IntentServiceDemo");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.e(TAG_INTENT_SERVICE, "线程id是:" + Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG_INTENT_SERVICE,"服务已经被销毁");
}
}
启动IntendService
Log.e("MainActivity", "线程id:" + Thread.currentThread().getId());
Intent intentService = new Intent(this, IntentServiceDemo.class);
startService(intentService);
这样Service就运行在子线程中。当然我们也可以不使用IntendService来实现,我们可以自己在 onStartCommand() 方法中,开启一个子线程。但是这样开启一个子线程将永远在执行,所以我们必须在任务完成后,调用 stopSelf() 或者 stopService()方法来关闭。
代码示例
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG,"每次服务被创建");
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
前台Service
应用场景,如果一个天气app,当后台数据更新后,会一直在前台显示更新后的数据情况。这就可以使用前台Service来实现。要在前台一直显示数据,这有点类似于Notification,当时我们不使用NotificationManger来显示出来,而是使用 startForeground()来实现。要注意 startForeground() 方法中的第一个参数id,不能使用0作为id。
代码示例
@Override
public void onCreate() {
super.onCreate();
// Log.e(TAG, "服务被创建");
//前台服务
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
builder.setContentTitle("通知");
builder.setContentText("吃饭了么");
builder.setContentIntent(pendingIntent);
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setDefaults(Notification.DEFAULT_ALL);
builder.setAutoCancel(true);
startForeground(1, builder.build());
}
后台定时执行任务
通过AlarmManager(在这不多说)来实现定时功能,同样首先我们通过 getSystemService()方法获得系统服务。然后通过set() 方法来定时唤醒 PendingIntent.getBroadcast()* 方法来发送一个广播,然后再广播内启动Service。整个流程如下图
代码示例
- Service
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "执行时间:" + new Date().toString());
}
}).start();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int aSecond = 60 * 1000;//1分钟
long triggerAtTime = SystemClock.elapsedRealtime() + aSecond;
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, i, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
//Log.e(TAG,"每次服务被创建");
return super.onStartCommand(intent, flags, startId);
}
- Broadcoast
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("AlarmReceiver", "接收到了");
Intent i = new Intent(context, ServiceDemo.class);
context.startService(i);
}
}
最后还是感谢《第一行代码》的作者,郭神,文中很多都是来自于这本书。