做减法,做减法,做减法,扎实基础,执笔记录
Service 兄弟篇
- Service基础认知、使用
- IntentSerivce的使用
- AIDL浅显易懂
- 实践效果
- 基础认知
- 步步实践
- 创建 Service APP 服务端
- 创建 Client APP 客户端
- 结果检验
- 兴趣扩展
- 为什么要进程通信?
- Linux常用进程通信方式
不知你有没有被问到过:在Android中有哪些进程间(IPC)通信的方式?
如果你懂的话,你会发现Android的四大组件其实都支持进程通信,只是每个组件对应的场景有所不同...
- Activity:Intent方式,常用于启动系统进程,如电话等
- Service:AIDL方式,常用于俩个app之间的进程通信
- BroadcaseReceve: 广播方式,常用于当前进程与三方进程通信
- Content Provider: 主要体现在数据方面,通过数据共享实现进程通信
AIDL:Android Interface definition language - Android接口定义语言 - 独属 Service 的进程通信方式
AIDL 角色分划
- Service 服务端,也称被供给者,主要逻辑的执行者
- Client 客户端,也称调用者,主要进行通信的需求方
AIDL并不是支持所有类型的数据,所以需要注意以下支持的基本数据类型
- 八种基本数据类型:byte、char、short、int、long、float、double、boolean
- String和CharSequence
- Percelable:实现了Parcelable接口的数据类型
- AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
- List 类型:List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
- Map类型:Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
here 主讲 Service 的 AIDL 通信方式,支持本进程通信,也支持俩个app间的进程通信,主说后者!
当没有好的学习资源时,可以通过官网看看AIDL,来一起写一个俩app间的进程通信把,AIDL走起...
步步实践通过一个简单的Demo来验证我们对AIDL的掌握,Demo中简单调用一下有参方法,无参方法~
创建 Service APP 服务端-
新建项目后,新建AIDL类
-
ServiceAIDL就是我创建的AIDL类,它是一个接口类,默认有basicTypes方法(说明支持类型),toastSelf、addMethod是我加的方法哈
-
写完对应的AIDL类后,记得编译下项目,会生成下面这样的java文件夹与AIDL文件
-
创建用于进程通信的Service,内部的Binder就用到了刚刚创建的AIDL类,主要在这里写通信的逻辑
短版
ServiceAIDL.Stub mBinder = new ServiceAIDL.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int addMethod(int a, int b) throws RemoteException {
return a + b;
}
@Override
public String toastSelf() throws RemoteException {
return "服务端的AIDL提供类";
}
};
完整版
package com.example.aidlservicedemo;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Nullable;
public class AIDLService extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.e("tag", "Service onCreate");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("tag", "Service onBind");
return mBinder;
// return new myBinder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("tag", "Service onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("tag", "Service onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("tag", "Service onDestroy");
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.e("tag", "Service onRebind");
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.e("tag", "Service onStart");
}
//和下方 - 方法意义相同
class myBinder extends ServiceAIDL.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int addMethod(int a, int b) throws RemoteException {
return a + b;
}
@Override
public String toastSelf() throws RemoteException {
return "服务端的AIDL提供类";
}
}
//和上方 - 方法意义相同
ServiceAIDL.Stub mBinder = new ServiceAIDL.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int addMethod(int a, int b) throws RemoteException {
return a + b;
}
@Override
public String toastSelf() throws RemoteException {
return "服务端的AIDL提供类";
}
};
}
- AndoridMainfest中注册Service
- process 独立进程
短版
完整版
创建 Client APP 客户端
注意:AIDL类、包名 需与 Service一摸一样!!!
- 将之前创建好的AIDL类,copy一份到新项目中,在次申明包名、类需完全一样!
- copy好AIDL后,编译项目,会自动生成对应Service类
- 配置已经完成,直接启动服务即可
短版
bindService启动服务
private void initAIDL() {
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.e("tag", "Service连接成功");
serviceAIDL = ServiceAIDL.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e("tag", "Service关闭连接");
serviceAIDL = null;
}
};
Intent intent = new Intent();
intent.setPackage("com.example.aidlservicedemo");
intent.setAction("com.example.aidlservicedemo.AIDLService");
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
完整版
package com.example.aidldemo;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.example.aidlservicedemo.ServiceAIDL;
public class MainActivity extends AppCompatActivity {
private ServiceConnection mServiceConnection;
private ServiceAIDL serviceAIDL;
// private ServiceConnection mServiceConnection = new ServiceConnection() {
// @Override
// public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// Log.e("tag", "Service连接成功");
// serviceAIDL = ServiceAIDL.Stub.asInterface(iBinder);
// }
//
// @Override
// public void onServiceDisconnected(ComponentName componentName) {
// Log.e("tag", "Service关闭连接");
// serviceAIDL = null;
// }
// };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initAIDL();
EditText mEtNum1 = findViewById(R.id.et_num1);
EditText mEtNum2 = findViewById(R.id.et_num2);
Button addBtn = findViewById(R.id.btn_add);
Button mBtn = findViewById(R.id.btn);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
Toast.makeText(MainActivity.this, serviceAIDL.toastSelf(), Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
addBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String num1 = mEtNum1.getText().toString().trim();
String num2 = mEtNum2.getText().toString().trim();
if (TextUtils.isEmpty(num1)) {
Toast.makeText(MainActivity.this, "请输入第一个相加数", Toast.LENGTH_LONG).show();
} else if (TextUtils.isEmpty(num2)) {
Toast.makeText(MainActivity.this, "请输入第二个相加数", Toast.LENGTH_LONG).show();
} else {
try {
int total = serviceAIDL.addMethod(Integer.parseInt(num1), Integer.parseInt(num2));
Toast.makeText(MainActivity.this, "相加结果:" + total, Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
private void initAIDL() {
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.e("tag", "Service连接成功");
serviceAIDL = ServiceAIDL.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e("tag", "Service关闭连接");
serviceAIDL = null;
}
};
Intent intent = new Intent();
intent.setPackage("com.example.aidlservicedemo");
intent.setAction("com.example.aidlservicedemo.AIDLService");
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
super.onDestroy();
}
}
结果检验
关于Service端和Client 端的项目代码,都已上传到此处!
检验流程
- 优先启动 Service 端的APP
- 然后启动 Client 端的APP
- 开始检验
检验结果
需注意Service优先挂起,然后再启动客户端进行AIDL通信
- Service 端 - aidlservicedemo
2021-12-11 19:27:15.547 5521-5521/? E/vicedemo:Remot: Unknown bits set in runtime_flags: 0x8000 2021-12-11 19:27:15.626 5521-5521/com.example.aidlservicedemo E/tag: Service onCreate 2021-12-11 19:27:15.626 5521-5521/com.example.aidlservicedemo E/tag: Service onBind
- App端 - aidldemo
2021-12-11 19:27:15.746 5481-5481/com.example.aidldemo E/tag: Service连接成功
兴趣扩展
这部分内容主要借鉴自这儿,扩展个人爱好~
为什么要进程通信?- 数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间
- 共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
- 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
- 资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。
- 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
- 管道(pipe):管道可用于具有亲缘关系的进程间的通信,是一种半双工的方式,数据只能单向流动,允许一个进程和另一个与它有共同祖先的进程之间进行通信。
- 命名管道(named pipe):命名管道克服了管道没有名字的限制,同时除了具有管道的功能外(也是半双工),它还允许无亲缘关系进程间的通信。命名管道在文件系统中有对应的文件名。命名管道通过命令mkfifo或系统调用mkfifo来创建。
- 信号(signal):信号是比较复杂的通信方式,用于通知接收进程有某种事件发生了,除了进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。
- 消息队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
- 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。
- 内存映射:内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它。
- 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。
- 套接字(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。



