上一篇咱們瞭解了Service的一些概念以及使用方式,這篇着重講解使用Service實現IPC通訊的2中方式。java
上面的代碼都是在當前進程內跟Service通訊,如今咱們來實現一下,不一樣進程內Service如何綁定。android
AIDL:Android Interface Definition Language,即Android接口定義語言。app
Service跨進程傳遞數據須要藉助aidl,主要步驟是這樣的:dom
咱們經過代碼來實現一下:ide
首先咱們須要新建一個Service測試
public class MyRemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("MyRemoteService", "MyRemoteService thread id = " + Thread.currentThread().getId());
return null;
}
}
複製代碼
在manifest文件中聲明咱們的Service同時指定運行的進程名,這裏並是不僅能寫remote進程名,你想要進程名均可以this
<service
android:name=".service.MyRemoteService"
android:process=":remote" />
複製代碼
新建一個aidl文件用戶進程間傳遞數據。spa
AIDL支持的類型:八大基本數據類型、String類型、CharSequence、List、Map、自定義類型。List、Map、自定義類型放到下文講解。線程
裏面會有一個默認的實現方法,刪除便可,這裏咱們新建的文件以下:3d
package xxxx;//aidl所在的包名
//interface以前不能有修飾符
interface IProcessInfo {
//你想要的通訊用的方法均可以在這裏添加
int getProcessId();
}
複製代碼
實現咱們的aidl類
public class IProcessInfoImpl extends IProcessInfo.Stub {
@Override
public int getProcessId() throws RemoteException {
return android.os.Process.myPid();
}
}
複製代碼
在Service的onBind()中返回
public class MyRemoteService extends Service {
IProcessInfoImpl mProcessInfo = new IProcessInfoImpl();
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("MyRemoteService", "MyRemoteService thread id = " + Thread.currentThread().getId());
return mProcessInfo;
}
}
複製代碼
綁定Service
mTvRemoteBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyRemoteService.class);
bindService(intent, mRemoteServiceConnection, BIND_AUTO_CREATE);
}
});
mRemoteServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("MainActivity", "MyRemoteService onServiceConnected");
// 經過aidl取出數據
IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service);
try {
Log.e("MainActivity", "MyRemoteService process id = " + processInfo.getProcessId());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("MainActivity", "MyRemoteService onServiceDisconnected");
}
};
複製代碼
只要綁定成功就能在有log打印成MyRemoteService所在進程的進程id。這樣咱們就完成了跟不一樣進程的Service通訊的過程。
跟調同app下不一樣進程下的Service相比,調用其餘的app定義的Service有一些細微的差異
因爲須要其餘app訪問,因此以前的bindService()使用的隱式調用不在合適,須要在Service定義時定義action
咱們在定義的線程的App A 中定義以下Service:
<service android:name=".service.ServerService">
<intent-filter>
//這裏的action自定義
<action android:name="com.jxx.server.service.bind" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
複製代碼
咱們在須要bindService的App B 中須要作這些處理
首先要將A中定義的aidl文件複製到B中,好比咱們在上面定義的IProcessInfo.aidl這個文件,包括路徑在內須要原封不動的複製過來。
在B中調用Service經過顯式調用
mTvServerBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("com.jxx.server.service.bind");//Service的action
intent.setPackage("com.jxx.server");//App A的包名
bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
}
});
複製代碼
主要步驟以下:
咱們來看一下具體的代碼:
定義自定義對象,並實現Parcelable接口
public class ServerInfo implements Parcelable {
public ServerInfo() {
}
String mPackageName;
public String getPackageName() {
return mPackageName;
}
public void setPackageName(String packageName) {
mPackageName = packageName;
}
protected ServerInfo(Parcel in) {
mPackageName = in.readString();
}
public static final Creator<ServerInfo> CREATOR = new Creator<ServerInfo>() {
@Override
public ServerInfo createFromParcel(Parcel in) {
return new ServerInfo(in);
}
@Override
public ServerInfo[] newArray(int size) {
return new ServerInfo[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mPackageName);
}
//使用out或者inout修飾時須要本身添加這個方法
public void readFromParcel(Parcel dest) {
mPackageName = dest.readString();
}
}
複製代碼
新建自定義對象的aidl文件
package com.jxx.server.aidl;
//注意parcelable 是小寫的
parcelable ServerInfo;
複製代碼
引用自定義對象
package com.jxx.server.aidl;
//就算在同一包下,這裏也要導包
import com.jxx.server.aidl.ServerInfo;
interface IServerServiceInfo {
ServerInfo getServerInfo();
void setServerInfo(inout ServerInfo serverinfo);
}
複製代碼
注意這裏的set方法,這裏用了inout,一共有3種修飾符
- in:客戶端寫入,服務端的修改不會通知到客戶端
- out:服務端修改同步到客戶端,可是服務端獲取到的對象可能爲空
- inout:修改都收同步的
複製代碼
當使用out和inout時,除了要實現Parcelable外還要手動添加readFromParcel(Parcel dest)
拷貝自定義對象以及aidl文件到在要引用的App中便可。
引用
mServerServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IServerServiceInfo serverServiceInfo = IServerServiceInfo.Stub.asInterface(service);
try {
ServerInfo serviceInfo = serverServiceInfo.getServerInfo();
Log.e("MainActivity", "ServerService packageName = " + serviceInfo.getPackageName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("MainActivity", "ServerService onServiceDisconnected");
}
};
複製代碼
List、Map中引用的對象也應該是符合上面要求的自定義對象,或者其餘的幾種數據類型。
步驟是這樣的:
public class MessengerService extends Service {
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_VALUE = 3;
//這個是給client端接收參數用的
static final int MSG_CLIENT_SET_VALUE = 4;
static class ServiceHandler extends Handler {
private final List<Messenger> mMessengerList = new ArrayList<>();
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mMessengerList.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mMessengerList.remove(msg.replyTo);
break;
case MSG_SET_VALUE:
int value = msg.arg1;
for (Messenger messenger : mMessengerList) {
try {
messenger.send(Message.obtain(null, MSG_CLIENT_SET_VALUE, value, 0));
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
default:
super.handleMessage(msg);
}
}
}
private Messenger mMessenger = new Messenger(new ServiceHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
複製代碼
public class MessengerClientActivity extends AppCompatActivity {
//這些類型要和Server端想對應
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_VALUE = 3;
static final int MSG_CLIENT_SET_VALUE = 4;
class ClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_CLIENT_SET_VALUE) {
mTvValue.setText(msg.arg1 + "");
} else {
super.handleMessage(msg);
}
}
}
TextView mTvServerBind;
TextView mTvServerUnbind;
TextView mTvValue;
TextView mTvSend;
ServiceConnection mServerServiceConnection;
Messenger mServerMessenger;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
mTvServerBind = findViewById(R.id.tv_server_bind);
mTvServerUnbind = findViewById(R.id.tv_server_unbind);
mTvValue = findViewById(R.id.tv_value);
mTvSend = findViewById(R.id.tv_send);
mTvServerBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("jxx.com.server.service.messenger");
intent.setPackage("jxx.com.server");
bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
}
});
mTvServerUnbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//就算這裏咱們unbindService,只要咱們還保留有mServerMessenger對象,
//咱們就能繼續與Server通訊
unbindService(mServerServiceConnection);
}
});
mTvSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mServerMessenger != null) {
try {
//測試一下可否設置數據
Message test = Message.obtain(null, MSG_SET_VALUE, new Random().nextInt(100), 0);
mServerMessenger.send(test);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
mServerServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//服務端的messenger
mServerMessenger = new Messenger(service);
//如今開始構client用來傳遞和接收消息的messenger
Messenger clientMessenger = new Messenger(new ClientHandler());
try {
//將client註冊到server端
Message register = Message.obtain(null, MSG_REGISTER_CLIENT);
register.replyTo = clientMessenger;//這是註冊的操做,咱們能夠在上面的Server代碼看到這個對象被取出
mServerMessenger.send(register);
Toast.makeText(MessengerClientActivity.this, "綁定成功", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
複製代碼