Android组件系列----AndroidService组件深入解析

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Android组件系列----AndroidService组件深⼊解析
【声明】
欢迎转载,但请保留⽂章原始出处→_→
【前⾔】
花了周末两天的时间,整理了⼀下作为Android四⼤组件之⼀的Service的基础知识,通过这篇⽂章,应该可以明⽩:对Service的理解、在什么地⽅使⽤、怎么使⽤、要注意哪些问题等。

【本⽂主要内容】
⼀、Service的基本概念(四⼤组件之⼀)
⼆、定义(启动)⼀个Service
1、如何定义(启动)⼀个Service:
2、停⽌⼀个started服务有两种⽅法
3、onStartCommand⽅法的返回值
三、IntentService
1、IntentService的引⼊
2、IntentService的作⽤
3、IntentService的⽤法
4、Service和Thread的关系
四、使⽤Bind Service完成Service和Activity之间的通信
1、Bind Service的介绍
2、实现Service和Activity之间通信步骤
3、started服务与bind服务的区别
4、Service的⽣命周期
五、使⽤Bind Service完成IPC进程间通信:(在同⼀个APP内模拟)
1、Bind Service的介绍
2、在客户端绑定⼀个服务的步骤
3、IPC(Inter-Process Communication)进程间通信机制
4、AIDL(Android Interface Definition Language)Android接⼝定义语⾔
5、IPC(进程间通讯)具体的步骤如下
6、让Activity与⼀个远程Service建⽴关联的步骤:(在同⼀个APP内模拟)
7、AIDL⽀持的⾃定义数据类型
六、使⽤Bind Service完成IPC进程间通信:(两个APP之间)
七、Messenger的使⽤
【正⽂】
⼀、Service的基本概念(四⼤组件之⼀)
Service是Android中实现程序后台运⾏的解决⽅案,⾮常适合⽤于去执⾏哪些不需要和⽤户交互⽽且还要求长期运⾏的任务。

不能运⾏在⼀个独⽴的进程当中,⽽是依赖与创建服务时所在的应⽤程序进程。

只能在后台运⾏,并且可以和其他组件进⾏交互。

Service可以在很多场合使⽤,⽐如播放多媒体的时候⽤户启动了其他Activity,此时要在后台继续播放;⽐如检测SD卡上⽂件的变化;⽐如在后台记录你的地理信息位置的改变等等,总之服务是藏在后台的。

服务不会⾃动开启线程,我们需要在服务的内部⼿动创建⼦线程,并在这⾥执⾏具体的任务。

关于多线程的知识:可以参考另外⼀篇⽂章:
⼆、定义(启动)⼀个Service:
1、如何定义(启动)⼀个Service:
核⼼步骤如下:
创建⼀个类继承android.app.Service类,实现抽象⽅法onBind(),重写onCreate()、onStartCommand()、onDestry();
在清单⽂件中配置Service。

新建⼀个Android项⽬ServiceTest,具体步骤如下:
(1)新建⼀个MyService类,继承⾃Service,并重写⽗类的onCreate()、onStartCommand()和onDestroy()⽅法,代码如下:
1package com.example.servicetest;
2
3import android.app.Service;
4import android.content.Intent;
5import android.os.IBinder;
6import android.util.Log;
7
8public class MyService extends Service {
9
10public static final String TAG = "MyService";
11
12//创建服务时调⽤
13 @Override
14public void onCreate() {
15super.onCreate();
16 Log.d(TAG, "onCreate");
17 }
18
19//服务执⾏的操作
20 @Override
21public int onStartCommand(Intent intent, int flags, int startId) {
22 Log.d(TAG, "onStartCommand");
23return super.onStartCommand(intent, flags, startId);
24 }
25
26//销毁服务时调⽤
27 @Override
28public void onDestroy() {
29super.onDestroy();
30 Log.d(TAG, "onDestroy");
31 }
32
33 @Override
34public IBinder onBind(Intent intent) {
35return null;
36 }
37 }
可以看到,我们只是在onCreate()、onStartCommand()和onDestroy()⽅法中分别打印了⼀句话,并没有进⾏其它任何的操作,注意代码注释中这三个⽅法的作⽤。

onBind()⽅法是Service中唯⼀的⼀个抽象⽅法,所以必须要在⼦类⾥实现。

我们知道,Service可以有两种启动⽅式:⼀种是startService(),另⼀种是bindService()。

第⼆种启动⽅式才会⽤到onBind()⽅法。

我们这先⽤第⼀种⽅式启动Service,所以暂时忽略onBind()⽅法。

(2)在清单⽂件中声明:(和Activity标签并列)
<service android:name=".MyService"></service>
(3)修改activity_main.xml代码,如下:
<LinearLayout xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button1_start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Service"/>
<Button
android:id="@+id/button2_stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service"/>
</LinearLayout>
我们在布局⽂件中加⼊了两个按钮,⼀个⽤于启动Service,⼀个⽤于停⽌Service。

