Android之service一些基础回顾

今天是开始记录学习的第二天,今天记录的东西大部分是以前学过的,今天在这里把它梳理成文,主要还是对以前学习的一个总结和回顾。知识除了积累还要经常温故,古语有云:温故而知新。

1.Service的启动一般都是 某个组件调用startService 或者 bindService  
2. 通过 Android特有的 Binder IPC机制 通知 ActivityServiceManager ,
3.Ams就会直接通过Binder IPC机制 通知 Service 所在的ActivityThread 把这个服务启动起来 (把这个Service的class文件load到内存)
4.如果是BindService ,Ams还会从Service哪里取到一个binder 然后同样通过IPC传给启动他的Activity ,Activity 拿到这个binder后就可以和Service通讯了

ok 下面注重介绍的是Service的用法
1.通过startService方式启动的Service
a 生命周期
context.startService() 的生命周期
context.startService()  -> onCreate()  -> onStart()  -> Service running  -> context.stopService()  -> onDestroy()  -> Service stop 
如果Service还没有运行,则android先调用onCreate(),然后调用onStart();
如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。 
如果stopService的时候会直接onDestroy,如果是调用者自己直接退出而没有调用stopService或者Service.stopSelfResult()的话,Service会一直在后台运行,该Service的调用者再启动起来后可以通过stopService或者Service.stopSelfResult()关闭Service。
所以调用startService的生命周期为:

onCreate-->onStart(可多次调用) -->onDestroy

如图:

实例代码
MainActivity.java
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package  com.example.testservice;
 
 
public  class  MainActivity  extends  Activity {
 
         @Override
         protected  void  onCreate(Bundle savedInstanceState) {
                 super .onCreate(savedInstanceState);
                 setContentView(R.layout.activity_main);
                 // Intent service = new Intent(this, MyService.class);//通过组件名调用
                 Intent service =  new  Intent(
                                 "com.example.testservice.MYSERVICEFROMACTION" ); // 隐式调用,注意在manifest中指定action
                 startService(service);
 
         }
 
}

