Android使用Service實現(xiàn)IPC通信的2種方式
借助AIDL實現(xiàn)IPC通信
一、代碼實操---與遠(yuǎn)端進(jìn)程的Service綁定
上面的代碼都是在當(dāng)前進(jìn)程內(nèi)跟Service通信,現(xiàn)在我們來實現(xiàn)一下,不同進(jìn)程內(nèi)Service如何綁定。
AIDL:Android Interface Definition Language,即Android接口定義語言。
Service跨進(jìn)程傳遞數(shù)據(jù)需要借助aidl,主要步驟是這樣的:
- 編寫aidl文件,AS自動生成的java類實現(xiàn)IPC通信的代理
- 繼承自己的aidl類,實現(xiàn)里面的方法
- 在onBind()中返回我們的實現(xiàn)類,暴露給外界
- 需要跟Service通信的對象通過bindService與Service綁定,并在ServiceConnection接收數(shù)據(jù)。
我們通過代碼來實現(xiàn)一下:
1、首先我們需要新建一個Service
public class MyRemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("MyRemoteService", "MyRemoteService thread id = " + Thread.currentThread().getId());
return null;
}
}
2、在manifest文件中聲明我們的Service同時指定運(yùn)行的進(jìn)程名,這里并是不只能寫remote進(jìn)程名,你想要進(jìn)程名都可以
<service
android:name=".service.MyRemoteService"
android:process=":remote" />
3、新建一個aidl文件用戶進(jìn)程間傳遞數(shù)據(jù)。
AIDL支持的類型:八大基本數(shù)據(jù)類型、String類型、CharSequence、List、Map、自定義類型。List、Map、自定義類型放到下文講解。

