小猪的Android入门之路 Day 9 part 1

Android四大组件之——Service浅析

                                                 ——转账请注明出处:coder-pig


本节引言:


在前面的学习中我们已经把安卓四个基本组件中的两个:

Actvity(活动)和BroadCastReceiver过了一遍,而在Day 9中我们会对第三个组件Service进行

解析,两种类型的Service,Service的生命周期,如何去使用Service,声明Service,调用,停止Service;

跨进程调用AIDL,以及常用的系统服务的使用!好了,引言就说到这里,接着开始本节的内容!



本节学习路线图:

小猪的Android入门之路 Day 9 part 1_第1张图片



正文:


Service简介与生命周期图解析:


小猪的Android入门之路 Day 9 part 1_第2张图片



代码验证生命周期图:

1.验证StartService启动Service的调用顺序:

首先我们自定义一个Service,重写相关的方法,用户在logcat上打印验证:

TestService1.java

[java]   view plain copy print ?
  1. package com.jay.example.servicetestdemo1;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6. import android.util.Log;  
  7.   
  8. public class TestService1 extends Service {  
  9.   
  10.     private final String TAG = "TestService1";    
  11.     //必须要实现的方法  
  12.     @Override  
  13.     public IBinder onBind(Intent intent) {  
  14.         Log.i(TAG, "onBind方法被调用!");  
  15.         return null;  
  16.     }  
  17.   
  18.     //Service被创建时调用  
  19.     @Override  
  20.     public void onCreate() {  
  21.         Log.i(TAG, "onCreate方法被调用!");  
  22.         super.onCreate();  
  23.     }  
  24.       
  25.     //Service被启动时调用  
  26.     @Override  
  27.     public int onStartCommand(Intent intent, int flags, int startId) {  
  28.         Log.i(TAG, "onStartCommand方法被调用!");  
  29.         return super.onStartCommand(intent, flags, startId);  
  30.     }  
  31.       
  32.       
  33.       
  34.     //Service被关闭之前回调  
  35.     @Override  
  36.     public void onDestroy() {  
  37.         Log.i(TAG, "onDestory方法被调用!");  
  38.         super.onDestroy();  
  39.     }  
  40. }  

接着在AndroidManifest.xml完成Service组件的注册:

[html]   view plain copy print ?
  1.   
  2.         <service android:name=".TestService1">  
  3.             <intent-filter>  
  4.                 <action android:name="com.jay.example.service.TEST_SERVICE1"/>  
  5.             intent-filter>  
  6. service>  


再接着是简单的布局文件,两个按钮,再最后是MainActivity的编写,在按钮的点击事件中分别

调用startService( )stopService( )!

[java]   view plain copy print ?
  1. package com.jay.example.servicetestdemo1;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.os.Bundle;  
  6. import android.view.View;  
  7. import android.view.View.OnClickListener;  
  8. import android.widget.Button;  
  9.   
  10.   
  11. public class MainActivity extends Activity {  
  12.   
  13.     private Button start;  
  14.     private Button stop;  
  15.       
  16.     @Override  
  17.     protected void onCreate(Bundle savedInstanceState) {  
  18.         super.onCreate(savedInstanceState);  
  19.         setContentView(R.layout.activity_main);  
  20.           
  21.         start = (Button) findViewById(R.id.btnstart);  
  22.         stop = (Button) findViewById(R.id.btnstop);  
  23.           
  24.         //创建启动Service的Intent,以及Intent属性  
  25.         final Intent intent = new Intent();  
  26.         intent.setAction("com.jay.example.service.TEST_SERVICE1");  
  27.         //为两个按钮设置点击事件,分别是启动与停止service  
  28.         start.setOnClickListener(new OnClickListener() {              
  29.             @Override  
  30.             public void onClick(View v) {  
  31.                 startService(intent);                 
  32.             }  
  33.         });  
  34.           
  35.         stop.setOnClickListener(new OnClickListener() {           
  36.             @Override  
  37.             public void onClick(View v) {  
  38.                 stopService(intent);  
  39.                   
  40.             }  
  41.         });  
  42.           
  43.     }  
  44. }  

