您的位置:首页 > 移动开发 > Android开发

AndroidStudio通过AIDL开启、绑定远程Service

2016-04-17 15:25 567 查看

前言

  关于服务的启动方式(startService()、stopService()、bindService()、unbindService()),我这里就不多说了,可以参考这篇博文

示例原理图

  本文以一个简单的案例,记录一下怎么使用AIDL结合服务实现进程间的通信:

  首先,创建两个项目,一个项目(RemoteService)作为远程服务提供端,另一个(RemoteServiceTest)作为调用远程服务的客户端.然后,当客户端绑定远程服务后,可以通过AIDL接口调用远程服务中的方法,原理过程如图:



远程服务RemoteService 项目

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对象中的方法来间接调用服务中的方法。

客户端RemoteServiceTest 项目:

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);必须设置为服务所在的包名,否则是不能开启服务的,而且没有任何错误或警告给出。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  aidl android Service