(4)在MainActivity作为程序的主Activity,在⾥⾯加⼊启动Service和停⽌Service的逻辑,代码如下:
1package com.example.servicetest;
2
3import android.app.Activity;
4import android.content.Intent;
5import android.os.Bundle;
6import android.view.View;
7import android.view.View.OnClickListener;
8import android.widget.Button;
9
10
11public class MainActivity extends Activity implements OnClickListener {
12
13private Button button1_start_service;
14
15private Button button2_stop_service;
16
17 @Override
18protected void onCreate(Bundle savedInstanceState) {
19super.onCreate(savedInstanceState);
20 setContentView(yout.activity_main);
21 button1_start_service = (Button) findViewById(R.id.button1_start_service);
22 button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
23 button1_start_service.setOnClickListener(this);
24 button2_stop_service.setOnClickListener(this);
25 }
26
27 @Override
28public void onClick(View v) {
29switch (v.getId()) {
30case R.id.button1_start_service:
31 Intent startIntent = new Intent(this, MyService.class);
32 startService(startIntent);
33break;
34case R.id.button2_stop_service:
35 Intent stopIntent = new Intent(this, MyService.class);
36 stopService(stopIntent);
37break;
38default:
39break;
40 }
41 }
42
43 }
核⼼代码:31⾏⾄32⾏、35⾏⾄36⾏。

可以看到,在Start Service按钮的点击事件⾥,我们构建出了⼀个Intent对象,并调⽤startService()⽅法来启动MyService。

然后在Stop Serivce按钮的点击事件⾥,我们同样构建出了⼀个Intent对象,并调⽤stopService()⽅法来停⽌MyService。

代码的逻辑⾮常简单。

这样的话,⼀个简单的带有Service功能的程序就写好了。

启动和停⽌服务:
定义好服务之后,接下来看⼀下如何启动和停⽌⼀个服务,这主要是借助Intent来实现的。

注意startService()和stopService()⽅法都是定义在Context类当中的,所以可以在MainActivity中直接调⽤这两个⽅法。

运⾏上⾯的程序,点击button1_start_service按钮,启动服务,后台打印⽇志如下:
说明服务启动成功。

那么如果我再连续点三次button1_start_service按钮,后台增加的⽇志如下:
事实上,onCreate()⽅法只会在Service第⼀次被创建的时候调⽤,⽽onStartCommand()⽅法在每次启动服务的时候都会调⽤。

我们还可以在正在“设置--应⽤---运⾏”中找到这个服务,如下图所⽰:
点开上图中的红框部分,可以看到:
如果我们再点击button2_stop_service按钮或者点击上图中的“Stop”,MyService服务就停⽌掉了:
需要注意的是:
服务对象同时只会有⼀个
默认情况下,⼀个started的Service与启动他的组件在同⼀个线程中。

上⾯的实例中,服务就是在主线程中运⾏的,如果是在服务中完成耗时操作的话,容易造成主线程阻塞。

2、停⽌⼀个started服务有两种⽅法:
(1)在外部使⽤stopService()
(2)在服务内部(onStartCommand⽅法内部)使⽤stopSelf()⽅法。

3、onStartCommand⽅法的返回值:
onStartCommand⽅法执⾏时,返回的是⼀个int型。

这个整型可以有三个返回值:START_NOT_STICKY、START_STICKY、
START_REDELIVER_INTENT
START_NOT_STICKY:“⾮粘性的”。

使⽤这个返回值时,如果在执⾏完onStartCommand⽅法后,服务被异常kill掉,系统不会⾃动重启该服务。

START_STICKY:如果Service进程被kill掉,保留Service的状态为开始状态,但不保留递送的intent对象。

随后系统会尝试重新创建Service,由于服务状态为开始状态,所以创建服务后⼀定会调⽤onStartCommand(Intent,int,int)⽅法。

如果在此期间没有任何启动命令被传递到Service,那么参数Intent将为null。

START_REDELIVER_INTENT:重传Intent。

使⽤这个返回值时,系统会⾃动重启该服务,并将Intent的值传⼊。

三、IntentService
1、IntentService的引⼊:
我们在第⼀段中就已经说了,服务中的代码默认运⾏在主线程中,如果直接在服务⾥执⾏⼀些耗时操作,容易造成ANR(Application Not Responding)异常,所以就需要⽤到多线程的知识了。

因此⼀个⽐较标准的服务可以这样写:
1package com.example.servicetest;
2
3import android.app.Service;
4import android.content.Intent;
5import android.os.IBinder;
6
7public class MyService extends Service {
8
9public static final String TAG = "MyService";
10
11//服务执⾏的操作
12 @Override
13public int onStartCommand(Intent intent, int flags, int startId) {
14new Thread(new Runnable() {
15public void run() {
16//处理具体的逻辑
17 stopSelf(); //服务执⾏完毕后⾃动停⽌
18 }
19 }).start();
20return super.onStartCommand(intent, flags, startId);
21 }
22
23 @Override
24public IBinder onBind(Intent intent) {
25// TODO Auto-generated method stub
26return null;
27 }
28
29 }
核⼼代码:14⾄19⾏,在⼦线程中处理具体的逻辑。

需要注意的是,如果没有第17⾏的stopSelf(),服务⼀旦启动后,就会⼀直处于运⾏状态,必须调⽤stopService()或者stopSelf()⽅法才能让服务停⽌下来;所以我们添加了17⾏的stopSelf(),服务执⾏完毕后会⾃动停⽌。

