【声明】 html
欢迎转载,但请保留文章原始出处→_→ java
生命壹号:http://www.cnblogs.com/smyhvae/android
文章来源:http://www.cnblogs.com/smyhvae/p/4070518.html安全
【前言】服务器
花了周末两天的时间,整理了一下做为Android四大组件之一的Service的基础知识,经过这篇文章,应该能够明白:对Service的理解、在什么地方使用、怎么使用、要注意哪些问题等。多线程
【本文主要内容】并发
1、Service的基本概念(四大组件之一)
2、定义(启动)一个Serviceapp
3、IntentService框架
4、使用Bind Service完成Service和Activity之间的通讯异步
5、使用Bind Service完成IPC进程间通讯:(在同一个APP内模拟)
6、使用Bind Service完成IPC进程间通讯:(两个APP之间)
7、Messenger的使用
【正文】
1、Service的基本概念(四大组件之一)
Service是Android中实现程序后台运行的解决方案,很是适合用于去执行哪些不须要和用户交互并且还要求长期运行的任务。不能运行在一个独立的进程当中,而是依赖与建立服务时所在的应用程序进程。只能在后台运行,而且能够和其余组件进行交互。
Service能够在不少场合使用,好比播放多媒体的时候用户启动了其余Activity,此时要在后台继续播放;好比检测SD卡上文件的变化;好比在后台记录你的地理信息位置的改变等等,总之服务是藏在后台的。
服务不会自动开启线程,咱们须要在服务的内部手动建立子线程,并在这里执行具体的任务。关于多线程的知识:能够参考另一篇文章:Android多线程----异步消息处理机制之Handler详解
2、定义(启动)一个Service:
一、如何定义(启动)一个Service:
核心步骤以下:
新建一个Android项目ServiceTest,具体步骤以下:
(1)新建一个MyService类,继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法,代码以下:
1 package com.example.servicetest; 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 MyService extends Service { 9 10 public static final String TAG = "MyService"; 11 12 //建立服务时调用 13 @Override 14 public void onCreate() { 15 super.onCreate(); 16 Log.d(TAG, "onCreate"); 17 } 18 19 //服务执行的操做 20 @Override 21 public int onStartCommand(Intent intent, int flags, int startId) { 22 Log.d(TAG, "onStartCommand"); 23 return super.onStartCommand(intent, flags, startId); 24 } 25 26 //销毁服务时调用 27 @Override 28 public void onDestroy() { 29 super.onDestroy(); 30 Log.d(TAG, "onDestroy"); 31 } 32 33 @Override 34 public IBinder onBind(Intent intent) { 35 return 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="http://schemas.android.com/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的逻辑,代码以下:
1 package com.example.servicetest; 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 implements OnClickListener { 12 13 private Button button1_start_service; 14 15 private Button button2_stop_service; 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.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 28 public void onClick(View v) { 29 switch (v.getId()) { 30 case R.id.button1_start_service: 31 Intent startIntent = new Intent(this, MyService.class); 32 startService(startIntent); 33 break; 34 case R.id.button2_stop_service: 35 Intent stopIntent = new Intent(this, MyService.class); 36 stopService(stopIntent); 37 break; 38 default: 39 break; 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服务有两种方法:
(1)在外部使用stopService()
(2)在服务内部(onStartCommand方法内部)使用stopSelf()方法。
三、onStartCommand方法的返回值:
onStartCommand方法执行时,返回的是一个int型。这个整型能够有三个返回值:START_NOT_STICKY、START_STICKY、START_REDELIVER_INTENT
3、IntentService
一、IntentService的引入:
咱们在第一段中就已经说了,服务中的代码默认运行在主线程中,若是直接在服务里执行一些耗时操做,容易形成ANR(Application Not Responding)异常,因此就须要用到多线程的知识了。
所以一个比较标准的服务能够这样写:
1 package com.example.servicetest; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.IBinder; 6 7 public class MyService extends Service { 8 9 public static final String TAG = "MyService"; 10 11 //服务执行的操做 12 @Override 13 public int onStartCommand(Intent intent, int flags, int startId) { 14 new Thread(new Runnable() { 15 public void run() { 16 //处理具体的逻辑 17 stopSelf(); //服务执行完毕后自动中止 18 } 19 }).start(); 20 return super.onStartCommand(intent, flags, startId); 21 } 22 23 @Override 24 public IBinder onBind(Intent intent) { 25 // TODO Auto-generated method stub 26 return null; 27 } 28 29 }
核心代码:14至19行,在子线程中处理具体的逻辑。
须要注意的是,若是没有第17行的stopSelf(),服务一旦启动后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务中止下来;因此咱们添加了17行的stopSelf(),服务执行完毕后会自动中止。
虽然说上面的这种写法并不复杂,但总会有一些程序猿忘记开启线程,或者忘记调用stopSelf()方法。为了能够简单地建立一个异步的、会自动中止的服务,Android专门提供了一个IntentService类,这个类就很好的解决了上面所提到的两种尴尬。另外,能够启动IntentService屡次,而每个耗时操做会以工做队列的方式在IntentService的onHandleIntent()回调方法中执行,而且每次只会执行一个工做线程,执行完第一个后,再执行第二个,以此类推。
二、IntentService的做用:
当咱们须要这样一次性完成的任务时,就可使用IntentService来完成。
三、IntentService的用法:
咱们在上面的项目ServiceTest基础上进行修改,步骤以下:
(1)新建一个MyIntentService类,继承自IntentService,并重写父类的onHandleIntent()方法,代码以下:
1 package com.example.servicetest; 2 3 import android.app.IntentService; 4 import android.content.Intent; 5 import android.util.Log; 6 7 public class MyIntentService extends IntentService{ 8 9 public MyIntentService() { 10 super("MyIntentService");//调用父类有参构造函数。这里咱们手动给服务起个名字为:MyIntentService 11 // TODO Auto-generated constructor stub 12 } 13 14 //该方法在会在一个单独的线程中执行,来完成工做任务。任务结束后,该Service自动中止 15 @Override 16 protected void onHandleIntent(Intent intent) { 17 // TODO Auto-generated method stub 18 for(int i = 0;i<3;i++) { 19 //打印当前线程的id 20 Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId()); 21 try { 22 Thread.sleep(1000); 23 } catch (InterruptedException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 } 28 } 29 30 @Override 31 public void onDestroy() { 32 // TODO Auto-generated method stub 33 super.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的逻辑,核心代码以下:
1 case R.id.button3_stop_intentservice: 2 Log.d("MainActivity","主线程的id是:"+Thread.currentThread().getId()); 3 Intent intentService = new Intent(this,MyIntentService.class); 4 startService(intentService); 5 default:
咱们在第02行中,打印主线程的id。
运行程序,点击按钮button3_stop_intentservice,显示以下:
因而可知,启动一个IntentService和启动一个普通的Service,步骤是同样的。
四、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节的样子。
4、使用Bind Service完成Service和Activity之间的通讯
有没有什么办法能让它们俩的关联更多一些呢?好比说在Activity中指挥Service去干什么,Service就去干什么。固然能够,只须要让Activity和Service创建关联就行了。
一、Bind Service的介绍:
应用程序组件(客户端)经过调用bindService()方法可以绑定服务,而后Android系统会调用服务的onBind()回调方法,则个方法会返回一个跟服务器端交互的Binder对象。
这个绑定是异步的,bindService()方法当即返回,而且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须建立一个ServiceConnection类的实例,而且把这个实例传递给bindService()方法。ServiceConnection对象包含了一个系统调用的传递IBinder对象的回调方法。
注意:只有Activity、Service、Content Provider可以绑定服务;BroadcastReceiver广播接收器不能绑定服务。
二、实现Service和Activity之间通讯步骤:
咱们依然在第二段中的项目ServiceTest基础上进行修改。
观察上面第二段中MyService中的代码,你会发现一直有一个onBind()方法咱们都没有使用到,这个方法其实就是用于和Activity创建关联的,修改MyService中的代码,以下所示:
1 package com.example.servicetest; 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 MyService extends Service { 10 11 public static final String TAG = "MyService"; 12 13 private MyBinder mBinder = new MyBinder(); 14 15 @Override 16 public void onCreate() { 17 super.onCreate(); 18 Log.d(TAG, "onCreate"); 19 } 20 21 @Override 22 public int onStartCommand(Intent intent, int flags, int startId) { 23 Log.d(TAG, "onStartCommand"); 24 return super.onStartCommand(intent, flags, startId); 25 } 26 27 @Override 28 public void onDestroy() { 29 super.onDestroy(); 30 Log.d(TAG, "onDestroy"); 31 } 32 33 @Override 34 public IBinder onBind(Intent intent) { 35 return mBinder; //在这里返回新建的MyBinder类 36 } 37 38 //MyBinder类,继承Binder:让里面的方法执行下载任务,并获取下载进度 39 class MyBinder extends Binder { 40 41 public void startDownload() { 42 Log.d("TAG", "startDownload() executed"); 43 // 执行具体的下载任务 44 } 45 public int getProgress(){ 46 Log.d("TAG", "getProgress() executed"); 47 return 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="http://schemas.android.com/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之间创建关联,代码以下所示:
1 package com.example.servicetest; 2 3 import android.app.Activity; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.util.Log; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.widget.Button; 13 14 public class MainActivity extends Activity implements OnClickListener { 15 16 private Button button1_start_service; 17 private Button button2_stop_service; 18 private Button button3_bind_service; 19 private Button button4_unbind_service; 20 21 private MyService.MyBinder myBinder; 22 23 //匿名内部类:服务链接对象 24 private ServiceConnection connection = new ServiceConnection() { 25 26 //当服务异常终止时会调用。注意,解除绑定服务时不会调用 27 @Override 28 public void onServiceDisconnected(ComponentName name) { 29 } 30 31 //和服务绑定成功后,服务会回调该方法 32 @Override 33 public 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 42 protected void onCreate(Bundle savedInstanceState) { 43 super.onCreate(savedInstanceState); 44 setContentView(R.layout.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 57 public void onClick(View v) { 58 switch (v.getId()) { 59 case R.id.button1_start_service: 60 Intent startIntent = new Intent(this, MyService.class); 61 startService(startIntent); 62 break; 63 case R.id.button2_stop_service: 64 Intent stopIntent = new Intent(this, MyService.class); 65 stopService(stopIntent); 66 break; 67 case R.id.button3_bind_service: 68 Intent bindIntent = new Intent(this, MyService.class); 69 bindService(bindIntent, connection, BIND_AUTO_CREATE); 70 break; 71 case R.id.button4_unbind_service: 72 unbindService(connection); 73 break; 74 75 default: 76 break; 77 } 78 } 79 80 }
能够看到,这里咱们首先建立了一个ServiceConnection的匿名类(24行),在里面重写了onServiceConnected()方法和onServiceDisconnected()方法,若是当前Activity与服务链接成功后,服务会回调onServiceConnected()方法,
在onServiceConnected()方法中,咱们又经过向下转型获得了MyBinder的实例(34行),有了这个实例,Activity和Service之间的关系就变得很是紧密了。如今咱们能够在Activity中根据具体的场景来调用MyBinder中的任何public方法(3六、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的完整代码以下:(加粗字体是添加的内容)
1 package com.example.servicetest02; 2 3 import android.app.Activity; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.util.Log; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.widget.Button; 13 14 public class MainActivity extends Activity implements OnClickListener { 15 private Button button1_start_service; 16 private Button button2_stop_service; 17 private Button button3_bind_service; 18 private Button button4_unbind_service; 19 private MyService.MyBinder myBinder; 20 21 boolean mBound = false; //一开始,并无和Service绑定.这个参数是用来显示绑定状态 22 23 //匿名内部类:服务链接对象 24 private ServiceConnection connection = new ServiceConnection() { 25 26 //当服务异常终止时会调用。注意,解除绑定服务时不会调用 27 @Override 28 public void onServiceDisconnected(ComponentName name) { 29 mBound = false; //服务异常终止时,状态为未绑定 30 } 31 32 //和服务绑定成功后,服务会回调该方法 33 @Override 34 public 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 43 protected void onCreate(Bundle savedInstanceState) { 44 super.onCreate(savedInstanceState); 45 setContentView(R.layout.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 56 public void onClick(View v) { 57 switch (v.getId()) { 58 case R.id.button1_start_service: 59 Intent startIntent = new Intent(this, MyService.class); 60 startService(startIntent); 61 break; 62 case R.id.button2_stop_service: 63 Intent stopIntent = new Intent(this, MyService.class); 64 stopService(stopIntent); 65 break; 66 case R.id.button3_bind_service: 67 Intent bindIntent = new Intent(this, MyService.class); 68 bindService(bindIntent, connection, BIND_AUTO_CREATE); 69 break; 70 case R.id.button4_unbind_service: 71 //若是和Service是绑定的状态,就解除绑定。 72 if(mBound){ 73 unbindService(connection); 74 mBound=false; 75 } 76 break; 77 78 default: 79 break; 80 } 81 } 82 }
添加的代码是第21行、29行、72行至74行。
这样的话,连续点击Unbind Service按钮,就不会使程序出现异常。
三、started服务与bind服务的区别:
区别一:生命周期
区别二:参数传递
实际开发中的技巧;
四、Service的生命周期:
一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并回调onstartCommand()方法。若是这个服务以前尚未建立过,onCreate()方法会先于onstartCommand()方法执行。服务启动事后,会一直保持运行状态,直到stopService()或stopself()方法被调用。注意虽然每次调用一次startService()方法,onstartCommand()方法就会以执行一次,但实际上每一个服务都只会存在一个实例。因此无论你调用了多少次startService()方法,只需调用一次stopService()或stopself()方法,服务就会中止。
另外,还能够调用Context的bindService()来获取一个服务的持久链接,这时就会回调服务中的onBind()方法。相似地,若是这个服务以前尚未建立过,onCreate()方法会先于onBind()方法执行。以后调用方能够获取到onBind()方法里返回的IBinder对象的实例,这样,就能自由地和服务进行通讯了。只要调用方和服务之间的链接没有断开,服务就会一直保持运行状态。
5、使用Bind Service完成IPC进程间通讯:(在同一个APP内模拟)
既然是在在同一个APP内模拟进程间通讯,其实就是完成进程内通讯,可是原理都是同样的嘛。
也就是说,要实现:让Activity与一个远程Service创建关联,这就要使用AIDL来进行跨进程通讯了(IPC)。这里把Bind Service及其余的概念再重复一下:
一、Bind Service的介绍:
应用程序组件(客户端)经过调用bindService()方法可以绑定服务,而后Android系统会调用服务的onBind()回调方法,则个方法会返回一个跟服务器端交互的Binder对象。
这个绑定是异步的,bindService()方法当即返回,而且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须建立一个ServiceConnection类的实例,而且把这个实例传递给bindService()方法。ServiceConnection对象包含了一个系统调用的传递IBinder对象的回调方法。
注意:只有Activity、Service、Content Provider可以绑定服务;BroadcastReceiver广播接收器不能绑定服务。
二、在客户端绑定一个服务的步骤:
(1)实现ServiceConnection抽象类。实现过程当中,必须重写一下两个回调方法:
(2)调用bindService()方法来传递ServiceConnection类的实现;
(3)当系统调用你的onServiceConnected()回调方法时,你就能够开始使用接口中定义的方法来调用服务了
(4)调用unbindService()方法断开与服务的连接。
注:bindService()和unbindService()方法都是Context类中的方法。
三、IPC(Inter-Process Communication)进程间通讯机制:
在同一进程中,各个组件进行通讯是十分方便的,普通的函数调用就能够解决;可是对于不一样的进程中的组件来讲,要进行通讯,就须要用到Android的IPC机制了。
对应用开发者来讲,Android的IBinder/Binder框架实现了Android的IPC通讯。固然,IBinder/Binder框架也能够用来实现进程内通讯(本地通讯),也能够实现进程间通讯(远程通讯)
从Android SDK中对IBinder/Binder的解释可知,IBinder/Binder是Android远程对象的基本接口,它是Android用于提供高性能IPC通讯而设计的一套轻量级远程调用机制的核心部分。该接口描述了与一个远程对象进行通讯的抽象协议。
四、AIDL(Android Interface Definition Language)Android接口定义语言:
AIDL它能够用于让某个Service与多个应用程序组件之间进行跨进程通讯,从而能够实现多个应用程序共享同一个Service的功能。
AIDL支持的类型:八大基本数据类型、String类型、CharSequence、List、Map、自定义。
来看下面的这张原理图:
上图中,若是A应用程序想访问B应用程序中的业务对象,能够先让A绑定B应用中的Service,而后经过Service去访问B中的业务对象。咱们能够用AIDL来描述须要被别人调用的接口(即B中的业务对象)。
五、IPC(进程间通信)具体的步骤以下:
下面就经过代码来实现。
六、让Activity与一个远程Service创建关联的步骤:(在同一个APP内模拟)
新建一个全新的Android工程ServiceTest02。
(1)新建IPerson.aidl文件,代码以下所示:
1 package com.example.servicetest02; 2 interface IPerson{ 3 void setName(String name); 4 void setSex(String sex); 5 void 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 */ 5 package com.example.servicetest02; 6 7 public interface IPerson extends android.os.IInterface { 8 /** Local-side IPC implementation stub class. */ 9 public static abstract class Stub extends android.os.Binder implements 10 com.example.servicetest02.IPerson { 11 private static final java.lang.String DESCRIPTOR = "com.example.servicetest02.IPerson"; 12 13 /** Construct the stub at attach it to the interface. */ 14 public Stub() { 15 this.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 */ 22 public static com.example.servicetest02.IPerson asInterface( 23 android.os.IBinder obj) { 24 if ((obj == null)) { 25 return null; 26 } 27 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 28 if (((iin != null) && (iin instanceof com.example.servicetest02.IPerson))) { 29 return ((com.example.servicetest02.IPerson) iin); 30 } 31 return new com.example.servicetest02.IPerson.Stub.Proxy(obj); 32 } 33 34 @Override 35 public android.os.IBinder asBinder() { 36 return this; 37 } 38 39 @Override 40 public boolean onTransact(int code, android.os.Parcel data, 41 android.os.Parcel reply, int flags) 42 throws android.os.RemoteException { 43 switch (code) { 44 case INTERFACE_TRANSACTION: { 45 reply.writeString(DESCRIPTOR); 46 return true; 47 } 48 case TRANSACTION_setName: { 49 data.enforceInterface(DESCRIPTOR); 50 java.lang.String _arg0; 51 _arg0 = data.readString(); 52 this.setName(_arg0); 53 reply.writeNoException(); 54 return true; 55 } 56 case TRANSACTION_setSex: { 57 data.enforceInterface(DESCRIPTOR); 58 java.lang.String _arg0; 59 _arg0 = data.readString(); 60 this.setSex(_arg0); 61 reply.writeNoException(); 62 return true; 63 } 64 case TRANSACTION_setAge: { 65 data.enforceInterface(DESCRIPTOR); 66 int _arg0; 67 _arg0 = data.readInt(); 68 this.setAge(_arg0); 69 reply.writeNoException(); 70 return true; 71 } 72 case TRANSACTION_getPerson: { 73 data.enforceInterface(DESCRIPTOR); 74 java.lang.String _result = this.getPerson(); 75 reply.writeNoException(); 76 reply.writeString(_result); 77 return true; 78 } 79 } 80 return super.onTransact(code, data, reply, flags); 81 } 82 83 private static class Proxy implements com.example.servicetest02.IPerson { 84 private android.os.IBinder mRemote; 85 86 Proxy(android.os.IBinder remote) { 87 mRemote = remote; 88 } 89 90 @Override 91 public android.os.IBinder asBinder() { 92 return mRemote; 93 } 94 95 public java.lang.String getInterfaceDescriptor() { 96 return DESCRIPTOR; 97 } 98 99 @Override 100 public void setName(java.lang.String name) 101 throws android.os.RemoteException { 102 android.os.Parcel _data = android.os.Parcel.obtain(); 103 android.os.Parcel _reply = android.os.Parcel.obtain(); 104 try { 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 116 public void setSex(java.lang.String sex) 117 throws android.os.RemoteException { 118 android.os.Parcel _data = android.os.Parcel.obtain(); 119 android.os.Parcel _reply = android.os.Parcel.obtain(); 120 try { 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 132 public 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(); 135 try { 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 147 public java.lang.String getPerson() 148 throws android.os.RemoteException { 149 android.os.Parcel _data = android.os.Parcel.obtain(); 150 android.os.Parcel _reply = android.os.Parcel.obtain(); 151 java.lang.String _result; 152 try { 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 } 162 return _result; 163 } 164 } 165 166 static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 167 static final int TRANSACTION_setSex = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); 168 static final int TRANSACTION_setAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); 169 static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); 170 } 171 172 public void setName(java.lang.String name) 173 throws android.os.RemoteException; 174 175 public void setSex(java.lang.String sex) throws android.os.RemoteException; 176 177 public void setAge(int age) throws android.os.RemoteException; 178 179 public java.lang.String getPerson() throws android.os.RemoteException; 180 }
分析:
这个Java文件其实是一个接口,同时生成了在aidl文件中定义的四个方法,并抛出了远程调用的异常。咱们按住Ctrl键,点开上图中蓝框部分的IInterface,查看一下源代码:
能够看到,在IInterface接口里,定义了一个接口IBinder,这是IPC机制的核心接口。
再回来看IPerson.java文件的第9行定义了这样一个抽象类:
上图中的Stub类能够比做存根。Stub类继承了Binder类,同时实现了IPerson接口(没有实现IPerson里的方法)。因此进一步理解为:Stub既是IPerson里的内部类,也是一个IPerson。
(2)新建PersonImpl类,继承IPerson.Stub类,重写父类里的方法。代码以下:(也就是说,根据上面的java类,生成业务对象,即原理图中B应用的业务对象)
1 package com.example.servicetest02; 2 3 import android.os.RemoteException; 4 5 public class PersonImpl extends IPerson.Stub{ 6 7 private String name; 8 private String sex; 9 private int age; 10 11 @Override 12 public void setName(String name) throws RemoteException { 13 // TODO Auto-generated method stub 14 this.name = name; 15 16 } 17 18 @Override 19 public void setSex(String sex) throws RemoteException { 20 // TODO Auto-generated method stub 21 this.sex = sex; 22 23 } 24 @Override 25 public void setAge(int age) throws RemoteException { 26 // TODO Auto-generated method stub 27 this.age = age; 28 29 } 30 31 @Override 32 public String getPerson() throws RemoteException { 33 // TODO Auto-generated method stub 34 return "name="+name+",sex="+sex+",age="+age; 35 } 36 }
(3)新建类MyService,代码以下:
1 package com.example.servicetest02; 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 MyService extends Service { 9 10 public static final String TAG = "MyService"; 11 12 PersonImpl mBinder = new PersonImpl(); 13 14 @Override 15 public void onCreate() { 16 super.onCreate(); 17 Log.d(TAG, "onCreate"); 18 } 19 20 @Override 21 public int onStartCommand(Intent intent, int flags, int startId) { 22 Log.d(TAG, "onStartCommand"); 23 return super.onStartCommand(intent, flags, startId); 24 } 25 26 @Override 27 public void onDestroy() { 28 super.onDestroy(); 29 Log.d(TAG, "onDestroy"); 30 } 31 32 @Override 33 public IBinder onBind(Intent intent) { 34 Log.d("MyService", "onBind"); 35 return mBinder; //在这里返回新建的MyBinder类 36 } 37 38 }
核心代码:12行和35行。
由于PersonImpl类继承了IPerson.Stub,而Stub继承了Binder,Binder又实现了IBinder。因此,PersonImpl能够理解为一个IBinder。因而能够在第35行返回PersonImpl的实例。
(4)在清单文件中添加权限:
<service android:name=".MyService"> </service>
如今,B应用的业务对象和服务创建好了。B应用的业务对象经过与Service绑定,让Service把业务对象暴露给了A应用或者其余的应用。也就是说,Service最终并无实现业务功能。
若是要让A应用来访问,该怎么作呢?
(5)在activity_main.xml中添加两个按钮button_bind_service和button_unbind_service,用于绑定远程服务和取消绑定。activity_main.xml的代码以下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button_bind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="bind Service" /> <Button android:id="@+id/button_unbind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="unbind Service" /> </LinearLayout>
(6)MainActivity中的代码,以下所示:
1 package com.example.servicetest02; 2 3 import android.app.Activity; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.os.RemoteException; 10 import android.util.Log; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.Button; 14 15 public class MainActivity extends Activity implements OnClickListener { 16 17 public static final String TAG = "MainActivity"; 18 private Button button_bind_service; 19 private Button button_unbind_service; 20 21 private IPerson person; 22 23 boolean mBound = false; // 一开始,并无和Service绑定.这个参数是用来判断绑定状态 24 25 // 匿名内部类:服务链接对象 26 private ServiceConnection connection = new ServiceConnection() { 27 28 // 当服务异常终止时会调用。注意,解除绑定服务时不会调用 29 @Override 30 public void onServiceDisconnected(ComponentName name) { 31 mBound = false; // 服务异常终止时,状态为未绑定 32 } 33 34 // 和服务绑定成功后,服务会回调该方法。在这个方法里调用的业务对象中的内容 35 @Override 36 public void onServiceConnected(ComponentName name, IBinder service) { 37 Log.d(TAG, "onServiceConnected"); 38 person = IPerson.Stub.asInterface(service); // 获得person对象 39 Log.d("person", "person对象的内存地址是" + person); // 打印出person对象的内存地址 40 try { 41 person.setName("生命壹号"); 42 person.setAge(22); 43 person.setSex("男"); 44 String p = person.getPerson(); 45 Log.d("person", "person的信息是" + p); 46 } catch (RemoteException e) { 47 // TODO Auto-generated catch block 48 e.printStackTrace(); 49 } 50 mBound = true; //true说明是绑定状态 51 52 } 53 }; 54 55 @Override 56 protected void onCreate(Bundle savedInstanceState) { 57 super.onCreate(savedInstanceState); 58 setContentView(R.layout.activity_main); 59 button_bind_service = (Button) findViewById(R.id.button_bind_service); 60 button_unbind_service = (Button) findViewById(R.id.button_unbind_service); 61 button_bind_service.setOnClickListener(this); 62 button_unbind_service.setOnClickListener(this); 63 64 } 65 66 @Override 67 public void onClick(View v) { 68 switch (v.getId()) { 69 case R.id.button_bind_service: 70 Intent bindIntent = new Intent(this, MyService.class); 71 bindService(bindIntent, connection, BIND_AUTO_CREATE); 72 break; 73 case R.id.button_unbind_service: 74 // 若是和Service是绑定的状态,就解除绑定。 75 if (mBound) { 76 unbindService(connection); 77 mBound = false; 78 } 79 break; 80 81 default: 82 break; 83 } 84 } 85 86 }
核心代码是第38行:能够看到,这里首先使用了MyAIDLService.Stub.asInterface()方法将传入的IBinder对象传换成了IPerson对象,接下来就能够调用在IPerson.aidl文件中定义的全部接口了(41至44行)。调用以后,咱们在后台打印输出。
运行程序,点击按钮,效果以下:
因而可知,咱们确实已经成功实现跨进程通讯了,在一个进程中访问到了另一个进程中的方法。 注意,这个Service是运行在主线程当中的,毕竟咱们是在本地模拟的嘛。
另外注意蓝色箭头处,能够看出,这个person其实就是personImpl,由于是在本地调用。因此说,目前的跨进程通讯其实并无什么实质上的做用,由于这只是在一个Activity里调用了同一个应用程序的Service里的方法。而跨进程通讯的真正意义是为了让一个应用程序去访问另外一个应用程序中的Service,以实现共享Service的功能。那么下面咱们天然要学习一下,如何才能在其它的应用程序中调用到MyService里的方法。
继续回顾第(1)步中自动生成的IPerson.java文件,截取第22至32行,摘抄以下:
1 public static com.example.servicetest02.IPerson asInterface( 2 android.os.IBinder obj) { 3 if ((obj == null)) { 4 return null; 5 } 6 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 7 if (((iin != null) && (iin instanceof com.example.servicetest.IPerson))) { 8 return ((com.example.servicetest.IPerson) iin); 9 } 10 return new com.example.servicetest.IPerson.Stub.Proxy(obj); 11 }
代码解释:
上方第06行的iin表明的是,查询本地对象返回的结果。
07行:若是iin不为空,而且iin为IPerson,那就将iin强制转换为IPerson(08行)。很显然,这里是进程内通讯(本地通讯)要用到的。也就是本段中的例子。
10行:表明的是进程间通讯(远程通讯),此时第07行的if语句不成立,因而返回第10行的代理对象Proxy(obj)。
实现进程间通讯的两个办法:
若是要实现进程间通讯,就必须让MainActivity和Service相互独立。有两个办法:
(1)办法一:将本地的Service设置为远程。只须要在清单文件中注册Service的时候将它的android:process属性指定成:remote就能够了,代码以下所示:
<service android:name=".MyService" android:process=":remote"> </service>
后台打印日志以下:
上图的红框部分显示,Service和Activity并不是在同一个线程内,连包名都不同。而IPeron也并不是是本地的IPeron。
若是将这种方法应用到上面的第四段中,情形是这样的:
点击button1_start_service,服务启动。既然已经将Service的android:process属性指定成:remote,此时Service和Activity不在同一个线程内,那么即便在Service的onStartCommand()方法中执行耗时操做而不从新开启子线程,程序也不会阻塞。
可是,若是点击button3_bind_service按钮绑定服务,程序会崩溃的。这是由于,目前MyService已是一个远程Service了,Activity和Service运行在两个不一样的进程当中,这时就不能再使用传统的创建关联的方式,程序也就崩溃了。
如今咱们总结一下:
第四段中使用的是传统的方式和Service创建关联,默认MainActivity和MyService在同一个线程内,若是将Service的android:process属性指定成:remote,此时MainActivity和MyService将在不一样的线程内,可是没法绑定服务。
本段中(第五段)使用的是IPC跨进程通讯,MainActivity和MyService在不一样的进程中,能够绑定远程服务。
(2)办法二:新建另一个工程,真正实现远程通讯。这就是咱们下一段(第六段)要讲的内容。
咱们仍是先回过头来再巩固一下本段中AIDL的知识吧。
七、AIDL支持的自定义数据类型:
咱们在本段中的第4小结讲到,AIDL支持的类型:八大基本数据类型、String类型、CharSequence、List、Map、自定义,那咱们就来详细说下这个自定义数据类型。
因为这是在不一样的进程之间传递数据,Android对这类数据的格式支持是很是有限的,基本上只能传递Java的基本数据类型、字符串、List或Map等。那么若是我想传递一个自定义的类该怎么办呢?这就必需要让这个类去实现Parcelable接口,而且要给这个类也定义一个同名的AIDL文件进行声明。这部份内容并不复杂,并且和Service关系不大。具体操做以下:
从新建一个工程ServiceTest03。步骤以下:
(1)新建一个Student类去实现Parcelable接口。Student类是做为传递的自定义类:
1 package com.example.servicetest; 2 3 import android.os.Parcel; 4 import android.os.Parcelable; 5 6 public class Student implements Parcelable { 7 private String name; 8 private String sex; 9 10 public Student() { 11 super(); 12 } 13 14 public String getName() { 15 return name; 16 } 17 18 public void setName(String name) { 19 this.name = name; 20 } 21 22 public String getSex() { 23 return sex; 24 } 25 26 public void setSex(String sex) { 27 this.sex = sex; 28 } 29 30 @Override 31 public int describeContents() { 32 // TODO Auto-generated method stub 33 return 0; 34 } 35 36 // 重写父类的方法:将须要传送的数据放进来 37 @Override 38 public void writeToParcel(Parcel dest, int flags) { 39 // TODO Auto-generated method stub 40 dest.writeString(name); 41 dest.writeString(name); 42 } 43 44 public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() { 45 public Student createFromParcel(Parcel in) { 46 Student s = new Student(); 47 s.setName(in.readString()); 48 s.setSex(in.readString()); 49 return s; 50 } 51 52 public Student[] newArray(int size) { 53 return new Student[size]; 54 } 55 }; 56 57 }
咱们在这个类中放入了name和age这两个参数,并实现了Parcelable接口。注意第44行至55行代码的修改。
接着,新建一个和类同名的aidl文件,即新建Student.aidl,代码以下:
parcelable Student;
注意这个parcelable的第一个字母是小写。
继续,新建IStudent.aidl,做为须要远程传递的业务方法。代码以下:
1 package com.example.servicetest03; 2 3 import com.example.servicetest03.Student; 4 interface IStudent{ 5 void setStudent(String name,String sex); 6 Student getStudent(); 7 }
核心代码是第03行,虽然Student类文件和本文件是在同一个包下,可是依然要导包,不然将没法识别Student类。而后在第06行代码中,就能够把Student这个类传递出去了。注意了,第06行返回的是Student类型,这不就是ADIL所支持的自定义类型嘛。
文件结构以下:
综上所述,传递自定义类,有三个步骤:
那么接下来的步骤就和本段中的第6小节同样了,就再也不多解释了,这里只贴代码:
(2)新建StudentImpl类,继承IStudent.Stub类。代码以下:(也就是说,根据步骤(1)中的java类,生成业务对象,即原理图中B应用的业务对象)
1 package com.example.servicetest03; 2 3 import android.os.RemoteException; 4 5 //业务对象的实现 6 public class StudentImpl extends IStudent.Stub{ 7 8 private Student student; 9 10 public StudentImpl(){ 11 student = new Student(); 12 } 13 @Override 14 public void setStudent(String name, String sex) throws RemoteException { 15 // TODO Auto-generated method stub 16 student.setName(name); 17 student.setSex(sex); 18 19 } 20 21 @Override 22 public Student getStudent() throws RemoteException { 23 // TODO Auto-generated method stub 24 return student; 25 } 26 27 }
(3)新建Service类,代码以下:
1 package com.example.servicetest03; 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 MyService extends Service { 9 10 public static final String TAG = "MyService"; 11 12 private StudentImpl studentImpl ; 13 14 @Override 15 public void onCreate() { 16 super.onCreate(); 17 Log.d(TAG, "onCreate"); 18 } 19 20 @Override 21 public int onStartCommand(Intent intent, int flags, int startId) { 22 Log.d(TAG, "onStartCommand"); 23 return super.onStartCommand(intent, flags, startId); 24 } 25 26 @Override 27 public void onDestroy() { 28 super.onDestroy(); 29 Log.d(TAG, "onDestroy"); 30 } 31 32 @Override 33 public IBinder onBind(Intent intent) { 34 Log.d("MyService", "onBind"); 35 studentImpl = new StudentImpl(); 36 return studentImpl; //在这里返回新建的MyBinder类 37 } 38 39 }
核心代码:12行、35行、36行。
(4)在清单文件中添加权限:
<service android:name=".MyService"> </service>
如今,B应用的业务对象和服务创建好了。B应用的业务对象经过与Service绑定,让Service把业务对象暴露给了A应用或者其余的应用。也就是说,Service最终并无实现业务功能。
若是要让A应用来访问,该怎么作呢?
(5)在activity_main.xml中添加两个按钮button1_setStudent和button2_getStudent。activity_main.xml的代码以下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1_setStudent" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="调用setStudent方法" /> <Button android:id="@+id/button2_getStudent" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="调用getStudent方法" /> </LinearLayout>
注:布局文件里再也不添加绑定服务和取消绑定的按钮,咱们稍后在Activity的生命周期里完成这件事。
(6)MainActivity中的代码,以下所示:
1 package com.example.servicetest03; 2 3 import android.app.Activity; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.os.RemoteException; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.widget.Button; 13 import android.widget.Toast; 14 15 16 public class MainActivity extends Activity implements OnClickListener { 17 public static final String TAG = "MainActivity"; 18 private Button button1_setStudent; 19 private Button button2_getStudent; 20 private IStudent studentImpl; 21 boolean mBound = false; // 一开始,并无和Service绑定.这个参数是用来判断绑定状态 22 23 @Override 24 protected void onCreate(Bundle savedInstanceState) { 25 super.onCreate(savedInstanceState); 26 setContentView(R.layout.activity_main); 27 button1_setStudent = (Button) findViewById(R.id.button1_setStudent); 28 button2_getStudent = (Button) findViewById(R.id.button2_getStudent); 29 button1_setStudent.setOnClickListener(this); 30 button2_getStudent.setOnClickListener(this); 31 } 32 33 34 // 匿名内部类:服务链接对象 35 private ServiceConnection connection = new ServiceConnection() { 36 // 当服务异常终止时会调用。注意,解除绑定服务时不会调用 37 @Override 38 public void onServiceDisconnected(ComponentName name) { 39 mBound = false; // 服务异常终止时,状态为未绑定 40 } 41 // 和服务绑定成功后,服务会回调该方法。在这个方法里调用的业务对象中的内容 42 @Override 43 public void onServiceConnected(ComponentName name, IBinder service) { 44 studentImpl = IStudent.Stub.asInterface(service); // 获得person对象 45 mBound = true; //true说明是绑定状态 46 } 47 }; 48 49 //程序启动时,开始绑定服务 50 @Override 51 protected void onStart() { 52 super.onStart(); 53 Intent bindIntent = new Intent(this, MyService.class); 54 bindService(bindIntent, connection, BIND_AUTO_CREATE); 55 } 56 57 //程序退出时,取消绑定服务 58 @Override 59 protected void onDestroy() { 60 super.onDestroy(); 61 // 若是和Service是绑定的状态,就解除绑定。 62 if (mBound) { 63 unbindService(connection); 64 mBound = false; 65 } 66 } 67 68 69 @Override 70 public void onClick(View v) { 71 switch (v.getId()) { 72 //点击button1_setStudent按钮,设置Student的值 73 case R.id.button1_setStudent: 74 try { 75 studentImpl.setStudent("生命壹号", "男"); 76 Toast.makeText(this, "设置成功", Toast.LENGTH_SHORT).show(); 77 } catch (RemoteException e) { 78 // TODO Auto-generated catch block 79 e.printStackTrace(); 80 } 81 break; 82 //点击button2_getStudent按钮,获取Student的值 83 case R.id.button2_getStudent: 84 Student s; 85 try { 86 s = studentImpl.getStudent(); 87 Toast.makeText(this, "name="+s.getName()+",sex="+s.getSex(), Toast.LENGTH_SHORT).show(); 88 } catch (RemoteException e) { 89 // TODO Auto-generated catch block 90 e.printStackTrace(); 91 } 92 break; 93 94 default: 95 break; 96 } 97 } 98 }
核心代码是第44行。
咱们在第75行、86至87行使用到了IStudent中的业务方法。
运行程序,点击第一个按钮,而后点击第二个按钮,效果以下:
这样,Acitivity就成功调用了远程Service的自定义类。
6、使用Bind Service完成IPC进程间通讯:(两个APP之间)
上一段中的跨进程通讯其实并无什么实质上的做用,由于这只是在一个Activity里调用了同一个应用程序的Service里的方法。而跨进程通讯的真正意义是为了让一个应用程序去访问另外一个应用程序中的Service,以实现共享Service的功能。那么下面咱们天然要学习一下,如何才能在其它的应用程序中调用到MyService里的方法。
在第四段中咱们已经知道,若是想要让Activity与Service之间创建关联,须要调用bindService()方法,并将Intent做为参数传递进去,在Intent里指定好要绑定的Service,核心代码以下:
Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE);
这里在构建Intent的时候是使用MyService.class来指定要绑定哪个Service的,可是在另外一个应用程序中去绑定Service的时候并无MyService这个类,这时就必须使用到隐式Intent了。
具体步骤以下:
咱们在第六段中的MyService02这个工程文件中进行修改。代码实现以下:
(1)如今修改AndroidManifest.xml中的代码,给MyService加上一个action,以下所示:
1 <service android:name=".MyService" > 2 <intent-filter> 3 <action android:name="com.example.servicetest02.MyService" /> 4 </intent-filter> 5 </service>
这就说明,MyService能够响应带有com.example.servicetest02.MyService这个action的Intent。
如今从新运行一下MyService02这个程序,这样就把远程Service端的工做所有完成了。
而后新建一个新的工程,起名为ClientTest,咱们就尝试在这个程序中远程调用MyService中的方法。
ClientTest中的Activity若是想要和MyService创建关联其实也不难,首先须要将IPerson.aidl文件从ServiceTest02项目中拷贝过来,注意要将原有的包路径一块儿拷贝过来,完成后项目的结构以下图所示:
(2)在activity_main.xml中添加两个按钮button_bind_service和button_unbind_service,用于绑定远程服务和取消绑定。activity_main.xml的代码以下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button_bind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="bind Service" /> <Button android:id="@+id/button_unbind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="unbind Service" /> </LinearLayout>
(3)在MainActivity中加入和远程的MyService创建关联的代码,以下所示:
1 package com.example.clienttest; 2 3 import android.app.Activity; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.os.RemoteException; 10 import android.util.Log; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.Button; 14 15 import com.example.servicetest02.IPerson; 16 17 18 public class MainActivity extends Activity implements OnClickListener { 19 public static final String TAG = "MainActivity"; 20 private Button button_bind_service; 21 private Button button_unbind_service; 22 private IPerson person; 23 boolean mBound = false; // 一开始,并无和Service绑定.这个参数是用来判断绑定状态 24 // 匿名内部类:服务链接对象 25 private ServiceConnection connection = new ServiceConnection() { 26 // 当服务异常终止时会调用。注意,解除绑定服务时不会调用 27 @Override 28 public void onServiceDisconnected(ComponentName name) { 29 mBound = false; // 服务异常终止时,状态为未绑定 30 } 31 // 和服务绑定成功后,服务会回调该方法。在这个方法里调用的业务对象中的内容 32 @Override 33 public void onServiceConnected(ComponentName name, IBinder service) { 34 Log.d(TAG, "onServiceConnected"); 35 person = IPerson.Stub.asInterface(service); // 获得person对象 36 Log.d("person", "person对象的内存地址是" + person); // 打印出person对象的内存地址 37 try { 38 person.setName("生命壹号"); 39 person.setAge(22); 40 person.setSex("男"); 41 String p = person.getPerson(); 42 Log.d("person", "person的信息是" + p); 43 } catch (RemoteException e) { 44 // TODO Auto-generated catch block 45 e.printStackTrace(); 46 } 47 mBound = true; //true说明是绑定状态 48 } 49 }; 50 @Override 51 protected void onCreate(Bundle savedInstanceState) { 52 super.onCreate(savedInstanceState); 53 setContentView(R.layout.activity_main); 54 button_bind_service = (Button) findViewById(R.id.button_bind_service); 55 button_unbind_service = (Button) findViewById(R.id.button_unbind_service); 56 button_bind_service.setOnClickListener(this); 57 button_unbind_service.setOnClickListener(this); 58 } 59 @Override 60 public void onClick(View v) { 61 switch (v.getId()) { 62 case R.id.button_bind_service: 63 Intent bindIntent = new Intent("com.example.servicetest02.MyService"); 64 bindService(bindIntent, connection, BIND_AUTO_CREATE); 65 break; 66 case R.id.button_unbind_service: 67 // 若是和Service是绑定的状态,就解除绑定。 68 if (mBound) { 69 unbindService(connection); 70 mBound = false; 71 } 72 break; 73 default: 74 break; 75 } 76 } 77 }
这部分代码你们必定会很是眼熟吧?没错,这和在ServiceTest02的MainActivity中的代码几乎是彻底相同的,只是在让Activity和Service创建关联的时候咱们使用了隐式Intent,将Intent的action指定成了com.example.servicetest02.MyAIDLService(63行)。
在当前Activity和MyService创建关联以后,咱们仍然是调用了setName、setAge、setSex、getPerson()这几个方法,远程的MyService会对传入的参数进行处理并返回结果,而后将结果打印出来。
这样的话,ClientTest中的代码也就所有完成了,如今运行一下这个项目,而后点击Bind Service按钮,此时就会去和远程的MyService创建关联,观察LogCat中的打印信息以下所示:
注意红框部分,包名是不同的哦。因而可知,咱们确实已经成功实现跨进程通讯了,在一个程序中访问到了另一个程序中的方法。
7、Messenger的使用:
public final class Messenger extends Object implements Parcelable
介绍:Messenger实现了IPC通讯,底层也是使用了AIDL方式。和AIDL方式不一样的是,Messenger方式是利用Handler形式处理,所以,它是线程安全的,这也表示它不支持并发处理;而AIDL方式是非线程安全的,支持并发处理,所以,咱们使用AIDL方式时,须要保证代码的线程安全。大部分状况下,应用中不须要并发处理,所以咱们一般只须要使用Messenger方式。
过程:在进程A中建立一个Message,将这个Message对象经过Messenger.send(message)方法传递到进程B的消息队列里,而后交给Handler去处理。
固然,Message对象自己是没法被传递到进程B的,send(message)方法会使用一个Pacel对象对Message对象编集,再将Pacel对象传递到进程B中,而后解编集,获得一个和进程A中的Message对象内容同样的对象。
关于多线程的Handler机制,若是不清楚的话,能够参考本人另一篇博客:
使用Messenger来实现IPC的步骤:
那咱们经过代码来实现如下吧。新建一个全新的工程MessengerTest。步骤以下:
(1)新建一个MessengerService类,继承Service类,代码以下:
1 package com.example.messengertest; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Handler; 6 import android.os.IBinder; 7 import android.os.Message; 8 import android.os.Messenger; 9 import android.util.Log; 10 import android.widget.Toast; 11 12 public class MessengerService extends Service{ 13 14 public static final int MSG_SAY_HELLO = 1; 15 16 private Handler handler = new Handler() { 17 public void handleMessage(Message msg) { 18 switch (msg.what) { 19 case MSG_SAY_HELLO: 20 //在服务里定义业务方法 21 Toast.makeText(MessengerService.this, "hello", Toast.LENGTH_SHORT).show(); 22 Log.d("MessengerService", "MessengerService thread id is " + Thread.currentThread().getId()); //打印MessengerService的线程id 23 break; 24 default: 25 break; 26 } 27 } 28 }; 29 30 31 private Messenger messenger = new Messenger(handler); 32 33 34 @Override 35 public IBinder onBind(Intent intent) { 36 // TODO Auto-generated method stub 37 return messenger.getBinder(); 38 } 39 }
核心代码:16至28行、31行、37行。
37行中,将IBinder类型返回以后,就已经和Messenger进行绑定了。
(2)在清单文件中注册服务:(和Activity标签并列)
<service android:name=".MessengerService"> </service>
(3)修改activity_main.xml代码,添加一个按钮,用于发送Message,代码以下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button_messenger" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="使用messenger" /> </LinearLayout>
(4)在MainActivity做为程序的主Activity,在里面加入发送Message消息和创建Service链接的逻辑,代码以下:
1 package com.example.messengertest; 2 3 import android.app.Activity; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.os.Message; 10 import android.os.Messenger; 11 import android.os.RemoteException; 12 import android.util.Log; 13 import android.view.View; 14 import android.view.View.OnClickListener; 15 import android.widget.Button; 16 17 public class MainActivity extends Activity implements OnClickListener { 18 19 private Button button_messenger; 20 21 private Messenger messenger; 22 boolean mBound = false; 23 24 @Override 25 protected void onCreate(Bundle savedInstanceState) { 26 super.onCreate(savedInstanceState); 27 setContentView(R.layout.activity_main); 28 button_messenger = (Button) findViewById(R.id.button_messenger); 29 button_messenger.setOnClickListener(this); 30 } 31 32 @Override 33 protected void onStart() { 34 // TODO Auto-generated method stub 35 super.onStart(); 36 Intent bindIntent = new Intent(this, MessengerService.class); 37 bindService(bindIntent, connection, BIND_AUTO_CREATE); 38 } 39 40 @Override 41 protected void onDestroy() { 42 // TODO Auto-generated method stub 43 super.onDestroy(); 44 if (mBound) { 45 unbindService(connection); 46 mBound = false; 47 } 48 } 49 50 private ServiceConnection connection = new ServiceConnection() { 51 @Override 52 public void onServiceConnected(ComponentName name, IBinder service) { 53 // TODO Auto-generated method stub 54 messenger = new Messenger(service); 55 56 mBound = true; 57 } 58 59 @Override 60 public void onServiceDisconnected(ComponentName name) { 61 // TODO Auto-generated method stub 62 mBound = false; 63 64 } 65 66 }; 67 68 //点击按钮,发送Message消息,在MessengerService里接收,从而执行Service里面的方法 69 @Override 70 public void onClick(View v) { 71 switch (v.getId()) { 72 case R.id.button_messenger: 73 Message msg = Message.obtain(); 74 msg.what = MessengerService.MSG_SAY_HELLO; 75 try { 76 messenger.send(msg); 77 Log.d("MainActivity", "MainActivity thread id is " + Thread.currentThread().getId()); //打印MainActivity的线程id 78 } catch (RemoteException e) { 79 // TODO Auto-generated catch block 80 e.printStackTrace(); 81 } 82 break; 83 84 default: 85 break; 86 } 87 } 88 89 }
咱们在上一步的MessengerService类新建了一个Messenger,在这里又新建另外一个Messenger(54行)。两个Messenger绑定了同一个服务,Activity就能够和Service实现通信了。
点击按钮(72行),发送消息,让MessengerService类里的Messenger去接收,而后交给handler去处理,从而执行handleMessage()里方法,也就是说,执行了Service里面的方法。
运行程序,点击按钮,显示效果以下:
后台打印日志以下:
说明这个MessengerService和普通Service同样,也是运行在主线程当中的。
固然了,这里的Messenger的实现比较简单,若是之后须要实现复杂IPC访问,仍是须要本身去写AIDL才更加直接有效,可控性强。