Service.java
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public  class  MyService  extends  Service {
         String TAG =  "MyService" ;
 
         @Override
         public  void  onCreate() {
                 
                 new  AsyncTask() {
 
                         @Override
                         protected  Void doInBackground(Void... params) {
                                 for  ( int  i =  0 ; i <  1000 ; i++) {
                                         try  {
                                                 Thread.sleep( 1000 );
                                         catch  (InterruptedException e) {
                                                 // TODO Auto-generated catch block
                                                 e.printStackTrace();
                                         }
 
                                         Log.e(TAG, i +  "service" );
 
                                 }
 
                                 return  null ;
                         }
 
                 }.execute();
                 super .onCreate();
 
         }
 
         @Override
         public  IBinder onBind(Intent intent) {
                 Log.e(TAG,  "onBind" );
                 return  null ;
         }
}

AndroidManifest.xml
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
< application
         android:allowBackup = "true"
         android:icon = "@drawable/ic_launcher"
         android:label = "@string/app_name"
         android:theme = "@style/AppTheme"  >
         < activity
             android:name = "com.example.testservice.MainActivity"
             android:label = "@string/app_name"  >
             < intent-filter >
                 < action  android:name = "android.intent.action.MAIN"  />
 
                 < category  android:name = "android.intent.category.LAUNCHER"  />
             intent-filter >
         activity >
 
         < service  android:name = "com.example.testservice.MyService"  >
             < intent-filter >
                 < action  android:name = "com.example.testservice.MYSERVICEFROMACTION"  />
             intent-filter >
         service >
     application >

这里注意一个问题,不要以为Service是一个大的Thread 其实Service就是一个组件 一个没有界面的Activity 所以耗时任务同样是需要在异步线程中完成的! 所以我添加了一个AsyncTask如果不添加异步,将会发送阻塞

2.通过bindService启动Service

context.bindService()启动的Service的生命周期
context.bindService()  -> onCreate()  -> onBind()  -> Service running  -> onUnbind()  -> onDestroy()  -> Service stop

onBind()将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。 所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。

如图所示:




其实bind的意思就是绑定。也就是把一个Activity和一个Service绑定在一块 有些时候的一些业务需要这样做,比如,你让Service处理网络数据,完成后Activity把数据放到View中去
所以Activity bind一个Service会拿到这个Service的引用可以操作Service中的方法
简单的代码如下:
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public  class  MainActivity  extends  Activity {
         public  MyService myService;
 
         @Override
         protected  void  onCreate(Bundle savedInstanceState) {
                 super .onCreate(savedInstanceState);
                 setContentView(R.layout.activity_main);
                 Intent service =  new  Intent( this , MyService. class ); // 通过组件名调用
                 bindService(service, conn, Context.BIND_AUTO_CREATE);
 
         }
 
         // Ams开启的Service后 会通知MainActivity在不同的时机执行这些回调
         private  ServiceConnection conn =  new  ServiceConnection() {
 
                 @Override
                 public  void  onServiceDisconnected(ComponentName name) {
 
                 }
 
                 // 在这里可以拿到Service中设置返回的东西,一般是一个Service对象
                 @Override
                 public  void  onServiceConnected(ComponentName name, IBinder service) {
                         myService = ((MyService.MyBinder) service).getService(); //拿到了Service的引用
 
                         Toast.makeText(MainActivity. this , myService.getVal(),  10 ).show();
 
                 }
         };
}

代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package  com.example.testservice;
 
import  android.app.Service;
import  android.content.Intent;
import  android.os.AsyncTask;
import  android.os.Binder;
import  android.os.IBinder;
import  android.util.Log;
 
public  class  MyService  extends  Service {
         String TAG =  "MyService" ;
 
         public  String getVal() {
                 return  "Hi I am from Service!" ;
 
         }
 
         // 封装后Activity就可以通过getService方法获取这个服务实例了
         @Override
         public  IBinder onBind(Intent intent) {
                 Log.e(TAG,  "onBind" );
                 return  new  MyBinder();
         }
 
         public  class  MyBinder  extends  Binder {
                 MyService getService() {
                         return  MyService. this ;
                 }
         }
}


其实,通常我们会做一个接口类,让Service实现他 ,然后在Activity 用这个接口类型就可以调用这些方法了 有点类似c++的头文件机制 如下:
代码片段,双击复制
01
02
03
public  interface  IMyService {
         public  String getVal();
}


代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public  class  MainActivity  extends  Activity {
         public  IMyService myService;
 
         @Override
         protected  void  onCreate(Bundle savedInstanceState) {
                 super .onCreate(savedInstanceState);
                 setContentView(R.layout.activity_main);
                 Intent service =  new  Intent( this , MyService. class ); // 通过组件名调用
                 bindService(service, conn, Context.BIND_AUTO_CREATE);
 
         }
 
         // Ams开启的Service后 会通知MainActivity在不同的时机执行这些回调
         private  ServiceConnection conn =  new  ServiceConnection() {
 
                 @Override
                 public  void  onServiceDisconnected(ComponentName name) {
 
                 }
 
                 // 在这里可以拿到Service中设置返回的东西,一般是一个Service对象
                 @Override
                 public  void  onServiceConnected(ComponentName name, IBinder service) {
                         myService = ((MyService.MyBinder) service).getService();
                         Log.e( "TAG" , name.toString()); // 组件的ComponentName
                         Toast.makeText(MainActivity. this , myService.getVal(),  10 ).show();
 
                 }
         };
}

代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public  class  MyService  extends  Service  implements  IMyService {
         String TAG =  "MyService" ;
 
         public  String getVal() {
                 return  "Hi I am from Service!" ;
 
         }
 
         // 封装后Activity就可以通过getService方法获取这个服务实例了
         @Override
         public  IBinder onBind(Intent intent) {
                 Log.e(TAG,  "onBind" );
                 return  new  MyBinder();
         }
 
         public  class  MyBinder  extends  Binder {
                 MyService getService() {
                         return  MyService. this ;
                 }
         }
}

3、关于Service.onStart()和Service.onStartCommand()
谢谢版主的提醒,这个问题还是和值得研究的!其实涉及的是Service的重启问题  还是说一下原理吧:
首先看 Service.onStartCommand()的代码
  
代码片段,双击复制
01
02
03
04
   public  int  onStartCommand(Intent intent,  int  flags,  int  startId) {
         onStart(intent, startId);
         return  mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
     }

原来其实 Service.onStartCommand()也是调用了  onStart(intent, startId);与onStart相比只不过多了一个flags 并且返回了一个参数而已 
首先是这个flags是什么呢?
其实 Service.onStartCommand()是在ActivityThread的void handleServiceArgs(ServiceArgsData data)调用的 这个方法是在ActivityThread控制
Service的消息队列中等待执行的:

代码片段,双击复制
01
02
03
04
case  SERVICE_ARGS:
   Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,  "serviceStart" );
   handleServiceArgs((ServiceArgsData)msg.obj);
  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);