虽说上⾯的这种写法并不复杂,但总会有⼀些程序猿忘记开启线程,或者忘记调⽤stopSelf()⽅法。

为了可以简单地创建⼀个异步的、会⾃动停⽌的服务,Android专门提供了⼀个IntentService类,这个类就很好的解决了上⾯所提到的两种尴尬。

另外,可以启动IntentService多次,⽽每⼀个耗时操作会以⼯作队列的⽅式在IntentService的onHandleIntent()回调⽅法中执⾏,并且每次只会执⾏⼀个⼯作线程,执⾏完第⼀个后,再执⾏第⼆个,以此类推。

2、IntentService的作⽤:
当我们需要这样⼀次性完成的任务时,就可以使⽤IntentService来完成。

3、IntentService的⽤法:
我们在上⾯的项⽬ServiceTest基础上进⾏修改,步骤如下:
(1)新建⼀个MyIntentService类,继承⾃IntentService,并重写⽗类的onHandleIntent()⽅法,代码如下:
1package com.example.servicetest;
2
3import android.app.IntentService;
4import android.content.Intent;
5import android.util.Log;
6
7public class MyIntentService extends IntentService{
8
9public MyIntentService() {
10super("MyIntentService");//调⽤⽗类有参构造函数。

这⾥我们⼿动给服务起个名字为:MyIntentService
11// TODO Auto-generated constructor stub
12 }
13
14//该⽅法在会在⼀个单独的线程中执⾏,来完成⼯作任务。

任务结束后,该Service⾃动停⽌
15 @Override
16protected void onHandleIntent(Intent intent) {
17// TODO Auto-generated method stub
18for(int i = 0;i<3;i++) {
19//打印当前线程的id
20 Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId());
21try {
22 Thread.sleep(1000);
23 } catch (InterruptedException e) {
24// TODO Auto-generated catch block
25 e.printStackTrace();
26 }
27 }
28 }
29
30 @Override
31public void onDestroy() {
32// TODO Auto-generated method stub
33super.onDestroy();
34 Log.d("MyIntentService","onDestroy");
35 }
36 }
这⾥⾸先要提供⼀个⽆参的构造⽅法,并且必须在其内部调⽤⽗类的有参构造⽅法(9⾄12⾏),我们在第10⾏⼿动将服务的名字改
为“MyIntentService”。

然后在⼦类中实现onHandleIntent()这个抽象⽅法,可以在这个⽅法⾥去处理⼀些具体的逻辑,我们就⽤三次for循环,打印当前线程的id,每次延时1秒。

因为这个服务在运⾏结束后会⾃动停⽌,所以我们在onDestroy()⽅法中打印⽇志验证⼀下。

(2)在清单⽂件中对服务进⾏注册服务:
<service android:name=".MyIntentService"></service>
(3)在activity_main.xml中添加⼀个按钮button3_stop_intentservice,⽤于启动MyIntentService服务,代码略。

(4)在MainActivity⾥⾯加⼊启动IntentService的逻辑,核⼼代码如下:
1case R.id.button3_stop_intentservice:
2 Log.d("MainActivity","主线程的id是:"+Thread.currentThread().getId());
3 Intent intentService = new Intent(this,MyIntentService.class);
4 startService(intentService);
5default:
我们在第02⾏中,打印主线程的id。

运⾏程序,点击按钮button3_stop_intentservice,显⽰如下:
由此可见,启动⼀个IntentService和启动⼀个普通的Service,步骤是⼀样的。

4、Service和Thread的关系:
不少Android初学者都可能会有这样的疑惑,Service和Thread到底有什么关系呢?什么时候应该⽤Service,什么时候⼜应该⽤Thread?答案可能会有点让你吃惊,因为Service和Thread之间没有任何关系!
之所以有不少⼈会把它们联系起来,主要就是因为Service的后台概念。

Thread我们⼤家都知道,是⽤于开启⼀个⼦线程,在这⾥去执⾏⼀些耗时操作就不会阻塞主线程的运⾏。

⽽Service我们最初理解的时候,总会觉得它是⽤来处理⼀些后台任务的,⼀些⽐较耗时的操作也可以放在这⾥运⾏,这就会让⼈产⽣混淆了。

但是,如果我告诉你Service其实是运⾏在主线程⾥的,你还会觉得它和Thread有什么关系吗?
其实,后台和⼦线程是两个完全不同的概念:
Android的后台就是指,它的运⾏是完全不依赖UI的。

即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运⾏。

⽐如说⼀些应⽤程序,始终需要与服务器之间始终保持着⼼跳连接,就可以使⽤Service来实现。

你可能⼜会问,Service既然是运⾏在主线程⾥,在这⾥⼀直执⾏着⼼跳连接,难道就不会阻塞主线程的运⾏吗?当然会,但是我们可以在Service中再创建⼀个⼦线程,然后在这⾥去
处理耗时逻辑就没问题了。

既然在Service⾥也要创建⼀个⼦线程,那为什么不直接在Activity⾥创建呢?这是因为Activity很难对Thread进⾏控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的⼦线程的实例;⽽且在⼀个Activity中创建的⼦线程,另⼀个Activity⽆法对其进⾏操作。

