标签:
关于服务的启动方式(startService()、stopService()、bindService()、unbindService()),我这里就不多说了,可以参考这篇博文。
本文以一个简单的案例,记录一下怎么使用AIDL结合服务实现进程间的通信:
首先,创建两个项目,一个项目(RemoteService)作为远程服务提供端,另一个(RemoteServiceTest)作为调用远程服务的客户端.然后,当客户端绑定远程服务后,可以通过AIDL接口调用远程服务中的方法,原理过程如图:
1.创建AIDL文件,选中要提供的服务类所在的包名,右键 -> New -> AIDL -> AIDL File文件,如图
这里将AIDL文件命名为RemoteInterface,创建完成后,会在main下生成一个aidl文件夹,一个包名与刚刚选中的包名相同的包,包下生成了一个RemoteInterface.aidl,如图
2.打开RemoteInterface.aidl文件,定义一个接口方法 remotePrintInterface(); 重新编译一下项目,如果成功编译那么会在如下目录中生成一个RemoteInterface接口文件
注意不要修改这个接口文件,接口中有一个重要的 Stub类,是之后要用的,Stub类继承了Binder,并实现了RemoteInterface接口。
3.创建一个服务类如RemoteService让其继承自Service, 服务中定义一个方法remotePrint(),并定义一个内部类 MyBinder 继承自 Stub, 实现Stub或者说RemoteInterface接口中的remotePrintInterface()的方法,让这个方法去调用服务的remotePrint()方法,代码如下:
package com.yu.remoteservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.Toast;
/**
* Created by yu on 2016/4/15.
*/
public class RemoteService extends Service {
private static final String TAG = "RemoteService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"RemoteService 绑定 ------------------------------------------");
Toast.makeText(this,"远程服务绑定",Toast.LENGTH_SHORT).show();
return new MyBinder();//一定要记得返回 MyBinder 对象
}
//内部类继承自Stub
class MyBinder extends RemoteInterface.Stub{
//实现RemoteInterface中的方法
@Override
public void remotePrintInterface() {
//调用服务的方法
remotePrint();
}
}
public void remotePrint() {
Log.d(TAG,"通过接口调用 RemoteService 的 remotePrint 方法");
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"RemoteService 创建 ------------------------------------------");
Toast.makeText(this,"远程服务创建",Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"RemoteService 销毁 ------------------------------------------");
Toast.makeText(this,"远程服务销毁",Toast.LENGTH_SHORT).show();
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG,"RemoteService 解绑 ------------------------------------------");
Toast.makeText(this,"远程服务绑定",Toast.LENGTH_SHORT).show();
return super.onUnbind(intent);
}
}
4.在清单文件中注册服务
<service android:name=".RemoteService">
<intent-filter>
<action android:name="com.yu.remote"/>
</intent-filter>
</service>
通过上面的步骤,服务端就算码完了,客户端绑定服务后,可以通过onBind方法返回的MyBinder对象中的方法来间接调用服务中的方法。
1.目的是要在main下生成一个跟RemoteService中的aidl一毛一样的文件,做法是:
1)在main下创建一个aidl文件夹
2)在aidl文件夹下创建一个包,包名跟服务端aidl文件所在的包名一样,此处即为com.yu.remoteservice
3)拷贝服务端的RemoteInterface.aidl文件到包下。
4)rebuild project,结束后,应该会生成和服务端一毛一样的接口
结果如图
2.绑定服务,得到服务返回的Ibinder对象,强转为接口的类型,并调用接口的方法,去间接调用服务中的方法
客户端代码如下:
package com.yu.remoteservicetest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.yu.remoteservice.RemoteInterface;
public class MainActivity extends AppCompatActivity {
private final String TAG = "RemoteServiceTest";
//远程服务的包名
private final String RemoteServicePackage = "com.yu.remoteservice";
//绑定服务时的连接对象
private ServiceConnection conn = null;
//通过aidl创建的接口类的对象
private RemoteInterface ri = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void start(View view) {
Intent intent = new Intent();
//设置意图对象要匹配的action
intent.setAction("com.yu.remote");
//android 5.0之后
//设置包名,这里测试了一下,必须是服务所在的包名才可以
intent.setPackage(RemoteServicePackage);
startService(intent);
}
public void stop(View view) {
Intent intent = new Intent();
intent.setAction("com.yu.remote");
intent.setPackage(RemoteServicePackage);
stopService(intent);
}
//绑定服务
public void bind(View view) {
Intent intent = new Intent();
intent.setAction("com.yu.remote");
intent.setPackage(RemoteServicePackage);
if (conn == null) {
//创建连接服务的对象
conn = new RemoteServiceConnection();
}
bindService(intent,conn,BIND_AUTO_CREATE);
}
public void unbind(View view) {
if (conn != null) {
unbindService(conn);
conn = null;
}
}
//调用服务的方法
public void callRemotePrint(View view) {
Log.d(TAG,"callRemotePrint........................");
if (ri != null) {
try {
//通过调用接口对象的remotePrintInterface方法,简介调用服务的remotePrint方法
ri.remotePrintInterface();
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Toast.makeText(this,"remote interface is null",Toast.LENGTH_SHORT).show();
}
}
//实现ServiceConnection类,用于创建连接服务的对象
class RemoteServiceConnection implements ServiceConnection {
//当使用bindService方法绑定服务,并且服务返回IBinder对象的时候会调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG,"onServiceConnected........................");
//强转为接口对象
ri = RemoteInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (conn != null) {
unbindService(conn);
conn = null;
}
}
}
客户端界面
客户端文件目录结构
远程服务文件目录结构
1.在创建aidl文件后,没有去自动生成相应的接口,reBuild后报以下错误:
Error:Execution failed for task ‘:app:compileDebugAidl’.
java.lang.RuntimeException:
com.android.ide.common.process.ProcessException:
org.gradle.process.internal.ExecException: Process ‘command
‘F:\Study\Android\soft\adt-bundle-windows-x86_64-20140321\sdk\build-tools\24.0.0-preview\aidl.exe”
finished with non-zero exit value 1
搜了一下,大概是说 项目的编译版本和编译工具的版本不一致。。。蛋碎。。。
最后解决的办法是:
右键项目 open Moudle Setting -> app -> 修改Compile Sdk Version 和Build Tools Version 版本一致。
2.无论是startService还是bindService都没有能够启动远程服务:
原因是android5.0以后表面上是不能隐式启动Service的,参考这篇文章
而且,intent.setPackage(RemoteServicePackage);必须设置为服务所在的包名,否则是不能开启服务的,而且没有任何错误或警告给出。。。
AndroidStudio通过AIDL开启、绑定远程Service
标签:
原文地址:http://blog.csdn.net/man_help/article/details/51173450