1. 多进程
开启方式:
-
android:process = ":remote"
android:process="完整报包名"
2. 基本概念
2.1 AIDL
2.1.1 AIDL的基本概念
- 当客户端发起请求时,当前线程会被挂起,所以如果一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求;
- 服务端的
Binder
方法运行在Binder线程池中,所以Binder方法不管是否耗时都应该采用同步的方法去实现 - 所有能在
Binder
中传输的类都需要实现IInterface
这个类 - 内部
Stub
类就是Binder
2.1.2 实现AIDL的基本步骤
步骤1:创建服务端工程IPC_Server
步骤2:创建AIDL文件IMyService.AIDl
// IMyService.aidl
package com.example.ipc_server;
// Declare any non-default types here with import statements
interface IMyService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
List<Student> getStudent();
void addStudent(in Student student);
}
- AIDL支持的数据类型:基本数据类型,String和Charsequence,ArrayList,HashMap,Parcelable,AIDL
- 注意:如果使用了Parcelable对象,需要创建和对象同名的aidl文件
- 注意:除了基本数据类型,其他类型需要表明方向:
in
out
inout
- 注意:aidl接口不支持静态常量,只能写方法
步骤3:Server端实现
package com.example.ipcdemo;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.List;
public class ServerService extends Service {
private static final String TAG = "ServerService";
public final static int MSG_FROM_CLIENT = 1;
private Binder mBinder = new IBookInterface.Stub() {
@Override
public List<Book> getBook() throws RemoteException {
// 具体代码
return null;
}
@Override
public void addBook(Book book) throws RemoteException {
//具体代码
}
};
public ServerService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
步骤4:Client端实现
package com.example.ipcdemo;
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.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookInterface iBookInterface = IBookInterface.Stub.asInterface(iBinder); //转化成aidl接口 IBookInterface.adil
try {
iBookInterface.addBook(new Book("大头儿子与小头爸爸", 10));
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = findViewById(R.id.btn);
final Intent intent = new Intent(this, ServerService.class);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bindService(intent, connection, BIND_AUTO_CREATE);
}
});
}
}
2.1.3 推荐阅读
- [Android]Service和Activity双向通信的两种方式
- Android 通过AIDL在两个APP之间Service通信
- 带你了解android的IPC机制
- AIDL,在Stub类中的asInterface函数
3. Android中的IPC方式
3.1 使用Bundle
- 四大组件中的
Activity
,Service
,Receiver
都支持在Intent中传递Bundle
-
Bundle
继承了Parcelable
接口,因此可以在不同的进程中传递数据
3.2 使用文件进行传输
- 利用序列化和反序列化操作将对象写入文件并读取
- 但是反序列化操作只能保证内容上是一样的,本质上还是两个对象
- 可能会产生并发读写问题
3.3 使用Messenger
- 使用串行的方式来处理消息,大量消息时效率低
package com.example.ipcdemo;
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.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
Messenger mMessenger;
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mMessenger = new Messenger(iBinder);
Message msg = new Message();
msg.what = ServerService.MSG_FROM_CLIENT;
Bundle bundle = new Bundle();
bundle.putString("msg", "Hello!!!");
msg.setData(bundle);
try {
mMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = findViewById(R.id.btn);
final Intent intent = new Intent(this, ServerService.class);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bindService(intent, connection, BIND_AUTO_CREATE);
}
});
}
}
package com.example.ipcdemo;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;
import androidx.annotation.NonNull;
public class ServerService extends Service {
private static final String TAG = "ServerService";
public final static int MSG_FROM_CLIENT = 1;
static class ServerHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
switch(msg.what) {
case MSG_FROM_CLIENT:
Log.d(TAG, "handleMessage: receive message from client!" + msg.getData().getString("msg"));
break;
}
}
}
public ServerService() {
}
@Override
public IBinder onBind(Intent intent) {
ServerHandler serverHandler = new ServerHandler();
Messenger messenger = new Messenger(serverHandler);
return messenger.getBinder();
}
}
3.3 ContentProvider
- 抽象类,需要实现
onCreate()
,query
,update
,insert
,delete
,getType
- 注册
- 继承
SQLiteDatabaseHelper
,操作数据库 - 注意:
query
,update
,insert
,delete
存在并发访问,因此要注意线程安全和同步 - 注意:
SqliteDatabase
对象内部操作数据库有同步处理,但多个SqliteDatabase
对象之间不能保证同步
3.4 Socket
实现步骤:
- 设置网络权限
<INTERNET>
<ACCESS_NETWORK_STATE>
注意:不能在主线程访问网络
- 在服务端Server创建
ServerSocket
监听8688
端口
-
BufferedReader
读取客户端数据 -
PrintWriter
给客户端发送数据
- 在客户端Client创建Socket连接
8688
端口
-
BufferedReader
读取服务端数据 -
PrintWriter
给服务端发送数据
package com.example.socketdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
public class ClientActivity extends AppCompatActivity {
private static final String TAG = "ClientActivity";
Socket socket = null;
PrintWriter out = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
Intent intent = new Intent(this, Server.class);
startService(intent);
new Thread(new Runnable() {
@Override
public void run() {
try {
socket = new Socket("localhost",8688);
} catch (IOException e) {
e.printStackTrace();
}
while(true) {
try {
out = new PrintWriter(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
Log.d(TAG, "run: sendMessage ");
out.println("123");
}
}
}).start();
}
}
package com.example.socketdemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
public class Server extends Service {
private static final String TAG = "Server";
boolean mIsServiceDestroyed = false;
public Server() {
}
@Override
public void onCreate() {
super.onCreate();
new socketThread().start();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
class socketThread extends Thread {
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
e.printStackTrace();
}
if(!mIsServiceDestroyed) {
try {
final Socket client = serverSocket.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
out.println("Hello");
while(!mIsServiceDestroyed) {
String str = in.readLine();
Log.d(TAG, "responseClient: " +str);
if(str == null) {
break;
}
out.println("Hello " + new Random().nextInt(3));
}
Log.d(TAG, "Client Quit" );
client.close();
}
}
参考资料:
https://blog.csdn.net/ding3106/article/details/80714410
我的问题:Socket连接不上
网上的解决方法:
1.设置访问的ip为10.0.2.2
模拟器默认把
127.0.0.1
和localhost
当做本身了,在模拟器上可以用10.0.2.2
代替127.0.0.1
和localhost
,另外如果是在局域网环境可以用192.168.0.x
或者192.168.1.x
(根据具体配置)连接本机,这样应该就不会报错了。
目前这个办法对我没用
2. 网络问题
可正常建立连接的:1,同处于一个校园网(使用状态信息或者wifi信息里的ip连接)2. 服务器开热点(移动网打开的情况下),客户端连状态信息里面的ip
https://blog.csdn.net/qq_37735413/article/details/80012390
https://www.cnblogs.com/1995hxt/p/4469389.html