但是Service就不同了,所有的Activity都可以与Service进⾏关联,然后可以很⽅便地操作其中的⽅法,即使Activity被销毁了,之后只要重新与Service建⽴关联,就⼜能够获取到原有的Service中Binder的实例。

因此,使⽤Service来处理后台任务,Activity就可以放⼼地finish,完全不需要担⼼⽆法对后台任务进⾏控制的情况。

所以说,⼀个⽐较标准的Service,就可以写成本段中第1节的样⼦。

四、使⽤Bind Service完成Service和Activity之间的通信
有没有什么办法能让它们俩的关联更多⼀些呢?⽐如说在Activity中指挥Service去⼲什么,Service就去⼲什么。

当然可以,只需要让Activity 和Service建⽴关联就好了。

1、Bind Service的介绍:
应⽤程序组件(客户端)通过调⽤bindService()⽅法能够绑定服务,然后Android系统会调⽤服务的onBind()回调⽅法,则个⽅法会返回⼀个跟服务器端交互的Binder对象。

这个绑定是异步的,bindService()⽅法⽴即返回,并且不给客户端返回IBinder对象。

要接收IBinder对象,客户端必须创建⼀个ServiceConnection类的实例,并且把这个实例传递给bindService()⽅法。

ServiceConnection对象包含了⼀个系统调⽤的传递IBinder对象的回调⽅法。

注意:只有Activity、Service、Content Provider能够绑定服务;BroadcastReceiver⼴播接收器不能绑定服务。

2、实现Service和Activity之间通信步骤:
我们依然在第⼆段中的项⽬ServiceTest基础上进⾏修改。

观察上⾯第⼆段中MyService中的代码,你会发现⼀直有⼀个onBind()⽅法我们都没有使⽤到,这个⽅法其实就是⽤于和Activity建⽴关联的,修改MyService中的代码,如下所⽰:
1package com.example.servicetest;
2
3import android.app.Service;
4import android.content.Intent;
5import android.os.Binder;
6import android.os.IBinder;
7import android.util.Log;
8
9public class MyService extends Service {
10
11public static final String TAG = "MyService";
12
13private MyBinder mBinder = new MyBinder();
14
15 @Override
16public void onCreate() {
17super.onCreate();
18 Log.d(TAG, "onCreate");
19 }
20
21 @Override
22public int onStartCommand(Intent intent, int flags, int startId) {
23 Log.d(TAG, "onStartCommand");
24return super.onStartCommand(intent, flags, startId);
25 }
26
27 @Override
28public void onDestroy() {
29super.onDestroy();
30 Log.d(TAG, "onDestroy");
31 }
32
33 @Override
34public IBinder onBind(Intent intent) {
35return mBinder; //在这⾥返回新建的MyBinder类
36 }
37
38//MyBinder类,继承Binder:让⾥⾯的⽅法执⾏下载任务,并获取下载进度
39class MyBinder extends Binder {
40
41public void startDownload() {
42 Log.d("TAG", "startDownload() executed");
43// 执⾏具体的下载任务
44 }
45public int getProgress(){
46 Log.d("TAG", "getProgress() executed");
47return 0;
48 }
49
50 }
51
52 }
38⾄50⾏:新建⼀个MyBinder类,继承Binder:让⾥⾯的⽅法执⾏下载任务,并获取下载进度。

当然,这⾥只是两个模拟⽅法,并没有实现真正的功能,我们通过打印⽇志的形式来体现。

接着创建MyBinder的实例(13⾏),然后在onBind()⽅法⾥返回这个实例(35⾏)。

核⼼代码是35⾏,返回这个mBinder,是⼀个IBinder类型,就可以把这个IBinder类型传递到MainActivity中,从⽽调⽤Service⾥⾯的⽅法。

下⾯就要看⼀看,在MainActivity是如何调⽤Service⾥⾯的两个⽅法的。

(2)检查清单⽂件,是否已经对Service进⾏注册:
<service android:name=".MyService" > </service>
(3)在activity_main.xml中继续添加两个按钮button3_bind_service和button4_unbind_service,⽤于绑定服务和取消绑定服务。


