Android四大组件之Service

来源:互联网 发布:淘宝同行恶意投诉售假 编辑:程序博客网 时间:2024/06/05 13:53

1、Service概述

1.1、service简介

Service是一种长生命周期的组件,没有界面,由其他组件开启,运行于当前界面的进程中用于去执行那些不需要和用户交互而且还要求长期运行的任务。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。 

Service和其他组件一样,都是运行在主线程中,因此不能用来做耗时的操作。

2、Service的基本用法

2.1、启动和停止服务

1)启动服务

采用Context对象的startService()方法可以启动一个服务。

2)停止服务

采用Context对象的stopService()方法可以停止一个服务。并且可以在自定义的Service中任意位置调用stopSelf()方法停止服务。

2.2、Service和Activity通信

实现ServiceActivity之间通信是通过Binder,通过ServiceonBind()方法就可以获取Binder对象,接着在Activity中定义一个ServiceConnection的子类,在Activity建立关联的时候,通过该类onServiceConnected()方法就能获取到Service中的Binder对象,接着就可以使用BinderAPI实现ServiceActivity之间通信。

如下,自定义Myservice继承Service,并定义了一个内部类MyBinder继承Binder,重写MyService中的onBind()方法,该方法返回自定义的Binder对象。

public class MyService extends Service {public static final String TAG = "MyService";private MyBinder mBinder = new MyBinder();@Overridepublic void onCreate() {super.onCreate();Log.d(TAG, "onCreate() executed");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "onStartCommand() executed");return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();Log.d(TAG, "onDestroy() executed");}@Overridepublic IBinder onBind(Intent intent) {return mBinder;}class MyBinder extends Binder {public void startDownload() {Log.d("TAG", "startDownload() executed");// 执行具体的下载任务}}}

接着在布局文件中添加四个按钮,分别是start_servicestop_servicebind_serviceunbind_service,定义与Service通信的Activity的代码如下:

public class MainActivity extends Activity implements OnClickListener {private Button startService;private Button stopService;private Button bindService;private Button unbindService;private MyService.MyBinder myBinder;private ServiceConnection connection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {myBinder = (MyService.MyBinder) service;myBinder.startDownload();}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startService = (Button) findViewById(R.id.start_service);stopService = (Button) findViewById(R.id.stop_service);bindService = (Button) findViewById(R.id.bind_service);unbindService = (Button) findViewById(R.id.unbind_service);startService.setOnClickListener(this);stopService.setOnClickListener(this);bindService.setOnClickListener(this);unbindService.setOnClickListener(this);}@Overridepublic 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;case R.id.bind_service:Intent bindIntent = new Intent(this, MyService.class);bindService(bindIntent, connection, BIND_AUTO_CREATE);break;case R.id.unbind_service:unbindService(connection);break;default:break;}}}

从以上代码可知,Activity首先定义了ServiceConnection的匿名类,通过调用bindService()方法将ActivityService进行绑定,绑定之,ServiceConnection中的onServiceConnected()方法就会调用,在该方法里通过类型转换就可得到MyBinder对象,然后使用MyBinder对象执行下载逻辑。

方法介绍:

bindService(service, conn, flags)

ServiceIntent对象,

ConnServiceConnection的实例,

Flags标志位,这里传入BIND_AUTO_CREATE表示在ActivityService建立关联后自动创建Service,这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。

3、服务的生命周期 

1)采用Context.startService()方法启动服务

完整的生命周期:onCreate-onStartCommandonStart已经过时了)-onDestroy

如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。

多次启动服务并不会多次调用onCreate(创建服务),而是会多次调用onStartCommand(启动服务)。服务一旦停止,再次停止服务并不会有任何效果。

2)采用Context.bindService()方法启动服

完整的生命周期:onCreate-onBind-onUnbind->onDestroyed

调用者退出了,Srevice就会调用onUnbind->onDestroyed方法。调用者也可以通过调用unbindService方法来停止服务。

如果一个service通过bindServicestart之后,多次调用bindService的话,service只会调用一次onBind方法。多次调用unbindService的话会抛出异常。

注: 

Android系统的机制规定一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,当调用startService()bindServer()这种情况下要同时调用 stopService()和 unbindService()方法,onDestroy()方法才会执行。 

4、服务的高级使用

4.1、前台服务