运行截图:

小猪的Android入门之路 Day 9 part 1_第3张图片

点击开始服务:


吃饱饭没事做,点多几下:


最后点击停止服务:



从上面的运行结果我们可以验证我们生命周期图中解释的内容:

我们发现onBind()方法并没有被调用,另外多次点击启动Service,只会重复地调用

onStartCommand方法!无论我们启动多少次Service,一个stopService就会停止

Service!



2.验证BindService启动Service的顺序:

在开始讲写代码之前,我们先要来了解一些东西先:

首先是第一个大图下面给出的Context的bindService方法

①ServiceConnection对象:监听访问者与Service间的连接情况,如果成功连接,回调

onServiceConnected(),如果异常终止或者其他原因终止导致Service与访问者断开

连接则回调onServiceDisconnected方法,调用unBindService()不会调用该方法!


②onServiceConnected方法中有一个IBinder对象,该对象即可实现与被绑定Service

之间的通信!我们再开发Service类时,默认需要实现IBinder onBind()方法,该方法返回的

IBinder对象会传到ServiceConnection对象中的onServiceConnected的参数,我们就可以

在这里通过这个IBinder与Service进行通信!


总结:

step 1:在自定义的Service中继承Binder,实现自己的IBinder对象

step 2:通过onBind( )方法返回自己的IBinder对象

step 3:在绑定该Service的类中定义一个ServiceConnection对象,重写两个方法,

onServiceConnected和onDisconnected!然后直接读取IBinder传递过来的参数即可!



那么好了,接下来就是写代码验证了,这里的话我们定义一个用来计时的Service,

然后来演示BindService的用法以及方法调用流程!代码比较简单,不解释了!

TestService2.java:

[java]   view plain copy print ?
  1. package com.jay.example.servicetestdemo2;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.os.Binder;  
  6. import android.os.IBinder;  
  7. import android.util.Log;  
  8.   
  9. public class TestService2 extends Service {  
  10.   
  11.     private final String TAG = "TestService2";  
  12.     private int count;  
  13.     private boolean quit;  
  14.       
  15.     //定义onBinder方法所返回的对象  
  16.     private MyBinder binder = new MyBinder();  
  17.     public class MyBinder extends Binder  
  18.     {  
  19.         public int getCount()  
  20.         {  
  21.             return count;  
  22.         }  
  23.     }  
  24.       
  25.     //必须实现的方法,绑定改Service时回调该方法  
  26.     @Override  
  27.     public IBinder onBind(Intent intent) {  
  28.         Log.i(TAG, "onBind方法被调用!");  
  29.         return binder;  
  30.     }  
  31.   
  32.       
  33.     //Service被创建时回调  
  34.     @Override  
  35.     public void onCreate() {  
  36.         super.onCreate();  
  37.         Log.i(TAG, "onCreate方法被调用!");  
  38.         //创建一个线程动态地修改count的值  
  39.         new Thread()  
  40.         {  
  41.             public void run()   
  42.             {  
  43.                 while(!quit)  
  44.                 {  
  45.                     try  
  46.                     {  
  47.                         Thread.sleep(1000);  
  48.                     }catch(InterruptedException e){e.printStackTrace();}  
  49.                     count++;  
  50.                 }  
  51.             };  
  52.         }.start();  
  53.           
  54.     }  
  55.       
  56.     //Service断开连接时回调  
  57.     @Override  
  58.     public boolean onUnbind(Intent intent) {  
  59.         Log.i(TAG, "onUnbind方法被调用!");  
  60.         return true;  
  61.     }  
  62.       
  63.     //Service被关闭前回调  
  64.     @Override  
  65.     public void onDestroy() {  
  66.         super.onDestroy();  
  67.         this.quit = true;  
  68.         Log.i(TAG, "onDestroyed方法被调用!");  
  69.     }  
  70.       
  71.     @Override  
  72.     public void onRebind(Intent intent) {  
  73.         Log.i(TAG, "onRebind方法被调用!");  
  74.         super.onRebind(intent);  
  75.     }  
  76.       
  77. }  