终,activity_main.xml的完整代码如下:
<LinearLayout xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button1_start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Start Service"/>
<Button
android:id="@+id/button2_stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop Service"/>
<Button
android:id="@+id/button3_bind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="bind Service"/>
<Button
android:id="@+id/button4_unbind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="unbind Service"/>
</LinearLayout>
(4)接下来再修改MainActivity中的代码,让MainActivity和MyService之间建⽴关联,代码如下所⽰:
1package com.example.servicetest;
2
3import android.app.Activity;
4import ponentName;
5import android.content.Intent;
6import android.content.ServiceConnection;
7import android.os.Bundle;
8import android.os.IBinder;
9import android.util.Log;
10import android.view.View;
11import android.view.View.OnClickListener;
12import android.widget.Button;
13
14public class MainActivity extends Activity implements OnClickListener {
15
16private Button button1_start_service;
17private Button button2_stop_service;
18private Button button3_bind_service;
19private Button button4_unbind_service;
20
21private MyService.MyBinder myBinder;
22
23//匿名内部类:服务连接对象
24private ServiceConnection connection = new ServiceConnection() {
25
26//当服务异常终⽌时会调⽤。

注意,解除绑定服务时不会调⽤
27 @Override
28public void onServiceDisconnected(ComponentName name) {
29 }
30
31//和服务绑定成功后,服务会回调该⽅法
32 @Override
33public void onServiceConnected(ComponentName name, IBinder service) {
34 myBinder = (MyService.MyBinder) service;
35//在Activity中调⽤Service⾥⾯的⽅法
36 myBinder.startDownload();
37 myBinder.getProgress();
38 }
39 };
40
41 @Override
42protected void onCreate(Bundle savedInstanceState) {
43super.onCreate(savedInstanceState);
44 setContentView(yout.activity_main);
45 button1_start_service = (Button) findViewById(R.id.button1_start_service);
46 button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
47 button3_bind_service = (Button) findViewById(R.id.button3_bind_service);
48 button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service);
49
50 button1_start_service.setOnClickListener(this);
51 button2_stop_service.setOnClickListener(this);
52 button3_bind_service.setOnClickListener(this);
53 button4_unbind_service.setOnClickListener(this);
54 }
55
56 @Override
57public void onClick(View v) {
58switch (v.getId()) {
59case R.id.button1_start_service:
60 Intent startIntent = new Intent(this, MyService.class);
61 startService(startIntent);
62break;
63case R.id.button2_stop_service:
64 Intent stopIntent = new Intent(this, MyService.class);
65 stopService(stopIntent);
66break;
67case R.id.button3_bind_service:
68 Intent bindIntent = new Intent(this, MyService.class);
69 bindService(bindIntent, connection, BIND_AUTO_CREATE);
70break;
71case R.id.button4_unbind_service:
72 unbindService(connection);
73break;
74
75default:
76break;
77 }
78 }
79
80 }
可以看到,这⾥我们⾸先创建了⼀个ServiceConnection的匿名类(24⾏),在⾥⾯重写了onServiceConnected()⽅法和onServiceDisconnected()⽅法,如果当前Activity与服务连接成功后,服务会回调onServiceConnected()⽅法,
在onServiceConnected()⽅法中,我们⼜通过向下转型得到了MyBinder的实例(34⾏),有了这个实例,Activity和Service之间的关系就变得⾮常紧密了。

现在我们可以在Activity中根据具体的场景来调⽤MyBinder中的任何public⽅法(36、37⾏),即实现了Activity指挥Service ⼲什么Service就去⼲什么的功能。

当然,现在Activity和Service其实还没关联起来了呢,这个功能是在Bind Service按钮的点击事件⾥完成的。

可以看到,这⾥我们仍然是构建出了⼀个Intent对象,然后调⽤bindService()⽅法将Activity和Service进⾏绑定。

bindService()⽅法接收三个参数,第⼀个参数就是刚刚构建出的Intent对象,第⼆个参数是前⾯创建出的ServiceConnection的实例,第三个参数是⼀个标志位,这⾥传⼊BIND_AUTO_CREATE表⽰在Activity和Service建⽴关联后会⾃动创建Service(即使之前没有创建Service也没有关系),这会使得MyService中的onCreate()⽅法得到执⾏,但onStartCommand()⽅法不会执⾏。

然后如何我们想解除Activity和Service之间的关联怎么办呢?调⽤⼀下unbindService()⽅法就可以了,这也是Unbind Service按钮的点击事件⾥实现的逻辑。

现在让我们重新运⾏⼀下程序吧,在MainActivity中点击⼀下Bind Service按钮,LogCat⾥的打印⽇志如下图所⽰:
可以看到,只点击了Bind Service按钮,但是oncreate()⽅法得到了执⾏,⽽onStartCommand()⽅法不会执⾏。

另外需要注意,任何⼀个Service在整个应⽤程序范围内都是通⽤的,即MyService不仅可以和MainActivity建⽴关联,还可以和任何⼀个Activity建⽴关联,⽽且在建⽴关联时它们都可以获取到相同的MyBinder实例。

如何销毁Service:
根据上⾯第⼀段的知识,我们介绍了销毁Service最简单的⼀种情况:现在卸载程序,重新运⾏程序,点击Start Service按钮启动Service,再点击Stop Service按钮停⽌Service,这样MyService就被销毁了:
现在回到本段内容。

卸载程序,重新开始。

那么如果我们只点击的Bind Service按钮呢?由于在绑定Service的时候指定的标志位是
BIND_AUTO_CREATE,说明点击Bind Service按钮的时候Service也会被创建,这时应该怎么销毁Service呢?其实也很简单,点击⼀下Unbind Service按钮,将Activity和Service的关联解除就可以了:
以上这两种销毁的⽅式都很好理解。

那么如果我们既点击了Start Service按钮,⼜点击了Bind Service按钮会怎么样呢?这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将Unbind Service按钮和Stop Service按钮都点击⼀下(没有先后顺序),Service才会被销毁。

也就是说,点击Stop Service按钮只会让Service停⽌,点击Unbind Service按钮只会让Service和Activity解除关联,⼀个Service必须要在既没有和任何Activity关联⼜处理停⽌状态的时候才会被销毁。

