您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關怎么在Android中利用Service實現IPC通信,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
Android是一種基于Linux內核的自由及開放源代碼的操作系統,主要使用于移動設備,如智能手機和平板電腦,由美國Google公司和開放手機聯盟領導及開發。
借助AIDL實現IPC通信
AIDL:Android Interface Definition Language,即Android接口定義語言。
Service跨進程傳遞數據需要借助aidl,主要步驟是這樣的:
編寫aidl文件,AS自動生成的java類實現IPC通信的代理
繼承自己的aidl類,實現里面的方法
在onBind()中返回我們的實現類,暴露給外界
需要跟Service通信的對象通過bindService與Service綁定,并在ServiceConnection接收數據。
我們通過代碼來實現一下:
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同時指定運行的進程名,這里并是不只能寫remote進程名,你想要進程名都可以
<service android:name=".service.MyRemoteService" android:process=":remote" />
3、新建一個aidl文件用戶進程間傳遞數據。
AIDL支持的類型:八大基本數據類型、String類型、CharSequence、List、Map、自定義類型。List、Map、自定義類型放到下文講解。
里面會有一個默認的實現方法,刪除即可,這里我們新建的文件如下:
package xxxx;//aidl所在的包名 //interface之前不能有修飾符 interface IProcessInfo { //你想要的通信用的方法都可以在這里添加 int getProcessId(); }
4、實現我們的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取出數據 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定義的Service有一些細微的差別
1、由于需要其他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>
2、我們在需要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); } });
aidl中自定義對象的傳遞
主要步驟如下:
定義自定對象,需要實現Parcelable接口
新建自定義對象的aidl文件
在傳遞數據的aidl文件中引用自定義對象
將自定義對象以及aidl文件拷貝到需要bindService的app中,主要路徑也要原封不動
我們來看一下具體的代碼:
1、定義自定義對象,并實現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; //就算在同一包下,這里也要導包 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)
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中引用的對象也應該是符合上面要求的自定義對象,或者其他的幾種數據類型。
使用Messenger實現IPC通信
步驟是這樣的:
在Server端新建一個Messenger對象,用于響應Client端的注冊操作,并在onBind()中傳遞出去
在Client端的ServiceConnection中,將Server端傳遞過來的Messenger對象進行保存
同時Client端也新建一個Messenger對象,通過Server傳遞過來的Messenger注冊到Server端,保持通信用。
不管是否進行unbindService()操作,只要Client保有Server端的Messenger對象,仍然能和Server端進行通信。
一、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端接收參數用的 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端想對應 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) { } }; }
以上就是怎么在Android中利用Service實現IPC通信,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。