需要在AndroidManifest.xml中对Service组件进行注册:

[html]   view plain copy print ?
  1. <service android:name=".TestService2" android:exported="false">  
  2.             <intent-filter>  
  3.                 <action android:name="com.jay.example.service.TEST_SERVICE2"/>  
  4.             intent-filter>  
  5.  service>  

MainActivity.java

[java]   view plain copy print ?
  1. package com.jay.example.servicetestdemo2;  
  2.   
  3. import com.jay.example.servicetestdemo2.TestService2.MyBinder;  
  4.   
  5. import android.app.Activity;  
  6. import android.app.Service;  
  7. import android.content.ComponentName;  
  8. import android.content.Intent;  
  9. import android.content.ServiceConnection;  
  10. import android.os.Bundle;  
  11. import android.os.IBinder;  
  12. import android.view.View;  
  13. import android.view.View.OnClickListener;  
  14. import android.widget.Button;  
  15. import android.widget.Toast;  
  16.   
  17.   
  18. public class MainActivity extends Activity {  
  19.   
  20.     private Button btnbind;  
  21.     private Button btncancel;  
  22.     private Button btnstatus;  
  23.       
  24.     //保持所启动的Service的IBinder对象,同时定义一个ServiceConnection对象  
  25.     TestService2.MyBinder binder;  
  26.     private ServiceConnection conn = new ServiceConnection() {  
  27.           
  28.         //Activity与Service断开连接时回调该方法  
  29.         @Override  
  30.         public void onServiceDisconnected(ComponentName name) {  
  31.             System.out.println("------Service DisConnected-------");  
  32.         }  
  33.           
  34.         //Activity与Service连接成功时回调该方法  
  35.         @Override  
  36.         public void onServiceConnected(ComponentName name, IBinder service) {  
  37.             System.out.println("------Service Connected-------");  
  38.             binder = (TestService2.MyBinder) service;  
  39.         }  
  40.     };  
  41.       
  42.     @Override  
  43.     protected void onCreate(Bundle savedInstanceState) {  
  44.         super.onCreate(savedInstanceState);  
  45.         setContentView(R.layout.activity_main);  
  46.         btnbind = (Button) findViewById(R.id.btnbind);  
  47.         btncancel = (Button) findViewById(R.id.btncancel);  
  48.         btnstatus  = (Button) findViewById(R.id.btnstatus);  
  49.           
  50.         final Intent intent = new Intent();  
  51.         intent.setAction("com.jay.example.service.TEST_SERVICE2");  
  52.           
  53.         btnbind.setOnClickListener(new OnClickListener() {            
  54.             @Override  
  55.             public void onClick(View v) {  
  56.                 //绑定service  
  57.                 bindService(intent, conn, Service.BIND_AUTO_CREATE);                  
  58.             }  
  59.         });  
  60.           
  61.         btncancel.setOnClickListener(new OnClickListener() {  
  62.             @Override  
  63.             public void onClick(View v) {  
  64.                 //解除service绑定  
  65.                 unbindService(conn);                  
  66.             }  
  67.         });  
  68.           
  69.         btnstatus.setOnClickListener(new OnClickListener() {  
  70.               
  71.             @Override  
  72.             public void onClick(View v) {  
  73.                 Toast.makeText(getApplicationContext(), "Service的count的值为:"  
  74.                         + binder.getCount(), Toast.LENGTH_SHORT).show();  
  75.                   
  76.             }  
  77.         });  
  78.     }  
  79. }  


运行截图:

小猪的Android入门之路 Day 9 part 1_第4张图片

