[TOC]
1. 服务是什么?
服务是一种应用程序组件,当应用程序希望在不与用户交互的情况下执行长时间运行的操作,或者为其他应用程序提供的功能。每个服务类必须在其包的AndroidManifest.xml中具有相应的声明。
特别需要注意的是,服务也是运行在主线程当中的,当你需要运行耗时任务的时候需要另外开辟子线程,否则会造成界面的卡死.
需要知道的是,服务本身并不是一个线程或进程,它是依赖于创建服务时所在的应用程序进程,当应用程序停止这个服务也会终止运行.
2. 异步处理:
2.1 异步消息的处理机制:
Android中的异步消息处理机制主要由下面四部分组成.
每当创建一个Handler实例的时候,它将自动绑定到创建该实例的线程上.所以当Handler在主线程中创建,里面的handlerMessage()方法执行的操作也是在主线程当中进行的.
功能 | |
---|---|
Message | 可以携带少量信息,用于在不同的线程之间交互数据.可以使用what字段或arg1,arg2,obj字段携带信息. |
Handler | 用于发送和处理Message,发送使用SendMessage(),接收则使用handleMessage(). |
MessageQueue | 消息队列,主要存放所有通过Handler发送的消息,.每个线程中只会有一个MessageQueue对象 |
Looper | 是MessageQueue的管家,调用Looper的loop()方法以后就会进入无限循环,每当发现消息队列中存在消息的时候,就会把消息取出,并传递到Handler的handlerMessage()方法中.和上面一样每个线程中只有一个Looper对象. |
一条Message经过这样的一个流程以后,也就由子线程进入了主线程,也就可以对UI进行相对应的修改了.整个异步消息处理的核心也就是如此
2.2 使用AsyncTask:
AsyncTask也是基于Handler封装而来的.由于AsyncTask是一个抽象类,因此想要使用AsyncTask就必须创建一个继承自AsyncTask的类,并指定泛型和重写以下四个方法.
用途 | |
---|---|
onPreExecute()gai | 该方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作 |
doInBackground(Parans ...) | 这个方法中的所有代码都会在子线程中进行操作,任务一旦完成就可以通过return语句来将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定为Void,就可以不返回任务执行结果.(如果想要更新任务进度的话,可以调用publishProgress(Progress ...)方法来完成) |
onProgressUpdate(Progress ...) | 当后台任务调用了publishProgress(Progress ...),当前方法就会被调用,该方法中的参数就是在后台任务中传递过来的.在这个方法中可以对UI进行操作.利用传进来的参数就可以对界面进行更新. |
onPostExecute(Result) | 当后台任务完成并通过return返回的时候,当前方法就被调用,返回的数据会作为参数传递到当前方法中,可以利用返回的数据来进行一些UI操作,比如提醒任务完成等... |
实践
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mySynnc mySyn = new mySynnc(); //异步任务的初始化
mySyn.execute(); //当实现异步任务类的第一个参数指定不为空的时候,需要把要提交的参数放在.execute()中
}
//通过代码实践可以直观的看出整个异步任务的执行流程
class mySynnc extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d(TAG, "onPreExecute: 初始化");
}
@Override
protected Boolean doInBackground(Void... voids) {
Log.d(TAG, "doInBackground: 后台");
publishProgress(123);
return null;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
Log.d(TAG, "onPostExecute: 任务完成,进入收尾阶段");
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Log.d(TAG, "onProgressUpdate: 更新");
}
}
}
复制代码
3. 服务的基本用法:
通过androidstudio右键新建一个继承自Service的类,并在内部实现相对应的方法,因为服务是四大组件之一所以必须在ManiFest.xml中进行注册,否则无法使用(没反应).
3.1 定义一个服务:
public class MyService extends Service {
private static final String TAG = "MyService";
public MyService() {
Log.d(TAG, "MyService: ");
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
// TODO: Return the communication channel to the service.
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
}
复制代码
定义一个服务必须重写 onBind(..) 方法,因为这是一个抽象方法,在实现类中必须被实例.
onCreate()方法在服务被创建时会被调用.
onStartCommond(...) 方法在每次服务启动的时候调用.
onDestroy() 方法 当活动停止这个服务或者活动被销毁的时候,该方法被调用.
服务内部也可以自己调用stopSelf()来停止该服务.
3.2 启动和停止一个服务:
protected void onCreate(Bundle savedInstanceState) {
//启动服务
Intent intent=new Intent(this,MyService.class);
startService(intent);
//停止服务
Intent intent2=new Intent(this,MyService.class);
stopService(intent);
}
复制代码
通常这样启用服务十分的不方便,当我们启动服务以后服务就一直在后台处于运行状态,除非执行完毕,否则我们就不知道服务在前在做什么,做到哪里了? 所以我们就需要服务和活动进行通讯.
3.3 服务和活动进行通讯:
在服务中创建一个Binder实例类,并在Binder类中提供相对应的方法给别人调用.
服务会通过public IBinder onBind(Intent intent) ;方法将Binder对象返回给活动.
服务:
public class MyService extends Service {
private static final String TAG = "MyService";
private Abinder mAbinder = new Abinder();
public MyService() {
Log.d(TAG, "MyService: ");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mAbinder;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
class Abinder extends Binder {
public void start() {
Log.d(TAG, "start: ");
}
public void pause() {
Log.d(TAG, "pause: ");
}
public void resume() {
Log.d(TAG, "resume: ");
}
public void restart() {
Log.d(TAG, "restart: ");
}
public void destory() {
Log.d(TAG, "destory: ");
}
}
}
复制代码
活动:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "调试信息:";
private Button btn1, btn2, btn3, btn4, btn5;
private MyService.Abinder mAbinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
Log.d(TAG, "onCreate: 当前活动线程:" + Thread.currentThread().getId());
}
private void initView() {
...
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
mAbinder = (MyService.Abinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
}
};
......
}
复制代码
通过上面完整的代码案列可以知道,在当前活动中通过于服务进行绑定可以获得一个service的内部对象,这个对象可以对service进行一定的控制和获取状态.
活动中创建了一个ServiceConnection的匿名内部类,
**里面分别重写了 onServiceConnected(); onServiceDisconnected(); 这两个方法,当活动和服务进行绑定或解绑时他们会被调用. **
onServiceConnected(); 中 通过向下转型得到service的binder的实例,然后我们就可以通过这个实例对服务进行操作控制.
其中活动与服务的绑定方法接受三个参数 bindService(bindIntent,connection,BIND_AUTO_CREATE);
第一个参数是intent对象,指明我们的意图;
第二个参数是ServiceConnection的实例,它会在绑定成功或失败时回调这个对象中的方法;
第三个参数是BIND_AUTO_CREATE 这个当前Context提供的一个常量, 表示活动和服务进行绑定以后自动创建服务,所以服务中的onCreate()方法可以执行,但onStartCommand()方法不会执行.
注意点:当前程序中任何一个服务都可以与多个活动进行绑定,而且绑定后获取到的Binder实例也是相同的.
4. 服务的生命周期:
**Context中的strartService(): **
onCreate()->onStartCommand()->onDestory()
当这个服务第一次被创建的时候才会调用onCreate();
因为服务只有一个实例,所以不管调用多少次onStartCommand(),只需要调用stopService()就可以将服务销毁
**Context中的bindService(): **
onCreate->onBinder()->onDestroy()
通过绑定来获取一个服务的持久化连接,这是会挥动服务中的onBind(),如果之前这个服务没有创建过,会先调用onCreate()方法. 之后就可以通过返回的Binder对象与服务进行通信. 调用unbindService()方法后 服务将会被销毁.
当服务既调用了startService()又调用了bindService()时,因为Android系统的机制,一个服务只要被启动或绑定就一直处于运行状态,只有两种情况同时不满足这个服务才可以被销毁,所以要同时调用stopService()和unbindService() 这个服务才可以调用onDestroy()
5. 更高级的服务:
因为服务几乎都是在后台运行的,并且服务的优先级还是比较低的,当系统出现内存不足的情况,就有可能回收掉正在后台运行的服务.如果不想因为内存不足导致服务被回收,就可以考虑使用前台服务.前台服务和普通的服务的最大区别就在于,他会有一个正在运行的图标在系统的状态了显示,类似通知.
5.1 前台服务:
只需要在Service的onCreate()方法中添加如下代码
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this).setContentIntent
(pendingIntent).setContentTitle("title").setContentText("text").setWhen(System
.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher).setLargeIcon
(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)).build();
startForeground(1, notification);
}
复制代码
首先先创建一个通知,但和往常显示通过的方式不一样,它是通过调用startForeground()方法来显示;
这个方法接收两个参数:第一个参数时通知的Id;第二个参数则是构建出的Notification对象,调用这个方法以后就会将这个服务变成一个前台服务.
5.2 IntentService:
如果在服务中执行一些耗时的操作,可能需要在onStartCommand中开启子线程. 但是因为服务一直处于运行状态,想要让服务停止下来的话需要调用stopSelf() 或者stopService(); 但是经常会忘记开启线程或者调用停止服务的方法,所以Android 专门提供了一个IntentService类,这个类就解决了这两个问题.
用法:
主要是创建一个继承自IntentService的类,并实现相应的方法;
方法 | 说明 |
---|---|
构造函数(); | 启动IntentService时调用,并赋值一个字符串给父类用于调试式判断工作线程 |
onHandleIntent(); | 这个方法中的代码将会在子线程中执行 |
onDestroy(); | 只要上面的方法执行完毕,就会调用这个方法停止当前服务. |
public class MyIntentService_ extends IntentService {
private static final String TAG = "调试信息:";
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService_() {
super("name");
//name主要时用于调试时 方便知道工作在哪个线程
Log.d(TAG, "MyIntentService_: ");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.d(TAG, "onHandleIntent: 服务中的线程:"+Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
}
复制代码
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
Log.d(TAG, "onCreate: 当前活动线程:" + Thread.currentThread().getId());
Intent intent=new Intent(this,MyIntentService_.class);
startService(intent);
}
复制代码
通过调试信息可以看清楚IntentService的运行流程. 这样就十分方便了,后台的代码都在子线程中完成,并且执行完毕之后会自动停止服务.