点击Unbind Service按钮后,再次点击Unbind Service按钮按钮引发的问题:
假设现在Service和Activity已经相关联了,点击Unbind Service按钮能够解除绑定,如果继续点击Unbind Service按钮,程序会异常退出,这说明代码不够完善,我们需要在代码中加⼀个判断是否绑定的标记mBound。

在改MainActivity中增加⼀部分代码,最终改MainActivity的完整代码如下:(加粗字体是添加的内容)
1package com.example.servicetest02;
2
3import android.app.Activity;
4import ponentName;
5import android.content.Intent;
6import android.content.ServiceConnection;
7import android.os.Bundle;
8import android.os.IBinder;
9import android.util.Log;
10import android.view.View;
11import android.view.View.OnClickListener;
12import android.widget.Button;
13
14public class MainActivity extends Activity implements OnClickListener {
15private Button button1_start_service;
16private Button button2_stop_service;
17private Button button3_bind_service;
18private Button button4_unbind_service;
19private MyService.MyBinder myBinder;
20
21boolean mBound = false; //⼀开始,并没有和Service绑定.这个参数是⽤来显⽰绑定状态
22
23//匿名内部类:服务连接对象
24private ServiceConnection connection = new ServiceConnection() {
25
26//当服务异常终⽌时会调⽤。

注意,解除绑定服务时不会调⽤
27 @Override
28public void onServiceDisconnected(ComponentName name) {
29 mBound = false; //服务异常终⽌时,状态为未绑定
30 }
31
32//和服务绑定成功后,服务会回调该⽅法
33 @Override
34public void onServiceConnected(ComponentName name, IBinder service) {
35 myBinder = (MyService.MyBinder) service;
36//在Activity中调⽤Service⾥⾯的⽅法
37 myBinder.startDownload();
38 myBinder.getProgress();
39 mBound = true; //true说明是绑定状态
40 }
41 };
42 @Override
43protected void onCreate(Bundle savedInstanceState) {
44super.onCreate(savedInstanceState);
45 setContentView(yout.activity_main);
46 button1_start_service = (Button) findViewById(R.id.button1_start_service);
47 button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
48 button3_bind_service = (Button) findViewById(R.id.button3_bind_service);
49 button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service);
50 button1_start_service.setOnClickListener(this);
51 button2_stop_service.setOnClickListener(this);
52 button3_bind_service.setOnClickListener(this);
53 button4_unbind_service.setOnClickListener(this);
54 }
55 @Override
56public void onClick(View v) {
57switch (v.getId()) {
58case R.id.button1_start_service:
59 Intent startIntent = new Intent(this, MyService.class);
60 startService(startIntent);
61break;
62case R.id.button2_stop_service:
63 Intent stopIntent = new Intent(this, MyService.class);
64 stopService(stopIntent);
65break;
66case R.id.button3_bind_service:
67 Intent bindIntent = new Intent(this, MyService.class);
68 bindService(bindIntent, connection, BIND_AUTO_CREATE);
69break;
70case R.id.button4_unbind_service:
71//如果和Service是绑定的状态,就解除绑定。

72if(mBound){
73 unbindService(connection);
74 mBound=false;
75 }
76break;
77
78default:
79break;
80 }
81 }
82 }
添加的代码是第21⾏、29⾏、72⾏⾄74⾏。

这样的话,连续点击Unbind Service按钮,就不会使程序出现异常。

3、started服务与bind服务的区别:
区别⼀:⽣命周期
通过started⽅式的服务会⼀直运⾏在后台,需要由组件本⾝或外部组件来停⽌服务才会以结束运⾏
bind⽅式的服务,⽣命周期就要依赖绑定的组件
区别⼆:参数传递
started服务可以给启动的服务对象传递参数,但⽆法获取服务中⽅法的返回值
bind服务可以给启动的服务对象传递参数,也可以通过绑定的业务对象获取返回结果
实际开发中的技巧;
第⼀次先使⽤started⽅式来启动⼀个服务
之后可以使⽤bind的⽅式绑定服务,从⽽可以直接调⽤业务⽅法获取返回值
4、Service的⽣命周期:
⼀旦在项⽬的任何位置调⽤了Context的startService()⽅法,相应的服务就会启动起来,并回调onstartCommand()⽅法。

如果这个服务之前还没有创建过,onCreate()⽅法会先于onstartCommand()⽅法执⾏。

服务启动过后,会⼀直保持运⾏状态,直到stopService()或stopself()⽅法被调⽤。

注意虽然每次调⽤⼀次startService()⽅法,onstartCommand()⽅法就会以执⾏⼀次,但实际上每个服务都只会存在⼀个实例。

所以不管你调⽤了多少次startService()⽅法,只需调⽤⼀次stopService()或stopself()⽅法,服务就会停⽌。

另外,还可以调⽤Context的bindService()来获取⼀个服务的持久连接,这时就会回调服务中的onBind()⽅法。

类似地,如果这个服务之前还没有创建过,onCreate()⽅法会先于onBind()⽅法执⾏。

之后调⽤⽅可以获取到onBind()⽅法⾥返回的IBinder对象的实例,这样,就能⾃由地和服务进⾏通信了。

只要调⽤⽅和服务之间的连接没有断开,服务就会⼀直保持运⾏状态。