点击锁定Service:


继续点击锁定:没任何变化


获取当前Service的状态:


解除绑定:

小猪的Android入门之路 Day 9 part 1_第5张图片

如果我们再绑定后直接关掉Activity的话会报错,

然后会自动调用onUnbind和onDestory方法!

小猪的Android入门之路 Day 9 part 1_第6张图片

。。。




从上面的运行结果验证了生命周期图中的:

使用BindService绑定Service,依次调用onCreate(),onBind()方法,我们可以在onBind()方法中

返回自定义的IBinder对象;再接着调用的是ServiceConnectiononServiceConnected()方法

该方法中可以获得IBinder对象,从而进行相关操作;当Service解除绑定后会自动调用onUnbind

onDestroyed方法,当然绑定多客户端情况需要解除所有的绑定才会调用onDestoryed方法进行销毁哦!




IntentService的使用:



上面已经学习了Service的用法,现在我们已经知道如何去定义和启动自己的Service了!

但是从上面的bindService的例子中,发现了一个问题,就是我们直接把耗时线程放在了

Service中的onStart( )方法中,网上很多都是直接这样做!但是这样容易引发ANR异常

(Application Not Responding),而Android的官方是这样介绍Service的:


1.A Service is not a separate process. The Service object itself does not imply it is running

 in its own process; unless otherwise specified, it runs in the same process as the application it is part of.

2.A Service is not a thread. It is not a means itself to do work off of the main thread

 (to avoid Application Not Responding errors).


直接翻译就是:

1.Service不是一个单独的进程,它和它的应用程序在同一个进程中

2.Service不是一个线程,这样就意味着我们应该避免在Service中进行耗时操作


于是乎肯定是有替代Service的东西啦,那就是我们要讲的IntentService

IntentService是继承与Service并处理异步请求的一个类,在IntentService中有

一个工作线程来处理耗时操作,请求的Intent记录会加入队列

工作流程:

客户端通过startService(Intent)来启动IntentService;

我们并不需要手动地区控制IntentService,当任务执行完后,IntentService会自动停止;

可以启动IntentService多次,每个耗时操作会以工作队列的方式在IntentService的

onHandleIntent回调方法中执行,并且每次只会执行一个工作线程,执行完一,再到二这样!


总结使用IntentService的原因:

1)无需在Service中手动地去开辟线程

2)无需手动停止Service,当操作完成时,Service会自动停止

3)简单的使用方式


再接着是代码演示,网上大部分的代码都是比较Service与IntentService的,定义足够长的

休眠时间,演示Service的ANR异常,然后引出IntentService有多好!

这里就不演示Service了,网上的都是自定义Service,然后在onStart()方法中Thread.sleep(20000)

然后引发ANR异常,有兴趣的可以自己写代码试试,这里的话只演示下IntentService的用法


首先自定义一个Service,继承IntentService,重写核心方法onHandleIntent,在这里完成耗时操作

接着重写其他方法,添加log.i用于查看方法的调用顺序!


TestService3.java