里面會有一個默認(rèn)的實現(xiàn)方法,刪除即可,這里我們新建的文件如下:
package xxxx;//aidl所在的包名
//interface之前不能有修飾符
interface IProcessInfo {
//你想要的通信用的方法都可以在這里添加
int getProcessId();
}
4、實現(xiàn)我們的aidl類
public class IProcessInfoImpl extends IProcessInfo.Stub {
@Override
public int getProcessId() throws RemoteException {
return android.os.Process.myPid();
}
}
5、在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;
}
}
6、綁定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取出數(shù)據(jù)
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");
}
};
只要綁定成功就能在有l(wèi)og打印成MyRemoteService所在進(jìn)程的進(jìn)程id。這樣我們就完成了跟不同進(jìn)程的Service通信的過程。
二、代碼實操---調(diào)用其他app的Service
跟調(diào)同app下不同進(jìn)程下的Service相比,調(diào)用其他的app定義的Service有一些細(xì)微的差別
1、由于需要其他app訪問,所以之前的bindService()使用的隱式調(diào)用不在合適,需要在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>
2、我們在需要bindService的App B 中需要做這些處理
- 首先要將A中定義的aidl文件復(fù)制到B中,比如我們在上面定義的IProcessInfo.aidl這個文件,包括路徑在內(nèi)需要原封不動的復(fù)制過來。
- 在B中調(diào)用Service通過顯式調(diào)用
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);
}
});
aidl中自定義對象的傳遞
主要步驟如下:
- 定義自定對象,需要實現(xiàn)Parcelable接口
- 新建自定義對象的aidl文件
- 在傳遞數(shù)據(jù)的aidl文件中引用自定義對象
- 將自定義對象以及aidl文件拷貝到需要bindService的app中,主要路徑也要原封不動
我們來看一下具體的代碼:
1、定義自定義對象,并實現(xiàn)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();
}
}
2、新建自定義對象的aidl文件
package com.jxx.server.aidl; //注意parcelable 是小寫的 parcelable ServerInfo;
3、引用自定義對象
package com.jxx.server.aidl;
//就算在同一包下,這里也要導(dǎo)包
import com.jxx.server.aidl.ServerInfo;
interface IServerServiceInfo {
ServerInfo getServerInfo();
void setServerInfo(inout ServerInfo serverinfo);
}
注意這里的set方法,這里用了inout,一共有3種修飾符
- in:客戶端寫入,服務(wù)端的修改不會通知到客戶端
- out:服務(wù)端修改同步到客戶端,但是服務(wù)端獲取到的對象可能為空
- inout:修改都收同步的
當(dāng)使用out和inout時,除了要實現(xiàn)Parcelable外還要手動添加readFromParcel(Parcel dest)
4、拷貝自定義對象以及aidl文件到在要引用的App中即可。
5、引用
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中引用的對象也應(yīng)該是符合上面要求的自定義對象,或者其他的幾種數(shù)據(jù)類型。
使用Messenger實現(xiàn)IPC通信
步驟是這樣的:
- 在Server端新建一個Messenger對象,用于響應(yīng)Client端的注冊操作,并在onBind()中傳遞出去
- 在Client端的ServiceConnection中,將Server端傳遞過來的Messenger對象進(jìn)行保存
- 同時Client端也新建一個Messenger對象,通過Server傳遞過來的Messenger注冊到Server端,保持通信用。
- 不管是否進(jìn)行unbindService()操作,只要Client保有Server端的Messenger對象,仍然能和Server端進(jìn)行通信。
一、Server端代碼
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端接收參數(shù)用的
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();
}
}
二、Client端代碼
public class MessengerClientActivity extends AppCompatActivity {
//這些類型要和Server端想對應(yīng)
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對象,
//我們就能繼續(xù)與Server通信
unbindService(mServerServiceConnection);
}
});
mTvSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mServerMessenger != null) {
try {
//測試一下能否設(shè)置數(shù)據(jù)
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) {
//服務(wù)端的messenger
mServerMessenger = new Messenger(service);
//現(xiàn)在開始構(gòu)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) {
}
};
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android入門之Service的使用詳解
- Android?NotificationListenerService通知監(jiān)聽服務(wù)使用
- Android Google AutoService框架使用詳解
- 說說在Android如何使用服務(wù)(Service)的方法
- Android使用Service實現(xiàn)簡單音樂播放實例
- 淺談Android中Service的注冊方式及使用
- Android編程使用Service實現(xiàn)Notification定時發(fā)送功能示例
- Android 通知使用權(quán)(NotificationListenerService)的使用
- Android Service功能使用示例代碼
相關(guān)文章
Android實現(xiàn)雙模(CDMA/GSM)手機(jī)短信監(jiān)聽的方法
這篇文章主要介紹了Android實現(xiàn)雙模(CDMA/GSM)手機(jī)短信監(jiān)聽的方法,涉及Android短信的原理與相關(guān)操作技巧,具有一定參考借鑒價值,需要的朋友可以參考下2016-06-06
Android應(yīng)用開發(fā)中WebView的常用方法筆記整理
WebView即是在安卓本地應(yīng)用中打開網(wǎng)頁視圖功能,其中對于JavaScript加載的各項操作是重點和難點,本文就為大家送上Android應(yīng)用開發(fā)中WebView的常用方法筆記整理2016-05-05
Flutter之自定義Dialog實現(xiàn)版本更新彈窗功能的實現(xiàn)
這篇文章主要介紹了Flutter之自定義Dialog實現(xiàn)版本更新彈窗功能的實現(xiàn),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
Android使用Profiler查看應(yīng)用內(nèi)存分析的操作步驟
內(nèi)存分析是Profiler中的一個組件,可以幫助我們識別可能會導(dǎo)致應(yīng)用卡頓、凍結(jié)甚至崩潰的內(nèi)存泄露和內(nèi)存抖動,本文小編將給大家介紹一下Android使用Profiler查看應(yīng)用內(nèi)存分析的操作步驟,需要的朋友可以參考下2023-10-10
Android 6.0 無法在SD卡創(chuàng)建目錄的方法
今天小編就為大家分享一篇Android 6.0 無法在SD卡創(chuàng)建目錄的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
Android的Launcher啟動器中添加快捷方式及小部件實例
這篇文章主要介紹了在Android的Launcher啟動器中添加快捷方式及窗口小部件的方法,包括在自己的應(yīng)用程序中添加窗口小部件AppWidget的例子,需要的朋友可以參考下2016-02-02