最后 我会找到调用他的本源
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
    public  final  void  scheduleServiceArgs(IBinder token,  boolean  taskRemoved,  int  startId,
             int  flags ,Intent args) {
             ServiceArgsData s =  new  ServiceArgsData();
             s.token = token;
             s.taskRemoved = taskRemoved;
             s.startId = startId;
             s.flags = flags;
             s.args = args;
 
             queueOrSendMessage(H.SERVICE_ARGS, s);
         }

这个方法是在ActiviceService这个类中调用的:
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private  final  void  sendServiceArgsLocked(ServiceRecord r,  
         boolean  oomAdjusted) {  
     final  int  N = r.pendingStarts.size();  
     if  (N ==  0 ) {  
         return ;  
     }  
   
     while  (r.pendingStarts.size() >  0 ) {  
         try  {  
             ServiceRecord.StartItem si = r.pendingStarts.remove( 0 );  
   
             si.deliveredTime = SystemClock.uptimeMillis();  
             r.deliveredStarts.add(si);  
             si.deliveryCount++;  
             if  (si.neededGrants !=  null ) {  
                 mAm.grantUriPermissionUncheckedFromIntentLocked(si.neededGrants,  
                         si.getUriPermissionsLocked());  
             }  
   
             int  flags =  0 ;  
             if  (si.deliveryCount >  1 ) {  
                 flags |= Service.START_FLAG_RETRY;  
             }  
             if  (si.doneExecutingCount >  0 ) {  
                 flags |= Service.START_FLAG_REDELIVERY;  
             }  
             r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);  
         catch  (RemoteException e) {  
            ....  
     }  
}  

            r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);  [/mw_shl_code]
我们看的这里他的参数有两个可能 START_FLAG_RETRY,START_FLAG_REDELIVERY 这个Sdk有说明哦 现在从源码看来  这个flags是为了告诉你 
当前是 START_FLAG_RETRY还是START_FLAG_REDELIVERY

