在讲解AIDL之前,我们需要梳理一下Service的使用,Service是Android系统中的四大组件之一,主要有两个应用场景:后台运行和跨进程访问。Service可以在后台执行长时间运行操作而不提供用户界面,除非系统必须回收内存资源,否则系统不会停止或销毁服务。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。 此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。
需要注意的是:
(1)Service是在主线程里执行操作的,可能会因为执行耗时操作而导致ANR,所以耗时操作可以在Service中开一个子线程来处理;
(2)Service可能会被系统回收,在使用Service时别忘了判断服务是否正在运行;
一、Service的三种使用方式
(1)使用startService启动服务,使用stopService停止服务
1.定义MyService
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("yunchong", "====onBind===");
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d("yunchong", "====onCreate==="+MyService.class.getName());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("yunchong", "====onStartCommand===");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("yunchong", "====onDestroy===");
}
}
服务的主要逻辑一般在onStartCommand执行。
2.在AndroidManifest注册服务
<service android:name=".MyService"/>
这里可以配置service的一些配置,本文中不做解释。
3.启动服务和停止服务
public class MainActivity extends AppCompatActivity {
private Button button1, button2, button3, button4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setOnClick();
}
private void initView(){
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button4 = findViewById(R.id.button4);
}
private void setOnClick(){
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//启动服务
startService(new Intent(MainActivity.this, MyService.class));
}
});
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//停止服务
stopService(new Intent(MainActivity.this, MyService.class));
}
});
}
}
4.Service的生命周期
第一次启动服务:onCreate-->onStartCommand
非第一次启动服务:onStartCommand
停止服务:onDestroy
这是最简单的使用方式。
(2)使用bindService绑定服务,使用unbindService解绑服务
1.定义MyService
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("yunchong", "====onBind===");
return new MyBind();
}
@Override
public void onCreate() {
super.onCreate();
Log.d("yunchong", "====onCreate==="+MyService.class.getName());
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("yunchong", "====onUnbind===");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
Log.d("yunchong", "====onRebind===");
super.onRebind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("yunchong", "====onDestroy===");
}
public class MyBind extends Binder{
//加法运算
public int addOperation(int a, int b){
return a + b;
}
//减法运算
public int subOperation(int a, int b){
return a - b;
}
}
}
服务的主要逻辑由MyBind对象来实现, Activity可以调用该对象中的方法实现某功能,而且还可以将返回值返回给客户端, 这样更加方便的实现了Activity和Service的交互。
2.在AndroidManifest注册服务
<service android:name=".MyService"/>
这里可以配置service的一些配置,本文中不做解释。
3.绑定服务和解绑服务服务
第一次点击“绑定服务”,执行顺序是:onCreate-->onBind-->onServiceConnected回调方法
非第一次点击“绑定服务”,则不执行以上方法, 官方的解释是,一个客户端只能绑定一次。
点击“解绑服务”,则解除绑定,执行顺序是onUnbind-->onDestroy
,这时再点击“绑定服务”就相当于第一次点击“绑定服务”了。
第一种直接启动的方式,服务的业务逻辑一般都是在onStartCommand中处理的,而绑定服务方式的业务逻辑都是自定义的Binder对象中,本文定义了MyBind对象, 有加法和减法的逻辑, 那么怎么运用这两个业务呢?
public class MainActivity extends AppCompatActivity {
private Button button2, button3, button5, button6;
private TextView addText, subText;
private MyService.MyBind myService;
private boolean isRegistered = false;//服务是否被注册
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("yunchong", "onServiceConnected");
myService = (MyService.MyBind)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
Log.d("yunchong", "服务已经断开了连接!");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setOnClick();
}
private void initView(){
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button5 = findViewById(R.id.button5);
button6 = findViewById(R.id.button6);
addText = findViewById(R.id.addText);
subText = findViewById(R.id.subText);
}
private void setOnClick(){
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//绑定服务,如果返回true,则说明服务已经被注册
isRegistered = bindService(new Intent(MainActivity.this, MyService.class), connection, BIND_AUTO_CREATE);
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(isRegistered){
//解绑服务,第一次接触绑定服务会被销毁,如果解除一个已被销毁的服务,那么将会报错
unbindService(connection);
isRegistered = false; }
}
});
button5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//加法运算
if(myService != null){
addText.setText(String.valueOf(myService.addOperation(10, 2)));
}
}
});
button6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//减法运算
if(myService != null){
subText.setText(String.valueOf(myService.subOperation(10, 2)));
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if(isRegistered){
//解绑服务
unbindService(connection);
isRegistered =false;
}
}
}
通过ServiceConnection的onServiceConnected方法回调获取MyBind对象, 然后用该对象调用对象中的业务。
看图:
4.Service的生命周期
第一次启动服务:onCreate-->onBind
非第一次启动服务:无生命周期
停止服务: onUnbind-->onDestroy
(3)startService和bindService混用
如图, 新增按钮“启动且绑定服务”和“解绑服务”。
关键代码如下:
//启动且绑定服务
Intent intent = new Intent(MainActivity.this, MyService.class);
startService(intent);//启动服务
isRegistered = bindService(intent, connection, BIND_AUTO_CREATE);//绑定服务,如果返回true,则说明服务已经被注册
生命周期是:
第一次点击“启动且绑定”按钮:onCreate-->onStartCommand-->onBind
非第一次点击“启动且绑定”按钮:onStartCommand
解除绑定服务
if(isRegistered){
//解绑服务
unbindService(connection);
isRegistered =false;
}
在启动并绑定服务的前提下,点击“解绑服务”按钮:onUnbind
在解绑服务的前提下,点击“停止服务”按钮:onDestroy
如果使用这种方式, 只有先unbindService再stopService才能真正销毁服务,所以从启动服务到停止服务的流程是:
startService-->bindService-->unbindService-->stopService
最后介绍一下Service的onRebind回调,上面说了那么多,onRebind一直没有用到。
首先我们改写一下Service的onUnbind回调方法
将
@Override
public boolean onUnbind(Intent intent) {
Log.d("yunchong", "====onUnbind===");
return super.onUnbind(intent);
}
改成
@Override
public boolean onUnbind(Intent intent) {
Log.d("yunchong", "====onUnbind===");
return true;
}
然后,当操作顺序是启动-->绑定-->解绑-->绑定
时,onRebind将被执行。
也就是说,onRebind的执行条件是:
(1)Service类中的onUnbind回调返回值为true;
(2)服务依然是启动状态,没有被销毁,并且服务二次绑定的时候;
二、AIDL基本使用
AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此而生成的一个IInterface的实例代码,AIDL其实是为了避免我们重复编写代码而出现的一个模板
设计AIDL这门语言的目的就是为了实现进程间通信。在Android系统中,每个进程都运行在一块独立的内存中,在其中完成自己的各项活动,与其他进程都分隔开来。可是有时候我们又有应用间进行互动的需求,比较传递数据或者任务委托等,AIDL就是为了满足这种需求而诞生的。通过AIDL,可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法,从而满足进程间通信的需求
通常,暴露方法给其他应用进行调用的应用称为服务端,调用其他应用的方法的应用称为客户端,客户端通过绑定服务端的Service来进行交互。
需要注意的是:
(1)AIDL文件以 .aidl 为后缀名
(2)AIDL支持的数据类型分为如下几种:
八种基本数据类型:byte、char、short、int、long、float、double、boolean
String,CharSequence
实现了Parcelable接口的数据类型
List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
(3)AIDL文件可以分为两类。一类用来声明实现了Parcelable接口的数据类型,以供其他AIDL文件使用那些非默认支持的数据类型。还有一类是用来定义接口方法,声明要暴露哪些接口给客户端调用,定向Tag就是用来标注这些方法的参数值
定向Tag。定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。此外,如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag。
(4)明确导包。在AIDL文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下。
要想实现AIDL,需要准备两个进程, 一个作为服务端进程,一个作为客户端进程。
(1)我们新建项目,搭建服务端代码
由于服务进程是另起一个单独的app, 所以我们需要做到以下几点:
* 隐藏桌面图标
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/TranslucentTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<data
android:host="myhost"
android:scheme="myscheme"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
核心代码是:
<data
android:host="myhost"
android:scheme="myscheme"/>
只要在AndroidManifest中添加以上代码就可以隐藏桌面图标。
* 启动该app时界面完全透明
去除Activity默认的布局代码
setContentView(R.layout.activity_main);//直接删掉,不要了
在配置文件中设置当前Activity为透明主题
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/TranslucentTheme">
<style name="TranslucentTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionBar">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
* 让当前Activity可以被其他应用调用
准备工作做好之后, 开始进入正题:
(1)服务端目录结构
(2)新建People对象,让他支持Parcelable
public class People implements Parcelable {
private String name;
public People(Parcel in) {
this.name = in.readString();
}
public People(String name) {
this.name = name;
}
public People() {
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
}
public void readFromParcel(Parcel dest) {
name = dest.readString();
}
public static final Creator<People> CREATOR = new Creator<People>() {
@Override
public People createFromParcel(Parcel source) {
return new People(source);
}
@Override
public People[] newArray(int size) {
return new People[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
'}';
}
}
(3)新建People.aidl文件, 用来声明Parcelable接口的数据类型(AS可以直接右击新建)
package com.jiangxi.login.servicedemo;
parcelable People;
(4)新建IMyAidlInterface.aidl文件(AS可以直接右击新建)
package com.jiangxi.login.servicedemo;
import com.jiangxi.login.servicedemo.People;//这个报名必须引用,否则编译报错
interface IMyAidlInterface {
int addOperation(int a, int b);//加法运算,此时默认TAG是in
double multiplicationOperation(in double a, in double b);//乘法运算
String getPeopleInfo1(in People people);//in只允许客户端传递数据到服务端
String getPeopleInfo2(out People people);//out只允许服务端把数据传递到客户端
String getPeopleInfo3(inout People people);//inout双向通道,既允许服务端把数据传递到客户端,也允许客户端传递数据到服务端
}
- 声明加法运算addOperation接口,用于验证AIDL是否支持int类型;
- 声明乘法运算multiplicationOperation接口,用于验证AIDL是否支持double 类型;
- 声明getPeopleInfo1、getPeopleInfo2、getPeopleInfo3接口,用于验证AIDL是否支持对象类型;
- 声明in、out、inout三种定向Tag,用于验证三者之间的区别;
(5)新建服务类
public class MyService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("yunchong", "====onBind===");
return new MyBind();
}
@Override
public void onCreate() {
super.onCreate();
Log.d("yunchong", "====onCreate==="+MyService.class.getName());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("yunchong", "====onStartCommand===");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("yunchong", "====onDestroy===");
}
public class MyBind extends IMyAidlInterface.Stub{
@Override
public int addOperation(int a, int b) throws RemoteException {
return a + b;
}
@Override
public double multiplicationOperation(double a, double b) throws RemoteException {
return a * b;
}
@Override
public String getPeopleInfo1(People people) throws RemoteException {
Log.d("yunchong", "getPeopleInfo1:"+people.toString());
people.setName("服务端赋值--getPeopleInfo1");
return people.toString();
}
@Override
public String getPeopleInfo2(People people) throws RemoteException {
Log.d("yunchong", "getPeopleInfo2:"+people.toString());
people.setName("服务端赋值--getPeopleInfo2");
return people.toString();
}
@Override
public String getPeopleInfo3(People people) throws RemoteException {
Log.d("yunchong", "getPeopleInfo3:"+people.toString());
people.setName("服务端赋值--getPeopleInfo3");
return people.toString();
}
}
}
定义MyBind内部类,用来具体实现那些接口。
(6)注册服务类
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.jiangxi.login.servicedemo.aidldemo" />
</intent-filter>
</service>
(7)第一启动activity的处理
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService();
}
private void startService(){
final Intent intent = new Intent(MainActivity.this, MyService.class);
//启动服务
startService(intent);
finish();
}
}
它的作用就是:当该app启动时,自动开启在后台运行的service。
(2)我们再新建项目,搭建客户端代码
(1)布局展示
(2)将服务端的两个aidl文件复制到客户端项目,且要求路劲一模一样
(3)将服务端定义的People类拷贝到客户端,内容不变,路劲也不变
(如上图)。
(4)调用
public class MainActivity extends AppCompatActivity {
private Button button1, button2, button3, button4, button5, button6, button7;
private IMyAidlInterface iMyAidlInterface;
private String servicePackname = "com.jiangxi.login.servicedemo";
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
setOnClick();
}
/**
* 启动服务
*/
private void startMyService(){
if (checkPackInfo(servicePackname)) {
Intent intent = new Intent(Intent.ACTION_MAIN);
ComponentName componentName = new ComponentName(servicePackname, servicePackname+".MainActivity");
intent.setComponent(componentName);
try {
startActivity(intent);
}catch (ActivityNotFoundException e){
e.printStackTrace();
}
}else{
Log.d("yunchong", "没有安装服务程序!");
}
}
private void initView(){
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button4 = findViewById(R.id.button4);
button5 = findViewById(R.id.button5);
button6 = findViewById(R.id.button6);
button7 = findViewById(R.id.button7);
}
private void setOnClick(){
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//启动服务
startMyService();
Toast.makeText(MainActivity.this, "启动服务!", Toast.LENGTH_SHORT).show();
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//绑定服务
final Intent intent = new Intent();
intent.setAction("com.jiangxi.login.servicedemo.aidldemo");
intent.setPackage("com.jiangxi.login.servicedemo");
if(isServiceRunning(MainActivity.this, "com.jiangxi.login.servicedemo.MyService")){
bindService(intent, connection, BIND_AUTO_CREATE);
}else{
startService(intent);
bindService(intent, connection, BIND_AUTO_CREATE);
}
Toast.makeText(MainActivity.this, "绑定服务!", Toast.LENGTH_SHORT).show();
}
});
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//加法计算
try {
Toast.makeText(MainActivity.this, "结果:"+iMyAidlInterface.addOperation(10, 2), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//乘法计算
try {
Toast.makeText(MainActivity.this, "结果:"+iMyAidlInterface.multiplicationOperation(10.0, 2.0), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
button5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取人的信息1
try {
People people = new People("张三");
Toast.makeText(MainActivity.this, "获取返回值信息1:"+iMyAidlInterface.getPeopleInfo1(people), Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "获取对象信息1:"+people.toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
button6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取人的信息2
try {
People people = new People("李四");
Toast.makeText(MainActivity.this, "获取返回值信息2:"+iMyAidlInterface.getPeopleInfo2(people), Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "获取对象信息2:"+people.toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
button7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获取人的信息3
try {
People people = new People("王五");
Toast.makeText(MainActivity.this, "获取返回值信息3:"+iMyAidlInterface.getPeopleInfo3(people), Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "获取对象信息3:"+people.toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
/**
* 判断服务是否开启
* @return
*/
public static boolean isServiceRunning(Context context, String ServiceName) {
if (TextUtils.isEmpty(ServiceName)){
return false;
}
ActivityManager myManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ArrayList<ActivityManager.RunningServiceInfo> runningService = (ArrayList<ActivityManager.RunningServiceInfo>) myManager.getRunningServices(30);
for (int i = 0; i < runningService.size(); i++) {
if (runningService.get(i).service.getClassName().toString().equals(ServiceName)) {
return true;
}
}
return false;
}
/**
* 检查安装包是否存在
*
* @param packname
* @return
*/
private boolean checkPackInfo(String packname) {
PackageInfo packageInfo = null;
try {
packageInfo = getPackageManager().getPackageInfo(packname, 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return packageInfo != null;
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
我们来依次点击这几个按钮:
1.启动服务并绑定服务
测试接口之前,必须做的两个操作
2.加法运算
计算结果是12,完全正确。
3.乘法运算
计算结果是20.0,完全正确。
4.分别点击“获取人的信息1”、“获取人的信息2”、“获取人的信息3”
服务端的日志如下
这就是AIDL的定向tag的神奇之处了,这里有必要说明一下三种定向tag的作用:
定向tag是针对于方法的形参来说的, 如以下代码:
String getPeopleInfo1(in People people);//in只允许客户端传递数据到服务端
String getPeopleInfo2(out People people);//out只允许服务端把数据传递到客户端
String getPeopleInfo3(inout People people);//inout双向通道,既允许服务端把数据传递到客户端,也允许客户端传递数据到服务端
- in:只允许客户端传递数据到服务端
- out:只允许服务端把数据传递到客户端
- inout:双向通道,既允许服务端把数据传递到客户端,也允许客户端传递数据到服务端
其中getPeopleInfo2方法中的形参,它的定向tag修饰符是out,客户端传递给服务端的对象是没有值的。
我们来点击“获取人的信息1”按钮:
对应的客户端代码如下:
People people = new People("张三");
Toast.makeText(MainActivity.this, "获取返回值信息1:"+iMyAidlInterface.getPeopleInfo1(people), Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "获取对象信息1:"+people.toString(), Toast.LENGTH_SHORT).show();
对应的服务端代码如下:
@Override
public String getPeopleInfo1(People people) throws RemoteException {
Log.d("yunchong", "getPeopleInfo1:"+people.toString());
people.setName("服务端赋值--getPeopleInfo1");
return people.toString();
}
现象:如果定向tag为int,返回值变了,但是原本对象没变。
我们来点击“获取人的信息2”按钮:
对应的客户端代码如下:
People people = new People("李四");
Toast.makeText(MainActivity.this, "获取返回值信息2:"+iMyAidlInterface.getPeopleInfo2(people), Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "获取对象信息2:"+people.toString(), Toast.LENGTH_SHORT).show();
对应的服务端代码如下:
@Override
public String getPeopleInfo2(People people) throws RemoteException {
Log.d("yunchong", "getPeopleInfo2:"+people.toString());
people.setName("服务端赋值--getPeopleInfo2");
return people.toString();
}
现象:如果定向tag为out,返回值变了,原本对象也变了。
我们来点击“获取人的信息3”按钮:
对应的客户端代码如下:
People people = new People("王五");
Toast.makeText(MainActivity.this, "获取返回值信息3:"+iMyAidlInterface.getPeopleInfo3(people), Toast.LENGTH_SHORT).show();
Toast.makeText(MainActivity.this, "获取对象信息3:"+people.toString(), Toast.LENGTH_SHORT).show();
对应的服务端代码如下:
@Override
public String getPeopleInfo3(People people) throws RemoteException {
Log.d("yunchong", "getPeopleInfo3:"+people.toString());
people.setName("服务端赋值--getPeopleInfo3");
return people.toString();
}
现象:如果定向tag为intout,返回值变了,但是原本对象也变了。
总结:
到目前为止AIDL的使用已经讲解完毕了, 但是很多地方都没有讲解清楚,因为真正的讲解清楚需要大量的篇幅,网上的大部分博客其实都没有讲解清楚。这里说几个注意点,剩下的大家就自己体会了:
(1)Service的使用方法有三种,大家根据具体情况选择其中一种就可以了
第一种通过startService启动服务,处理的业务逻辑主要是在onStartCommand;其启动到停止的生命周期是:
onCreate-->onStartCommand-->onDestroy
第二种通过绑定的方式,处理的业务逻辑主要是自定义的Binder对象里的方法;其绑定到解绑的生命周期是:
onCreate-->onBind-->onUnbind-->onDestroy
第三种两种结合的方式主要是涉及到onRebind回调以及,其启动并绑定到解绑并停止的生命周期是:
onCreate-->onStartCommand-->onBind-->onUnbind-->onRebind-->onUnbind-->onDestroy
(只是其中的一种情况)
(2)AIDL其实就是客户端和服务端的交互,两者之间所支持的类型上文已经说明了, 尤其要注意的是Parcelable类型;
(3)注意aidl中的导包,比如“import com.jiangxi.login.servicedemo.People;”, 这个必须加,即使People和aidl文件在同一个目录下也加,否则编译失败;
(4)文章中额外讲解了一个app怎么隐藏图标,怎么让app启动透明,让用户看不出效果;
(5)文章是通过检查服务app是否安装,检查服务是否启动,如果没有启动服务,则直接启动该服务app。这样可以保证service正常工作。和service保活没半毛线关系,所以大家不要误解了;
(6)文章中重点介绍了定向tag,本人也是重点研究之后才敢写这个博客的,经过不断的调试代码,得出的结论是:
1.in、out、inout只是用来修饰接口形参的,不能修饰返回值;
2.in:只允许客户端传递数据到服务端;
3.out:只允许服务端把数据传递到客户端,服务端传递数据到客户端不是通过返回值来传递的,传递数据的载体是对象本身;
4.inout:就是双向传递数据,没毛病;