[java]   view plain copy print ?
  1. package com.com.example.testservice3;  
  2.   
  3. import android.app.IntentService;  
  4. import android.content.Intent;  
  5. import android.os.IBinder;  
  6. import android.util.Log;  
  7.   
  8. public class TestService3 extends IntentService {  
  9.   
  10.     private final String TAG = "hehe";  
  11.   
  12.     //必须实现父类的构造方法  
  13.     public TestService3()  
  14.     {  
  15.         super("TestService3");  
  16.     }  
  17.   
  18.       
  19.     //必须重写的核心方法  
  20.     @Override  
  21.     protected void onHandleIntent(Intent intent) {  
  22.         //Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务  
  23.         String action = intent.getExtras().getString("param");  
  24.         if(action.equals("s1"))Log.i(TAG,"启动service1");  
  25.         else if(action.equals("s2"))Log.i(TAG,"启动service2");  
  26.         else if(action.equals("s3"))Log.i(TAG,"启动service3");  
  27.           
  28.         //让服务休眠2秒  
  29.         try{  
  30.             Thread.sleep(2000);  
  31.         }catch(InterruptedException e){e.printStackTrace();}          
  32.     }  
  33.   
  34.       
  35.       
  36.     //重写其他方法,用于查看方法的调用顺序  
  37.     @Override  
  38.     public IBinder onBind(Intent intent) {  
  39.         Log.i(TAG,"onBind");  
  40.         return super.onBind(intent);  
  41.     }  
  42.   
  43.   
  44.     @Override  
  45.     public void onCreate() {  
  46.         Log.i(TAG,"onCreate");  
  47.         super.onCreate();  
  48.     }  
  49.   
  50.     @Override  
  51.     public int onStartCommand(Intent intent, int flags, int startId) {  
  52.         Log.i(TAG,"onStartCommand");  
  53.         return super.onStartCommand(intent, flags, startId);  
  54.     }  
  55.   
  56.   
  57.     @Override  
  58.     public void setIntentRedelivery(boolean enabled) {  
  59.         super.setIntentRedelivery(enabled);  
  60.         Log.i(TAG,"setIntentRedelivery");  
  61.     }  
  62.       
  63.     @Override  
  64.     public void onDestroy() {  
  65.         Log.i(TAG,"onDestroy");  
  66.         super.onDestroy();  
  67.     }  
  68.       
  69. }  

接着记得到AndroidManifest中注册小Service组件哦,不然Service是没响应的哦!

[html]   view plain copy print ?
  1. <service android:name=".TestService3" android:exported="false">  
  2.     <intent-filter >  
  3.         <action android:name="com.test.intentservice"/>  
  4.     intent-filter>  
  5. service>  

最后在MainActivity中启动三次服务

[java]   view plain copy print ?
  1. package com.com.example.testservice3;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.os.Bundle;  
  6. import android.view.Menu;  
  7. import android.view.MenuItem;  
  8.   
  9. public class MainActivity extends Activity {  
  10.   
  11.     @Override  
  12.     protected void onCreate(Bundle savedInstanceState) {  
  13.         super.onCreate(savedInstanceState);  
  14.         setContentView(R.layout.activity_main);  
  15.           
  16.         Intent it1 = new Intent("com.test.intentservice");  
  17.         Bundle b1 = new Bundle();  
  18.         b1.putString("param""s1");  
  19.         it1.putExtras(b1);  
  20.           
  21.         Intent it2 = new Intent("com.test.intentservice");  
  22.         Bundle b2 = new Bundle();  
  23.         b2.putString("param""s2");  
  24.         it2.putExtras(b2);  
  25.           
  26.         Intent it3 = new Intent("com.test.intentservice");  
  27.         Bundle b3 = new Bundle();  
  28.         b3.putString("param""s3");  
  29.         it3.putExtras(b3);  
  30.           
  31.         //接着启动多次IntentService,每次启动,都会新建一个工作线程  
  32.         //但始终只有一个IntentService实例  
  33.         startService(it1);  
  34.         startService(it2);  
  35.         startService(it3);  
  36.           
  37.     }  
  38. }  

然后看下运行的截图:

小猪的Android入门之路 Day 9 part 1_第7张图片



好了,最后总结下,当一个后台的任务,需要分成几个子任务,然后按先后顺序执行,子任务

(简单的说就是异步操作),此时如果我们还是定义一个普通Service然后在onStart方法中

开辟线程,然后又要去控制线程,这样显得非常的繁琐;

此时应该自定义一个IntentService然后再onHandleIntent()方法中完成相关任务!







本节参考代码下载:

1)验证StartService生命周期:点击下载

2)验证BindService生命周期:点击下载

3)IntentService的简单使用:点击下载

你可能感兴趣的:(小猪的Android入门之路 Day 9 part 1)