都说到这里了 继续往下吧:
那么谁调用的sendServiceArgsLocked() 呢?其实是ActivityThread通过下面的方法使用aidl通知的ActiveService  于是 我们就发现 是在Service调用了onCreate之后调用的这个方法。
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
private  void  handleCreateService(CreateServiceData data) {  
     // If we are getting ready to gc after going to the background, well  
     // we are back active so skip it.  
   
     LoadedApk packageInfo = getPackageInfoNoCheck(  
             data.info.applicationInfo, data.compatInfo);  
     Service service =  null ;  
     try  {  
         java.lang.ClassLoader cl = packageInfo.getClassLoader();  
         service = (Service) cl.loadClass(data.info.name).newInstance();  
     catch  (Exception e) {  
         ... ...  
     }  
   
     try  {  
         if  (localLOGV) Slog.v(TAG,  "Creating service "  + data.info.name);  
   
         ContextImpl context =  new  ContextImpl();  
         context.init(packageInfo,  null this );  
   
         Application app = packageInfo.makeApplication( false , mInstrumentation);  
         context.setOuterContext(service);  
         service.attach(context,  this , data.info.name, data.token, app,  
                 ActivityManagerNative.getDefault());  
         service.onCreate();  
         mServices.put(data.token, service);  
         try  {  
             ActivityManagerNative.getDefault().serviceDoneExecuting(  
                     data.token,  0 0 0 );  
         catch  (RemoteException e) {  
             // nothing to do.  
         }  
     catch  (Exception e) {  
         ... ...  
     }  
}  


好了 说完了 参数说法返回值吧

代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   private  void  handleServiceArgs(ServiceArgsData data) {
         Service s = mServices.get(data.token);
         if  (s !=  null ) {
             try  {
                 if  (data.args !=  null ) {
                     data.args.setExtrasClassLoader(s.getClassLoader());
                 }
                 int  res;
                 if  (!data.taskRemoved) {
                     res = s.onStartCommand(data.args, data.flags, data.startId);
                 else  {
                     s.onTaskRemoved(data.args);
                     res = Service.START_TASK_REMOVED_COMPLETE;
                 }
 
                 QueuedWork.waitToFinish();
 
                 try  {
                     ActivityManagerNative.getDefault().serviceDoneExecuting(
                             data.token,  1 , data.startId, res); //重新调用了 serviceDoneExecuting
                 catch  (RemoteException e) {
                     // nothing to do.
                 }
                 ensureJitEnabled();
             catch  (Exception e) {
                 if  (!mInstrumentation.onException(s, e)) {
                     throw  new  RuntimeException(
                             "Unable to start service "  + s
                             " with "  + data.args +  ": "  + e.toString(), e);
                 }
             }
         }
     }


这个方法后两个作用
1. 调用s.onStartCommand去做相关的操作,onStartCommand会返回一个integer. The integer is a value that describes how the system should continue the service in the event that the system kills it.  
2. 告诉AMS已经完成了ActivityManagerNative.getDefault().serviceDoneExecuting(data.token, 1, data.startId, res); ActiveServices会收到通知,在ActiveServices.serviceDoneExecutingLocked中会对onStartCommand返回的值作出对应的处理。

代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void  serviceDoneExecutingLocked(ServiceRecord r,  int  type,  int  startId,  int  res) {  
     boolean  inStopping = mStoppingServices.contains(r);  
     if  (r !=  null ) {  
         if  (type ==  1 ) {  
             // This is a call from a service start...  take care of  
             // book-keeping.  
             r.callStart =  true ;  
             switch  (res) {  
                 case  Service.START_STICKY_COMPATIBILITY:  
                 case  Service.START_STICKY: {  
                     // We are done with the associated start arguments.  
                     r.findDeliveredStart(startId,  true );  
                     // Don't stop if killed.  
                     r.stopIfKilled =  false ;  
                     break ;  
                 }  
                 case  Service.START_NOT_STICKY: {  
                     // We are done with the associated start arguments.  
                     r.findDeliveredStart(startId,  true );  
                     if  (r.getLastStartId() == startId) {  
                         // There is no more work, and this service  
                         // doesn't want to hang around if killed.  
                         r.stopIfKilled =  true ;  
                     }  
                     break ;  
                 }  
                 case  Service.START_REDELIVER_INTENT: {  
                     // We'll keep this item until they explicitly  
                     // call stop for it, but keep track of the fact  
                     // that it was delivered.  
                     ServiceRecord.StartItem si = r.findDeliveredStart(startId,  false );  
                     if  (si !=  null ) {  
                         si.deliveryCount =  0 ;  
                         si.doneExecutingCount++;  
                         // Don't stop if killed.  
                         r.stopIfKilled =  true ;  
                     }  
                     break ;  
                 }  
                 case  Service.START_TASK_REMOVED_COMPLETE: {  
                     // Special processing for onTaskRemoved().  Don't  
                     // impact normal onStartCommand() processing.  
                     r.findDeliveredStart(startId,  true );  
                     break ;  
                 }  
                 default :  
                     throw  new  IllegalArgumentException(  
                             "Unknown service start result: "  + res);  
             }  
             if  (res == Service.START_STICKY_COMPATIBILITY) {  
                 r.callStart =  false ;  
             }  
         }  
         final  long  origId = Binder.clearCallingIdentity();  
         serviceDoneExecutingLocked(r, inStopping);  
         Binder.restoreCallingIdentity(origId);  
     else  {  
         Slog.w(TAG,  "Done executing unknown service from pid "  
                 + Binder.getCallingPid());  
     }  
}  


代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private  void  serviceDoneExecutingLocked(ServiceRecord r,  boolean  inStopping) {  
     if  (DEBUG_SERVICE) Slog.v(TAG,  "<<< DONE EXECUTING "  + r  
             ": nesting="  + r.executeNesting  
             ", inStopping="  + inStopping +  ", app="  + r.app);  
     else  if  (DEBUG_SERVICE_EXECUTING) Slog.v(TAG,  "<<< DONE EXECUTING "  + r.shortName);  
     r.executeNesting--;  
     if  (r.executeNesting <=  0  && r.app !=  null ) {  
         if  (DEBUG_SERVICE) Slog.v(TAG,  
                 "Nesting at 0 of "  + r.shortName);  
         r.app.executingServices.remove(r);  
         if  (r.app.executingServices.size() ==  0 ) {  
             if  (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG,  
                     "No more executingServices of "  + r.shortName);  
             mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);  
         }  
         if  (inStopping) {  
             if  (DEBUG_SERVICE) Slog.v(TAG,  
                     "doneExecuting remove stopping "  + r);  
             mStoppingServices.remove(r);  
             r.bindings.clear();  
         }  
         mAm.updateOomAdjLocked(r.app);  
     }  
}  


说了一堆废话 我们会发现,其实这个返回值就是用来告诉Android系统:在系统资源紧张时服务的任务还没有完成而被回收后,系统对这个被强行回收的服务所做的事情。

还是 参考《 Android开发 精要》一书的解释吧:(强烈推荐这本书啊)
“当系统资源紧张时,后台服务组件可能会被系统强制回收,而此时,服务组件可能尚未完成。”“onStartCommand函数增加了返回值和控制参数用于指定后台服务自己的运行方式”

介绍一下他们的几个返回值:具体请看Sdk 
START_STICKY

如果资源紧张,service进程被kill掉,保留service的状态为开始状态,但不保留传递的的intent参数。随后系统会尝试重新创建service,由于服务状态回到了开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令(startService)被传递到service,那么参数Intent将为null。这种模式可以应用于形如后台播放音乐这种随时可能会被启动和停止的场景中,Android原生音乐播放器就是这么做的了,详情查看源码。注意返回这值请显示stop这个Service,否则浪费系统资源。

START_NOT_STICKY
“非粘性的”。如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务,系统不会关注这个服务的任务是否完成。邮件服务就使用了这个方案配合
alarmmanager每隔一段时间唤醒一次服务。服务执行完毕很有可能被系统回收。
START_REDELIVER_INTENT

使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,原来的intent参数不会被丢失,这是那个flags参数会被标志为START_FLAG_REDELIVERY以区分是否是首次调用onstartCommand()方法、参考Android原生的日历应用就是使用这个参数,在系统把服务回收后,他重启了扔可以获得intent中的数据。

START_STICKY_COMPATIBILITY
START_STICKY的兼容版本,不保证服务被kill后一定能重启。

我们知道这个方法只能支持2.0以上,那么2.0以下呢?

   
代码片段,双击复制
01
02
03
04
public  int  onStartCommand(Intent intent,  int  flags,  int  startId) {
         onStart(intent, startId);
         return  mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
     }
如果mStartCompatibility 为 true就是START_STICKY_COMPATIBILITY [兼容模式],否则为START_STICKY
mStartCompatibility 是什么?
   
代码片段,双击复制
01
02
03
04
private  boolean  mStartCompatibility =  false ;
     .... //省略若干无关方法与代码
     mStartCompatibility = getApplicationInfo().targetSdkVersion
                 < Build.VERSION_CODES.ECLAIR;

如果系统版本低于ECLAIR(Android 2.0以下)就兼容模式了


4、 拓展知识(进程和声明周期)
Android操作系统尝试尽可能长时间的保持应用的进程,但当可用内存很低时最终要移走一部分进程。怎样确定那些程序可以运行,那些要被销毁,Android让每一个进程在一个重要级的基础上运行,重要级低的进程最有可能被淘汰,一共有5级,下面这个列表就是按照重要性排列的:

1 一个前台进程显示的是用户此时需要处理和显示的。下列的条件有任何一个成立,这个进程都被认为是在前台运行的。
        a 与用户正发生交互的。
        b 它控制一个与用户交互的必须的基本的服务。
        c 有一个正在调用生命周期的回调函数的service(如onCreate()、onStar()、onDestroy())
        d 它有一个正在运行onReceive()方法的广播接收对象。
        只有少数的前台进程可以在任何给定的时间内运行,销毁他们是系统万不得已的、最后的选择——当内存不够系统继续运行下去时。通常,在这一点上,设备已经达到了内存分页状态,所以杀掉一些前台进程来保证能够响应用户的需求。

2 一个可用进程没有任何前台组件,但它仍然可以影响到用户的界面。下面两种情况发生时,可以称该进程为可用进程。
        它是一个非前台的activity,但对用户仍然可用(onPause()方法已经被调用)这是可能发生的,例如:前台的activity是一个允许上一个activity可见的对话框,即当前activity半透明,能看到前一个activity的界面,它是一个服务于可用activity的服务。

3 一个服务进程是一个通过调用startService()方法启动的服务,并且不属于前两种情况。尽管服务进程没有直接被用户看到,但他们确实是用户所关心的,比如后台播放音乐或网络下载数据。所以系统保证他们的运行,直到不能保证所有的前台可见程序都正常运行时才会终止他们。

4 一个后台进程就是一个非当前正在运行的activity(activity的onStop()方法已经被调用),他们不会对用户体验造成直接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。通常,后台进程会有很多个在运行,所以他们维护一个LRU最近使用程序列表来保证经常运行的activity能最后一个被终止。如果一个activity正确的实现了生命周期的方法,并且保存它当前状态,杀死这些进程将不会影响到用户体验。

5 一个空线程没有运行任何可用应用程序组,保留他们的唯一原因是为了设立一个缓存机制,来加快组件启动的时间。系统经常杀死这些内存来平衡系统的整个系统的资源,进程缓存和基本核心缓存之间的资源。
Android把进程里优先级最高的activity或服务,作为这个进程的优先级。例如,一个进程拥有一个服务和一个可见的activity,那么这个进程将会被定义为可见进程,而不是服务进程。

此外,如果别的进程依赖某一个进程的话,那么被依赖的进程会提高优先级。一个进程服务于另一个进程,那么提供服务的进程不会低于获得服务的进程。例如,如果进程A的一个内容提供商服务于进程B的一个客户端,或者进程A的一个service被进程B的一个组件绑定,那么进程A至少拥有和进程B一样的优先级,或者更高。

因为一个运行服务的进程的优先级高于运行后台activity的进程,一个activity会准备一个长时间运行的操作来启动一个服务,而不是启动一个线程–尤其是这个操作可能会拖垮这个activity。例如后台播放音乐的同时,通过照相机向服务器发送一张照片,启动一个服务会保证这个操作至少运行在service 进程的优先级下,无论这个activity发生了什么,广播接收者应该作为一个空服务而不是简单的把耗时的操作单独放在一个线程里。 



Android 创建永不Kill的Service

 

1、提升服务的优先级

 

Android AndroidManifest.xml 里面给服务增加优先级,通过content.StartService();方式启动服务。1000是最高值,如果数字越小则优先级越低

 


    

          

    


Intent intent = new Intent();

intent .setAction("com.xsl.push");

context.startService(intent );

 

 

2、在Android AndroidManifest.xmlapplication标签中添加android:persistent属性

 


        android:icon="@drawable/app_default"

        android:label="@string/app_name"

        android:persistent="true" >

        ........................................

  切记,这个不可滥用,系统中用这个的serviceapp一多,整个系统就完蛋了

 

3、在ServiceonDestroy()中重启Service.这种方式,用户在无法再设置-运行的服务中将此服务停止

 


public void onDestroy() {

    // TODO Auto-generated method stub

    super.onDestroy();

        

    Intent localIntent = new Intent();

        localIntent.setClass(this, TestService.class);  //销毁时重新启动Service

        this.startService(localIntent);

}



===========================================================================


1、Service的种类

按运行地点分类:
类别 区别 优点 缺点 应用
本地服务(Local) 该服务依附在主进程上, 服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。 主进程被Kill后,服务便会终止。 非常常见的应用如:HTC的音乐播放服务,天天动听音乐播放服务。
远程服务(Remote) 该服务是独立的进程, 服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。 该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。 一些提供系统服务的Service,这种Service是常驻的。
其实remote服务还是很少见的,并且一般都是系统服务。

按运行类型分类:
类别 区别 应用
前台服务 会在通知一栏显示 ONGOING 的 Notification, 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。
后台服务 默认的服务即为后台服务,即不会在通知一栏显示 ONGOING 的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。
有同学可能会问,后台服务我们可以自己创建 ONGOING 的 Notification 这样就成为前台服务吗?答案是否定的,前台服务是在做了上述工作之后需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为 前台服务。这样做的好处在于,当服务被外部强制终止掉的时候,ONGOING 的 Notification 任然会移除掉。

按使用方式分类:
类别 区别
startService 启动的服务 主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService
bindService 启动的服务 该方法启动的服务要进行通信。停止服务使用unbindService
startService 同时也 bindService 启动的服务 停止服务应同时使用stepService与unbindService
以上面三种方式启动的服务其生命周期也有区别,将在随后给出。

2、Service 与 Thread 的区别

很多时候,你可能会问,为什么要用 Service,而不用 Thread 呢,因为用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!

既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

3、Service的生命周期

onCreate  onStart  onDestroy  onBind
1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

特别注意:
1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自      动解除,并且Service会自动停止);
2、你应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService;
3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;
4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。
5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。

4、startService 启动服务

想要用 startService  启动服务,不管Local 还是 Remote 我们需要做的工作都是一样简单。当然要记得在 Androidmanifest.xml 中注册 service。
根据上面的生命周期,我们便会给出 Service 中的代码框架:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.newcj.test;


import android.app.Service;

import android.content.Intent;

import android.os.IBinder;


public class LocalService1 extends Service {


    /**

     * onBind 是 Service 的虚方法,因此我们不得不实现它。

     * 返回 null,表示客服端不能建立到此服务的连接。

     */

    @Override

    public IBinder onBind(Intent intent) {

        return null;

    }


    @Override

    public void onCreate() {

        super.onCreate();

    }


    @Override

    public void onStart(Intent intent, int startId) {

        super.onStart(intent, startId);

    }


    @Override

    public void onDestroy() {

        super.onDestroy();

    }


}


对应生命周期系统回调函数上面已经说明,在对应地方加上适当的代码即可。下面是启动与停止 Service 的代码:
1
2
3
4
5
// 启动一个 Activity

startActivity(new Intent(this, LocalService1.class));

...

// 停止一个 Activity

stopService(new Intent(this, LocalService1.class));


对应的 Intent 为标志服务类的 Intent。

5、Local 与 Remote 服务绑定

同样记得在 Androidmanifest.xml 中注册 service
1). Local 服务绑定:Local 服务的绑定较简单,首先在 Service 中我们需要实现 Service 的抽象方法 onBind,并返回一个实现 IBinder 接口的对象。
Service 中的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.newcj.test;


import android.app.Service;

import android.content.Intent;

import android.os.Binder;

import android.os.IBinder;


public class LocalService extends Service {


    /**

     * 在 Local Service 中我们直接继承 Binder 而不是 IBinder,因为 Binder 实现了 IBinder 接口,这样我们可以少做很多工作。

     * @author newcj

     */

    public class SimpleBinder extends Binder{

        /**

         * 获取 Service 实例

         * @return

         */

        public LocalService getService(){

            return LocalService.this;

        }


        public int add(int a, int b){

            return a + b;

        }

    }


    public SimpleBinder sBinder;


    @Override

    public void onCreate() {

        super.onCreate();

        // 创建 SimpleBinder

        sBinder = new SimpleBinder();

    }


    @Override

    public IBinder onBind(Intent intent) {

        // 返回 SimpleBinder 对象

        return sBinder;

    }


}


上面的代码关键之处,在于 onBind(Intent) 这个方法 返回了一个实现了 IBinder 接口的对象,这个对象将用于绑定Service 的 Activity 与 Local Service 通信。下面是 Activity 中的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.newcj.test;


import android.app.Activity;

import android.content.ComponentName;

import android.content.Context;

import android.content.Intent;

import android.content.ServiceConnection;

import android.os.Bundle;

import android.os.IBinder;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;


public class Main extends Activity {

    private final static String TAG = "SERVICE_TEST";

    private ServiceConnection sc;

    private boolean isBind;


    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        sc = new ServiceConnection() {


            @Override

            public void onServiceDisconnected(ComponentName name) {


            }


            @Override

            public void onServiceConnected(ComponentName name, IBinder service) {

                LocalService.SimpleBinder sBinder = (LocalService.SimpleBinder)service;

                Log.v(TAG, "3 + 5 = " + sBinder.add(3, 5));

                Log.v(TAG, sBinder.getService().toString());

            }

        };

        findViewById(R.id.btnBind).setOnClickListener(new OnClickListener() {


            @Override

            public void onClick(View v) {

                bindService(new Intent(Main.this, LocalService.class), sc, Context.BIND_AUTO_CREATE);

                isBind = true;

            }

        });

        findViewById(R.id.btnUnbind).setOnClickListener(new OnClickListener() {


            @Override

            public void onClick(View v) {

                if(isBind){

                    unbindService(sc);

                    isBind = false;

                }

            }

        });

    }

}


在 Activity 中,我们通过 ServiceConnection 接口来取得建立连接 与 连接意外丢失的回调。bindService有三个参数,第一个是用于区分 Service 的Intent 与 startService 中的 Intent 一致,第二个是实现了 ServiceConnection 接口的对象,最后一个是 flag 标志位。有两个flag,BIND_DEBUG_UNBIND 与 BIND_AUTO_CREATE,前者用于调试(详细内容可以查看javadoc 上面描述的很清楚),后者默认使用。unbindService 解除绑定,参数则为之前创建的 ServiceConnection 接口对象。另外,多次调用 unbindService 来释放相同的连接会抛出异常,因此我创建了一个 boolean 变量来判断是否 unbindService 已经被调用过。
运行结果:

2). Remote 服务绑定:Remote 的服务绑定由于服务是在另外一个进程,因此需要用到 android 的 IPC 机制。这将又是一个很长的话题,因此,我打算写另外一篇 android 的 IPC 机制分析 ,并在其中进行详述,然后在这里更新链接,敬请关注。

特别注意:
1、Service.onBind如果返回null,则调用 bindService 会启动 Service,但不会连接上 Service,因此 ServiceConnection.onServiceConnected 不会被调用,但你任然需要使用 unbindService 函数断开它,这样 Service 才会停止。

6、创建前台服务

前台服务的优点上面已经说明,但设置服务为前台服务,我们需要注意在 sdk 2.0 及其以后版本使用的方法是 startForeground 与 stopForeground,之前版本使用的是 setForeground ,因此如果你应用程序的最低运行环境要求是 2.0,那么这里可以直接运用新方法,如果运行环境是2.0以下,那么为了保证向后兼容性,这里必须使用反射技术来调用新方法。
下面是我仿照 ApiDemos 重新敲的代码,对某些地方进行了修改,因此更具有说明性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package com.newcj.test;


import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;


import android.app.Notification;

import android.app.NotificationManager;

import android.app.PendingIntent;

import android.app.Service;

import android.content.Context;

import android.content.Intent;

import android.os.IBinder;


public class ForegroundService extends Service {


    private static final Class[] mStartForegroundSignature = new Class[] {

        int.class, Notification.class};

    private static final Class[] mStopForegroundSignature = new Class[] {

        boolean.class};

    private NotificationManager mNM;

    private Method mStartForeground;

    private Method mStopForeground;

    private Object[] mStartForegroundArgs = new Object[2];

    private Object[] mStopForegroundArgs = new Object[1];


    @Override

    public IBinder onBind(Intent intent) {

        return null;

    }


    @Override

    public void onCreate() {

        super.onCreate();

        mNM = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

        try {

            mStartForeground = ForegroundService.class.getMethod("startForeground", mStartForegroundSignature);

            mStopForeground = ForegroundService.class.getMethod("stopForeground", mStopForegroundSignature);

        } catch (NoSuchMethodException e) {

            mStartForeground = mStopForeground = null;

        }

         // 我们并不需要为 notification.flags 设置 FLAG_ONGOING_EVENT,因为

         // 前台服务的 notification.flags 总是默认包含了那个标志位

        Notification notification = new Notification(R.drawable.icon, "Foreground Service Started.",

                System.currentTimeMillis());

        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,

                new Intent(this, Main.class), 0);

        notification.setLatestEventInfo(this, "Foreground Service",

                "Foreground Service Started.", contentIntent);

        // 注意使用  startForeground ,id 为 0 将不会显示 notification

        startForegroundCompat(1, notification);

    }


    @Override

    public void onDestroy() {

        super.onDestroy();

        stopForegroundCompat(1);

    }


    // 以兼容性方式开始前台服务

    private void startForegroundCompat(int id, Notification n){

        if(mStartForeground != null){

            mStartForegroundArgs[0] = id;

            mStartForegroundArgs[1] = n;


            try {

                mStartForeground.invoke(this, mStartForegroundArgs);

            } catch (IllegalArgumentException e) {

                e.printStackTrace();

            } catch (IllegalAccessException e) {

                e.printStackTrace();

            } catch (InvocationTargetException e) {

                e.printStackTrace();

            }


            return;

        }

        setForeground(true);

        mNM.notify(id, n);

    }


    // 以兼容性方式停止前台服务

    private void stopForegroundCompat(int id){

        if(mStopForeground != null){

            mStopForegroundArgs[0] = Boolean.TRUE;


            try {

                mStopForeground.invoke(this, mStopForegroundArgs);

            } catch (IllegalArgumentException e) {

                e.printStackTrace();

            } catch (IllegalAccessException e) {

                e.printStackTrace();

            } catch (InvocationTargetException e) {

                e.printStackTrace();

            }

            return;

        }


        //  在 setForeground 之前调用 cancel,因为我们有可能在取消前台服务之后

        //  的那一瞬间被kill掉。这个时候 notification 便永远不会从通知一栏移除

        mNM.cancel(id);

        setForeground(false);

    }


}


特别注意:
  1、使用 startForeground ,如果 id 为 0 ,那么 notification 将不会显示。

7、在什么情况下使用 startService 或 bindService 或 同时使用startService 和 bindService

如果你只是想要启动一个后台服务长期进行某项任务那么使用 startService 便可以了。如果你想要与正在运行的 Service 取得联系,那么有两种方法,一种是使用 broadcast ,另外是使用 bindService ,前者的缺点是如果交流较为频繁,容易造成性能上的问题,并且 BroadcastReceiver 本身执行代码的时间是很短的(也许执行到一半,后面的代码便不会执行),而后者则没有这些问题,因此我们肯定选择使用 bindService(这个时候你便同时在使用 startService 和 bindService 了,这在 Activity 中更新 Service 的某些运行状态是相当有用的)。另外如果你的服务只是公开一个远程接口,供连接上的客服端(android 的 Service 是C/S架构)远程调用执行方法。这个时候你可以不让服务一开始就运行,而只用 bindService ,这样在第一次 bindService 的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是Remote Service,那么该效果会越明显(当然在 Service 创建的时候会花去一定时间,你应当注意到这点)。


8、在 AndroidManifest.xml 里 Service 元素的常见选项
android:name  -------------  服务类名
android:label  --------------  服务的名字,如果此项不设置,那么默认显示的服务名则为类名
android:icon  --------------  服务的图标
android:permission  -------  申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务
android:process  ----------  表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字
android:enabled  ----------  如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false
android:exported  ---------  表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false



你可能感兴趣的:(Android)