服务几乎都是在后台运行的,但是服务的系统优先级还是比较低的,当系统出现内存不足的情况时,就有可能会回收掉正在后台运行的服务。为了不让服务由于系统内存不足的原因导致被回收, 就可以考虑使用前台服务。前台服务和普通服务最大的区别就在于它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,类似于通知的效果。

创建前台服务代码如下:

public class MyService extends Service {public static final String TAG = "MyService";private MyBinder mBinder = new MyBinder();@Overridepublic void onCreate() {super.onCreate();Notification notification = new Notification(R.drawable.ic_launcher,"有通知到来", System.currentTimeMillis());Intent notificationIntent = new Intent(this, MainActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,notificationIntent, 0);notification.setLatestEventInfo(this, "这是通知的标题", "这是通知的内容",pendingIntent);startForeground(1, notification);Log.d(TAG, "onCreate() executed");} .........}

4.2、intentService

IntentServiceService类的子类,用来处理异步请求。它会创建独立的worker线程来处理所有intent请求。用onHandleIntent() 处理耗时的操作,所有请求处理完成,IntentService会自动调用stopSelf()停止。

因为Service是安卓四大组件之一,运行在Main线程中,在服务中不能执行耗时操作,否则会出现ANRApplication Not Responding)异常,这时就需要要在服务中开启子线程去执行耗时任务了,代码如下:

public class MyService extends Service {@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {new Thread(new Runnable() {@Overridepublic void run() {// 处理具体的逻辑stopSelf();// 服务逻辑执行完成后,自己停止服务}}).start();return super.onStartCommand(intent, flags, startId);}}

以上代码虽不复杂,但容易忘记开启线程或者忘调用stopSlef(),为了解决上面的两大问题,就可以使用IntentService类。

public class MyIntentService extends IntentService {public MyIntentService() {super("MyIntentService"); // 调用父类的有参构造函数}@Overrideprotected void onHandleIntent(Intent intent) { // 打印当前线程的idLog.d("MyIntentService", "Thread id is "+ Thread.currentThread().getId());}@Overridepublic void onDestroy() {super.onDestroy();Log.d("MyIntentService", "onDestroy executed");}}

在自定义的IntentService中,需提供一个无参构造函数且必须在其内部调用父类的有参构造函数。然后要在子类中去实现onHandleIntent()这个抽象方法,在这个方法中可以去处理一些具体的逻辑,这个方法是在子线程中运行的。(查看log就会知道其id和主线程的id是不同的),当服务服务在运行结束后会自动停止的.

4.3、后台执行定时任务

Android中的定时任务一般有两种实现方式,一种是使用JavaAPI里提供的Timer类, 一种是使用AndroidAlarm机制。

由于Android手机会在长时间不操作的情况下自动让 CPU进入到睡眠状态,这就有可能导致 Timer中的定时任务无法正常运行。 而Alarm机制则不存在这种情况,它具有唤醒CPU的功能,即可以保证每次需要执行定时任务的时候CPU都能正常工作。

Alarm 机制的用法

首先通过Context的 getSystemService(Context.ALARM_SERVICE)方法来获取AlarmManager类。 

 然后调用 AlarmManager的 set()方法就可以设置一个定时任务了,

方法介绍:

am.set(type, triggerAtMillis, operation)

Type是一个整型参数,用于指定AlarmManager的工作类型,有四种值可选,分别是ELAPSED_REALTIMEELAPSED_REALTIME_WAKEUP、 RTC和 RTC_WAKEUP。 

ELAPSED_REALTIME表示定时任务的触发时间从系统开机开始算起,但不会唤醒 CPU

ELAPSED_REALTIME_WAKEUP:表示定时任务触发时间从系统开机开始,会唤醒 CPU

RTC表示定时任务的触发时间从 1970年 月 1日 0点开始算起,但不会唤醒 CPU

RTC_WAKEUP表示定时任务的触发时间从197011日 0点开始算起,会唤醒 CPUSystemClock.elapsedRealtime()方法可获取到系统开机至今所经历时间的毫秒数 System.currentTimeMillis()方法可以获取到 1970年 1月 1日 0点至今所经历时间的毫秒数。

triggerAtMillis:表示定时任务触发的时间,以毫秒为单位。

如果第一个参数使用的是ELAPSED_REALTIMEELAPSED_REALTIME_WAKEUP, 则这里传入开机至今的时间再加上延迟执行的时间。

如果第一个参数使用的是 RTC 或 RTC_WAKEUP,则这里传入 1970年 1月 1日 0点至今的时间再加上延迟执行的时间。

Operation是一个 PendingIntent,这里一般会调用 getBroadcast()方法来获取一个能够执行广播的 PendingIntent。这样当定时任务被触发的时候,广播接收器的 onReceive()方法就可以得到执行。 

因此设定一个任务在 10秒钟后执行有如下两种方式:

Long triggerAtTime = System.currentTimeMills+10*1000;

manager.set(AlarmManager.RTC_WAKEUP, triggerAtTime, pendingIntent); 

 

long triggerAtTime = SystemClock.elapsedRealtime()+10*1000; 

manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent); 

下面来看看一个简单定时任务的案例:

public class LongRunningService extends Service {@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {new Thread(new Runnable() {@Overridepublic void run() {Log.d("LongRunningService","executed at " + new Date().toString());}}).start();AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);int anHour = 60 * 60 * 1000; // 这是一小时的毫秒数long triggerAtTime = SystemClock.elapsedRealtime() + anHour;Intent i = new Intent(this, AlarmReceiver.class);PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);return super.onStartCommand(intent, flags, startId);}}

如上,定义一个服务类,在onStartCommand中开启了一个子线程去执行耗时任务,接着在子线程外设定一个定时任务。这里使用 PendingIntent指定处理定时任务的广播接收器为 AlarmReceiver,接下来定义AlarmReceiver

public class AlarmReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Intent i = new Intent(context, LongRunningService.class);context.startService(i);}}

从以上代码可知,广播接收器中只是开启了LongRunningService。这样当在activity中开启LongRunningService这个服务,服务中的onStartCommand()又会设定一个定时任务,这样一小时后AlarmReceiver 的onReceive()方法就会得到执行,就会又去启动服务,同时又会设定一个定时任务,构成了一个循环。

4.4、AIDL

AIDL:Android Interface Definition Language,安卓接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,通过远程服务调用从而实现Android设备上的两个进程间通信(IPC)inter process communication

实现跨进程通信分为创建远程服务和访问远程服务。

1)创建远程服务

1)新建一个AIDL文件,在这个文件中定义好Activity需要与Service进行通信的方法

package com.example.servicetest;interface MyAIDLService {int plus(int a, int b);String toUpperCase(String str);}

2点击保存之后,gen目录下就会生成一个对应的Java文件

3修改自定义的MyService中的代码,在里面实现刚定义好的MyAIDLService接口

public class MyService extends Service { ......@Overridepublic IBinder onBind(Intent intent) {return mBinder;}MyAIDLService.Stub mBinder = new Stub() {@Overridepublic String toUpperCase(String str) throws RemoteException {if (str != null) {return str.toUpperCase();}return null;}@Overridepublic int plus(int a, int b) throws RemoteException {return a + b;}};}

4)在清单文件中注册,并给MyService加一个action,因为必须使用隐式Intent

<service    android:name="com.example.servicetest.MyService"    android:process=":remote" >    <intent-filter>        <action android:name="com.example.servicetest.MyAIDLService" />    </intent-filter></service>

2)访问远程服务

通过bindService()来访问远程服务。

1)拷贝aidl文件到其他应用中同名包下

2)新建一个activity,使用bindService()绑定远程服务。

public class MainActivity extends Activity {private MyAIDLService myAIDLService;private ServiceConnection connection = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {myAIDLService = MyAIDLService.Stub.asInterface(service);try {int result = myAIDLService.plus(50, 50);String upperStr = myAIDLService.toUpperCase("comes from ClientTest");Log.d("TAG", "result is " + result);Log.d("TAG", "upperStr is " + upperStr);} catch (RemoteException e) {e.printStackTrace();}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button bindService = (Button) findViewById(R.id.bind_service);bindService.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.servicetest.MyAIDLService");bindService(intent, connection, BIND_AUTO_CREATE);}});}}

3)在activity创建ServiceConnection内部类,在onServiceConnected()方法中通过MyAIDLService.Stub.asInterface(service)获取Bindler对象,并可以实现跨进程通信。

  

0 0
原创粉丝点击