五、使⽤Bind Service完成IPC进程间通信:(在同⼀个APP内模拟)
既然是在在同⼀个APP内模拟进程间通信,其实就是完成进程内通信,但是原理都是⼀样的嘛。

也就是说,要实现:让Activity与⼀个远程Service建⽴关联,这就要使⽤AIDL来进⾏跨进程通信了(IPC)。

这⾥把Bind Service及其他的概念再重复⼀下:
1、Bind Service的介绍:
应⽤程序组件(客户端)通过调⽤bindService()⽅法能够绑定服务,然后Android系统会调⽤服务的onBind()回调⽅法,则个⽅法会返回⼀个跟服务器端交互的Binder对象。

这个绑定是异步的,bindService()⽅法⽴即返回,并且不给客户端返回IBinder对象。

要接收IBinder对象,客户端必须创建⼀个ServiceConnection类的实例,并且把这个实例传递给bindService()⽅法。

ServiceConnection对象包含了⼀个系统调⽤的传递IBinder对象的回调⽅法。

注意:只有Activity、Service、Content Provider能够绑定服务;BroadcastReceiver⼴播接收器不能绑定服务。

2、在客户端绑定⼀个服务的步骤:
(1)实现ServiceConnection抽象类。

实现过程中,必须重写⼀下两个回调⽅法:
onServiceConnected() 和服务绑定成功后,系统会调⽤这个⽅法来发送由服务的onBind()⽅法返回的IBinder对象
onServiceDisconnected() 当服务异常终⽌时会调⽤(如服务崩溃或被杀死时)。

注意,在客户端解除绑定时不会调⽤该⽅法。

(2)调⽤bindService()⽅法来传递ServiceConnection类的实现;
(3)当系统调⽤你的onServiceConnected()回调⽅法时,你就可以开始使⽤接⼝中定义的⽅法来调⽤服务了
(4)调⽤unbindService()⽅法断开与服务的链接。

注:bindService()和unbindService()⽅法都是Context类中的⽅法。

3、IPC(Inter-Process Communication)进程间通信机制:
在同⼀进程中,各个组件进⾏通信是⼗分⽅便的,普通的函数调⽤就可以解决;但是对于不同的进程中的组件来说,要进⾏通信,就需要⽤到Android的IPC机制了。

对应⽤开发者来说,Android的IBinder/Binder框架实现了Android的IPC通信。

当然,IBinder/Binder框架也可以⽤来实现进程内通信(本地通信),也可以实现进程间通信(远程通信)
从Android SDK中对IBinder/Binder的解释可知,IBinder/Binder是Android远程对象的基本接⼝,它是Android⽤于提供⾼性能IPC通信⽽设计的⼀套轻量级远程调⽤机制的核⼼部分。

该接⼝描述了与⼀个远程对象进⾏通信的抽象协议。

4、AIDL(Android Interface Definition Language)Android接⼝定义语⾔:
AIDL它可以⽤于让某个Service与多个应⽤程序组件之间进⾏跨进程通信,从⽽可以实现多个应⽤程序共享同⼀个Service的功能。

AIDL⽀持的类型:⼋⼤基本数据类型、String类型、CharSequence、List、Map、⾃定义。

来看下⾯的这张原理图:
上图中,如果A应⽤程序想访问B应⽤程序中的业务对象,可以先让A绑定B应⽤中的Service,然后通过Service去访问B中的业务对象。

我们可以⽤AIDL来描述需要被别⼈调⽤的接⼝(即B中的业务对象)。

5、IPC(进程间通讯)具体的步骤如下:
使⽤AIDL定义业务接⼝,通过ADT⼯具来⽣成⼀个java类,此类实现了进程间远程通讯的代理
编写⾃⼰的业务类(继承⽣成的类中的Stub)来实现业务接⼝功能
再通过绑定Service的⽅式来暴露此业务对象,给其它组件提供功能
调⽤者组件通过bindService⽅法绑定服务,从⽽获取绑定成功后的远程业务对象或本地业务对象,然后就可以调⽤相关功能。

注意:⼀般在使⽤完绑定服务后,需要解除绑定。

下⾯就通过代码来实现。

6、让Activity与⼀个远程Service建⽴关联的步骤:(在同⼀个APP内模拟)
新建⼀个全新的Android⼯程ServiceTest02。

(1)新建IPerson.aidl⽂件,代码如下所⽰:
1package com.example.servicetest02;
2interface IPerson{
3void setName(String name);
4void setSex(String sex);
5void setAge(int age);
6 String getPerson();
7 }
这个⽂件⾥,添加我们需要的业务⽅法。

第01⾏是包名。

注意不要写public等修饰符。

(如果这个⽂件写错了,程序会报错,后⾯的Java⽂件也不会⾃从⽣成)
⽂件保存之后,ADT会在gen⽬录下⾃动⽣成⼀个对应的Java⽂件,如下图所⽰:
之后,程序运⾏的时候使⽤的是这个Java⽂件,与aidl⽂件就没有关系了。

我们来⼤致分析⼀下这个⾃动⽣成的Java⽂件。

