什么是远程服务?
远程服务(Remote Service)也被称之为独立进程,它不受其它进程影响,可以为其它应用程序提供调用的接口——实际上就是进程间通信IPC(Inter-Process Communication),Android提供了AIDL(Android Interface Definition Language,接口描述语言)工具来帮助进程间接口的建立。
在Android中,不同的应用属于不同的进程(Process),一个进程不能访问其他进程的存储(可以通过ContentProvider实现)。
优点:1.远程服务有自己的独立进程,不会受到其它进程的影响;
2.可以被其它进程复用,提供公共服务;
3.具有很高的灵活性。
缺点:相对普通服务,占用系统资源较多,使用AIDL进行IPC也相对麻烦。
1、首先我们来简单深入了解远程服务,在此我们新建一个demo,在它的布局文件中增加两个ExitText文本域,再增加一个Button按钮。代码如下:
2、新建一个类(LoginService.java)继承Service,重写它的onBind()方法;写一个类继承Binder,实现一个自定义接口,实现业务方法,在onBind()方法中返回该类对象。在代码如下:
public class LoginService extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("test", "onBind: ");
//如果你需要绑定服务,就需要返回值,否则默认为null
return new MyIbinder();
}
class MyIbinder extends Binder implements LoginInterface{
@Override
public boolean login(String name, String pwd) {
if ("sunny".equals(name) && "123".equals(pwd)) {//判断用户名和密码:如果密码正确就登录成功,否则失败
return true;
}
return false;
}
// public boolean login(String name,String pwd){
// if ("sunny".equals(name)&&"123".equals(pwd)) {//判断用户名和密码:如果密码正确就登录成功,否则失败
// return true;
// }
// return false;
// }
// }
}
在清单文件AndroidManifest.xml
中对LoginService.java中配置如下所示:
3、新建一个自定义接口(LoginInterface),定义方法和属性。在LoginService.java中写一个类实现这个接口(LoginInterface)。代码如下:
package com.example.administrator.android_servicemain;
/**
* Created by Administrator on 2017/2/12.
*/
public interface LoginInterface {
public boolean login(String name,String pwd);
}
4、
在客户端MainActivity.java中与LoginService.java这个类绑定,传递ServiceConnection用来接收onBind()方法返回的Ibinder,强转为接口类型,就可以调用这个方法。绑定服务中有三个参数:<1>、需要Intent对象。<2>、Connection是远程服务的对象,可以使用匿名内部类(ServiceConnection)的方式实现。<3>、对于绑定操作,一般用BIDN_AUTO_CREATE;这个服务会在你绑定时自动创建。代码如下:
public class MainActivity extends AppCompatActivity {
private EditText et_main_name;
private EditText et_main_password;
private Intent intent;
private LoginService.MyIbinder myibinder;
private LoginInterface loginInterface;
private LoginInterfaceOut loginInterfaceOut;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取文本域的值
et_main_name = (EditText) findViewById(R.id.et_main_name);
et_main_password = (EditText) findViewById(R.id.et_main_password);
intent = new Intent(this,LoginService.class);//开始绑定服务
}
//实列化一个对象
ServiceConnection serviceConnection=new ServiceConnection() {
@Override//绑定成功
public void onServiceConnected(ComponentName name, IBinder ibinder) {
loginInterface = (LoginInterface) ibinder;//这个接口就是等于你服务中的ibinder,服务中的ininder的实现了LoginInterface
这个接口,所以可以直接替换。 // myibinder = (LoginService.MyIbinder) ibinder;
Log.i("test", "onServiceConnected: 绑定成功!");
}
@Override//绑定失败
public void onServiceDisconnected(ComponentName name) {
Log.i("test", "onServiceConnected: 绑定失败!");
}
};
//绑定服务
@Override
protected void onResume() {
//Service.BIND_AUTO_CREATE:这个服务在你绑定的时自动创建
bindService(intent,serviceConnection, Service.BIND_AUTO_CREATE);
super.onResume();
}
//按钮的点击事件
public void login(View view){
//获取文本框的用户名和密码
String name=et_main_name.getText().toString();
String pwd=et_main_password.getText().toString();
boolean flag= loginInterface.login(name,pwd);
// boolean flag= myibinder.login(name,pwd);
Toast.makeText(MainActivity.this, ""+ flag , Toast.LENGTH_SHORT).show();
// if ("sunny".equals(name)&&"123".equals(pwd)) {//判断用户名和密码:如果密码正确就登录成功,否则失败
// Toast.makeText(MainActivity.this, "" + name + " " + pwd, Toast.LENGTH_SHORT).show();
// }else {
// Toast.makeText(MainActivity.this, "登录失败!用户名或密码输入错误~~", Toast.LENGTH_SHORT).show();
// }
}
}
5、使用AIDL(Android interface define language):Android接口定义语言。可以在Android设备上两个进程之间进行进程间通信(interpricess communication,IPC)。新建一个aidl文件,然后将demo编译,它会自动生成aidl相对应的接口的java类。代码如下:
// LoginInterfaceOut.aidl
package com.example.administrator.android_servicemain;
interface LoginInterfaceOut {
boolean login(String name, String pwd);
}
然后MainActivity.java和LoginService.java代码同上,只需要修改代码即可。(1)在LoginService.java写一个类,继承LoginInterfaceOut.Stub,并重写它的方法。代码如下:
//AIDL:Android interface define language-->安卓接口定义语言
public class LoginService extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("test", "onBind: ");
//如果你需要绑定服务,就需要返回值,否则默认为null
return new MyIbinder();
}
class MyIbinder extends LoginInterfaceOut.Stub{//写一个类继承LoginInterfaceOut这个接口
@Override
public boolean login(String name, String pwd) throws RemoteException {
if ("sunny".equals(name) && "123".equals(pwd)) {//判断用户名和密码:如果密码正确就登录成功,否则失败
return true;
}
return false;
}
}
}
(2)MainActivity.java:(
1、在连接之后可以获取服务对象;(2、必须用asinterface方法得到LoginService对象。
代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取文本域的值
et_main_name = (EditText) findViewById(R.id.et_main_name);
et_main_password = (EditText) findViewById(R.id.et_main_password);
intent = new Intent(this,LoginService.class);//开始绑定服务
}
//实列化一个对象
ServiceConnection serviceConnection=new ServiceConnection() {
@Override//绑定成功
public void onServiceConnected(ComponentName name, IBinder ibinder) {
loginInterfaceOut = LoginInterfaceOut.Stub.asInterface(ibinder);
Log.i("test", "onServiceConnected: 绑定成功!");
}
@Override//绑定失败
public void onServiceDisconnected(ComponentName name) {
Log.i("test", "onServiceConnected: 绑定失败!");
}
};
//绑定服务
@Override
protected void onResume() {
//Service.BIND_AUTO_CREATE:这个服务在你绑定的时自动创建
bindService(intent,serviceConnection, Service.BIND_AUTO_CREATE);
super.onResume();
}
//按钮的点击事件
public void login(View view){
//获取文本域的用户名和密码
String name=et_main_name.getText().toString();
String pwd=et_main_password.getText().toString();
boolean flag= false;
try {
flag = loginInterfaceOut.login(name,pwd);
} catch (RemoteException e) {
e.printStackTrace();
}
Toast.makeText(MainActivity.this, ""+ flag , Toast.LENGTH_SHORT).show();
}
}
6、
再新建一个demo,将你要通信的AIDL产生的(LoginInterfaceOut)java类复制到demo中。其中demo的布局文件与上述一样,这时我们需要在MianActivity主activity中写入ComponentName componentName=new ComponentName("与进程进行通讯的包名","与进程进行通讯的包名下的服务名");intent.setComponent(componentName);多进程间启动Services;Android 5.0 之后,启动其他应用程序的服务,不允许使用隐式。代码如下:
public class MainActivity extends AppCompatActivity {
private EditText et_main_name;
private EditText et_main_password;
private LoginInterfaceOut loginInterfaceOut;
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取文本域的值
et_main_name = (EditText) findViewById(R.id.et_main_name);
et_main_password = (EditText) findViewById(R.id.et_main_password);
intent = new Intent();
ComponentName componentName=new ComponentName("com.example.administrator.android_servicemain","com.example.administrator.android_servicemain.LoginService");
intent.setComponent(componentName);
}
ServiceConnection serviceConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder ibinder) {
loginInterfaceOut = LoginInterfaceOut.Stub.asInterface(ibinder);
Log.i("test", "onServiceConnected: 绑定成功!");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test", "onServiceConnected: 绑定失败!");
}
};
//绑定服务
@Override
protected void onResume() {
//Service.BIND_AUTO_CREATE:这个服务在你绑定的时自动创建
bindService(intent,serviceConnection, Service.BIND_AUTO_CREATE);
super.onResume();
}
//按钮的点击事件
public void login(View view){
//获取文本框的用户名和密码
String name=et_main_name.getText().toString();
String pwd=et_main_password.getText().toString();
boolean flag= false;
try {
flag = loginInterfaceOut.login(name,pwd);
} catch (RemoteException e) {
e.printStackTrace();
}
Toast.makeText(MainActivity.this, ""+ flag , Toast.LENGTH_SHORT).show();
}
}
7、总结:
<2、生命周期:多个组件可以绑定同一个远程服务,当所有的组件与远程服务解除绑定的时,远程服务也就会被销毁。
<3、AIDL(Android interface define language):Android接口定义语言。可以进行进程间的通信。
<4、LoginInterfaceOut.Stub是根据LoginInterfaceOut.aidl文件自动生成的,一般不需要去了解这个类的内容,只需写一个类继承LoginInterfaceOut.Stub即可。
<5、onBinder()方法必须返回LoginInterfaceOut.Stub类的子类,否则客户端无法获取服务对象。