距离上篇文章竟然快一年了。这次是想明确service一些比较重要的点。
至于什么是service,我也不想多去讨论,我只想清晰确认这么几个问题:
1、service的生命周期到底如何?
2、Activity如何让service做事?
3、service与thread之间有没有关系?
4、远程service是什么东西?
5、AIDL的使用?
6、前台service?
一、生命周期
如果需要图,可以百度,好多。我这里直接运行代码打log。
1、startService()启动Service
操作顺序是:
startService(intent),service相应执行的是oncreate() ------> onStartCommand() ------> onStart()。
stopService(intent),service相应执行的是onDestroy()。
startService(intent),service相应执行的是oncreate() ------> onStartCommand() ------> onStart()。
startService(intent),service相应执行的是onStartCommand() ------> onStart()。
stopService(intent),service相应执行的是onDestroy()。
这里可见,onCreate() 只会执行一次,即service第一次被启动的时候,在没有destroy之前,继续启动onCreate()不会再执行。
2、bindService()启动Service
首先说明:
如果使用bindService()启动Service,需要实例一个接口,ServiceConnection。然后实现接口里面的两个方法,onServiceDisconnected()和onServiceConnected()。正常情况下只会执行onServiceConnected()这个方法,另一个方法会在系统内存不足强制回收service导致connection断开时执行。
private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub } @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub DebugLog("onServiceConnected()"); } };
bindService的原型:
public boolean bindService(Intent service, ServiceConnection conn, int flags)
参数:
service:Intent对象,说明要启动哪一个service
conn:就是上面创建的连接
flags:一个标志,一般使用自动创建(BIND_AUTO_CREATE)
unBindService的原型:
public void unbindService(ServiceConnection conn)
参数:
conn:就是上面建立的连接,如果unbindservice后继续unbind会抛出运行异常,终止程序。
log如下:
操作顺序:
bindService(intent3, connection, BIND_AUTO_CREATE): service执行顺序:onCreate() ------>onBind() ------> onServiceConnected()
unbindService(connection):service执行顺序:onUnbind() -------> onDestroy()
startService(intent):service执行顺序:onCreate() ------> onStartCommand() -------->onStart()
bindService(intent3, connection, BIND_AUTO_CREATE): service执行顺序:onBind() ------> onServiceConnected()
unbindService(connection):service执行顺序:onUnbind()
stopService(intent):service执行顺序:onDestroy()
当service的生命周期了解之后,那么如果需要用到service时,应该也知道该在哪个方法里面写了。
二、Activity如何让service服务自己?
对于activity向service传值不再讲,使用intent启动服务时,该intent就是service 中onStartCommand()和onStart()函数中参数的intent,因此简单的传值可以直接使用intent即可。下面主要讲如何进行方法的调用?使service真正的服务activity。
1、使用binder类
首先在Service类里面创建一个继承于Binder的类,比如:
public class MyBinder extends Binder{ public void startDownload(){ DebugLog("start download..."); } //获取当前Myservice实例 public MyService getService(){ return MyService.this; } }
这个类里面有一个startDownload()方法,模拟下载。
在onBinder()方法中返回类实例:
public IBinder onBind(Intent intent) { // TODO Auto-generated method stub DebugLog("onBind()"); return new MyBinder(); }
在serviceConnection实现里的onServiceConnected()方法中,得到这个类的实例,,然后可以执行里面的方法。
MyService.MyBinder myBinder = (MyBinder)service; myBinder.startDownload();
这种方法可以简单的让Activity调用service里面的方法。
2、使用广播
至于activity与service哪一端做接收器哪一端做发送端,根据实际情况而定。这里是activity发送广播,service进行监听。这种方式在音乐播放器可以使用,比如让service监听系统广播,如果来电了要暂停播放。等等。
这里简单写一下,只是提供思路。
在service里面动态注册广播:
private BroadcastReceiver myReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub String string = intent.getStringExtra("main"); DebugLog("string:"+string); } };
在Service 中的onCreate()函数里注册:
IntentFilter filter = new IntentFilter(); filter.addAction("com.fleur.mybroadcast"); registerReceiver(myReceiver, filter); DebugLog("registerReceiver success");
在service中的onDestroy()函数中反注册:
unregisterReceiver(myReceiver);
动态注册的广播一定不要忘记反注册。
如此之后就可以在activity里面发送广播了。比如:
case R.id.button8: Intent intent5 = new Intent("com.fleur.mybroadcast"); intent5.putExtra("main", "this broadcast is from mainActivity"); sendBroadcast(intent5); break;
log如下:
3、使用回调
我一直觉得回调是一件很神奇的事情。如果不懂回调可以看一下这篇文章:http://blog.csdn.net/xiaanming/article/details/8703708
回调解决了service去调用activity里面的方法。
首先创建回调接口(单独一个接口文件):
public interface OnProgressListener { public void onProgress(int progress); }
接口里面就是回调方法。
在Service里面生命一个接口变量,然后提供一个外部注册接口用的方法。比如:
//更新进度的回调接口 private OnProgressListener onProgressListener; /** * 注册回调接口的方法 * @param onProgressListener */ public void setOnProgressListener(OnProgressListener onProgressListener) { this.onProgressListener = onProgressListener; }
然后再Service里面声明一个方法,依然模拟下载:
public void startDownload(){ new Thread(){ @Override public void run() { while(progress < MAX_PROGRESS){ progress += 5; //进度发生变化时告诉调用方 if(onProgressListener!=null){ onProgressListener.onProgress(progress); } try { sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }.start(); }
注意这个是service里面的方法,不是MyBinder类里面的方法。
在activity里面:
首先声明一个MyService类的对象,然后在connection中得到这个对象,注册回调接口,实现回调方法。如下:
public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub DebugLog("onServiceConnected()"); myBinder = (MyBinder) service; myService = myBinder.getService(); myService.setOnProgressListener(new OnProgressListener() { @Override public void onProgress(int progress) { // TODO Auto-generated method stub progressBar.setProgress(progress); } }); }
//然后自己写一个button控制,使用得到的MyService对象调用startDownload()方法,实现回调。
myService.startDownload();
对于不同进程间的activity通信,有两种方式,一是使用Messenger,二是使用AIDL。这里先不讲了。
三、service与thread之间有没有关系?
service是运行在后台的没有用户界面的,新建一个thread也是用户不能看到的,运行在后台的。但是service与thread真的是一点关系都没有,对于普通的local service来说,他跟activity是运行在同一个进程里面的主线程里的,也就是说service也是主线程,UI线程,不能进行耗时操作。那如果我想后台访问网页去下载呢?简单啊,在service里开辟一个新线程呗,跟在activity里面开辟新线程没有区别。
四、远程service是什么东西?
正常我们在ActivityManifest.xml文件中声明的service都是LocalService,即本地服务。也就是只能服务本应用程序(APP)。但是有些service是想服务别的APP的,这时就应该用远程service,其实只需要在ActivityManifest.xml文件声明service时这样写:
<service android:name="com.fleur.service.MyRemoteService" android:process=":remote"> <intent-filter > <action android:name="com.fleur.service.MyRemoteService"/> </intent-filter>
添加了一句话:android:process=":remote"
即变成了远程服务。
这里注意,远程服务已经跟activity不在同一个进程了,使用startService() 打印log如下:
这样如果在远程服务做耗时操作,并不会产生ANR问题。但是,
使用使用远程服务,不能直接使用bindService()了,不能像localservice那样与activity通信了。这时需要使用AIDL进行,进程间通信。
还是能不使用远程service就不要用了吧。
五、AIDL的使用?
首先建立一个.aidl文件,里面使用Java的语法写一个接口。保存后会在gen下面自动生成Java文件。如下:
package com.fleur.service; interface MyAIDLService{ String toUpperCase(String str); }
在远程服务里实例一个Binder对象,实现接口的方法。如下:
import com.fleur.service.MyAIDLService.Stub;
Stub mBinder = new Stub() { @Override public String toUpperCase(String str) throws RemoteException { // TODO Auto-generated method stub if(str!=null){ return str.toUpperCase(); } return null; } };
其实stub是aidl文件里的,它继承了Binder实现了我们自己写的接口。
然后在onBind()里面return这个对象。
在activity中同样实例serviceConnected接口,声明一个刚才写的接口对象,在onServiceConnected里面得到实例。
private MyAIDLService myAIDLService; private ServiceConnection conn = new ServiceConnection( ) { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub } @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub myAIDLService = MyAIDLService.Stub.asInterface(service); try { String upperStr = myAIDLService.toUpperCase("hello world"); DebugLog("upperStr = "+upperStr); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } };
这样在去bindService就OK了。实现了不同进程间的通信。
六、前台service
service优先级特别低的,如果内存不足很容易会回收,但是有时候我们的APP很依赖service,不希望回收,如果回收了,我这app也没意思了,这时就可以使用前台service了。其实很简单,在service的onCreate方法中这样写:
Notification notification = new Notification(R.drawable.ic_launcher, "Android_Component", System.currentTimeMillis()); Intent notificationIntent = new Intent(this,MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(this, "title of notification", "content of notification", pendingIntent); startForeground(1, notification);
最最关键的就是最后一句,startForeground()方法。
当再次启动这个服务的时候就会发现通知栏一个图标,这就是你在后台运行的“前台service”。
当service被销毁时前台service相应销毁。
比如音乐播放器啊,天气类的啊,对service依赖比较高的可以考虑前台service。
一些东西自己明白但是也可能说不明白,参考了一篇博客:http://www.360doc.com/content/14/0415/18/2793098_369238276.shtml
然后加之自己的理解。
对于service一些盲点或者重要的点,以前没有总结过,这次真的认真总结了一回。除此还有前台service,可以自己看一下。
service只是Android中我需要总结的很多重要的点中的一个。接下来会陆陆续续总结一下。
版权声明:本文为博主原创文章,未经博主允许不得转载。