完整版代码如下:
1/*
2 * This file is auto-generated. DO NOT MODIFY.
3 * Original file: E:\\workspace\\xiongdilian\\ServiceTest02\\src\\com\\example\\servicetest02\\IPerson.aidl
4*/
5package com.example.servicetest02;
6
7public interface IPerson extends android.os.IInterface {
8/** Local-side IPC implementation stub class. */
9public static abstract class Stub extends android.os.Binder implements
10 com.example.servicetest02.IPerson {
11private static final ng.String DESCRIPTOR = "com.example.servicetest02.IPerson"; 12
13/** Construct the stub at attach it to the interface. */
14public Stub() {
15this.attachInterface(this, DESCRIPTOR);
16 }
17
18/**
19 * Cast an IBinder object into an com.example.servicetest02.IPerson
20 * interface, generating a proxy if needed.
21*/
22public static com.example.servicetest02.IPerson asInterface(
23 android.os.IBinder obj) {
24if ((obj == null)) {
25return null;
26 }
27 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
28if (((iin != null) && (iin instanceof com.example.servicetest02.IPerson))) {
29return ((com.example.servicetest02.IPerson) iin);
30 }
31return new com.example.servicetest02.IPerson.Stub.Proxy(obj);
32 }
33
34 @Override
35public android.os.IBinder asBinder() {
36return this;
37 }
38
39 @Override
40public boolean onTransact(int code, android.os.Parcel data,
41 android.os.Parcel reply, int flags)
42throws android.os.RemoteException {
43switch (code) {
44case INTERFACE_TRANSACTION: {
45 reply.writeString(DESCRIPTOR);
46return true;
47 }
48case TRANSACTION_setName: {
49 data.enforceInterface(DESCRIPTOR);
50 ng.String _arg0;
51 _arg0 = data.readString();
52this.setName(_arg0);
53 reply.writeNoException();
54return true;
55 }
56case TRANSACTION_setSex: {
57 data.enforceInterface(DESCRIPTOR);
58 ng.String _arg0;
59 _arg0 = data.readString();
60this.setSex(_arg0);
61 reply.writeNoException();
62return true;
63 }
64case TRANSACTION_setAge: {
65 data.enforceInterface(DESCRIPTOR);
66int _arg0;
67 _arg0 = data.readInt();
68this.setAge(_arg0);
69 reply.writeNoException();
70return true;
71 }
72case TRANSACTION_getPerson: {
73 data.enforceInterface(DESCRIPTOR);
74 ng.String _result = this.getPerson();
75 reply.writeNoException();
76 reply.writeString(_result);
77return true;
78 }
79 }
80return super.onTransact(code, data, reply, flags);
81 }
82
83private static class Proxy implements com.example.servicetest02.IPerson {
84private android.os.IBinder mRemote;
85
86 Proxy(android.os.IBinder remote) {
87 mRemote = remote;
88 }
89
90 @Override
91public android.os.IBinder asBinder() {
92return mRemote;
93 }
94
95public ng.String getInterfaceDescriptor() {
96return DESCRIPTOR;
97 }
98
99 @Override
100public void setName(ng.String name)
101throws android.os.RemoteException {
102 android.os.Parcel _data = android.os.Parcel.obtain();
103 android.os.Parcel _reply = android.os.Parcel.obtain();
104try {
105 _data.writeInterfaceToken(DESCRIPTOR);
106 _data.writeString(name);
107 mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
108 _reply.readException();
109 } finally {
110 _reply.recycle();
111 _data.recycle();
112 }
113 }
114
115 @Override
116public void setSex(ng.String sex)
117throws android.os.RemoteException {
118 android.os.Parcel _data = android.os.Parcel.obtain();
119 android.os.Parcel _reply = android.os.Parcel.obtain();
120try {
121 _data.writeInterfaceToken(DESCRIPTOR);
122 _data.writeString(sex);
123 mRemote.transact(Stub.TRANSACTION_setSex, _data, _reply, 0);
124 _reply.readException();
125 } finally {
126 _reply.recycle();
127 _data.recycle();
128 }
129 }
130
131 @Override
132public void setAge(int age) throws android.os.RemoteException {
133 android.os.Parcel _data = android.os.Parcel.obtain();
134 android.os.Parcel _reply = android.os.Parcel.obtain();
135try {
136 _data.writeInterfaceToken(DESCRIPTOR);
137 _data.writeInt(age);
138 mRemote.transact(Stub.TRANSACTION_setAge, _data, _reply, 0);
139 _reply.readException();
140 } finally {
141 _reply.recycle();
142 _data.recycle();
143 }
144 }
145
146 @Override
147public ng.String getPerson()
148throws android.os.RemoteException {
149 android.os.Parcel _data = android.os.Parcel.obtain();
150 android.os.Parcel _reply = android.os.Parcel.obtain();
151 ng.String _result;
152try {
153 _data.writeInterfaceToken(DESCRIPTOR);
154 mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply,
155 0);
156 _reply.readException();
157 _result = _reply.readString();
158 } finally {
159 _reply.recycle();
160 _data.recycle();
161 }
162return _result;
163 }
164 }
165
166static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 167static final int TRANSACTION_setSex = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); 168static final int TRANSACTION_setAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); 169static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); 170 }
171
172public void setName(ng.String name)。

相关文档
最新文档