Android系統的IPC方式通常爲:文件、socket、binder、messenger、AIDL、ContentProvider,此外還有個Anonymous Shared Memory(匿名共享內存),這篇文章介紹Ashm基礎使用相關知識。
- ShareMemory android O(8.0)之後增加新的共享內存方式,SharedMemory.java 此類繼承Parcelable,可以作爲IPC通訊傳輸的數據;
- ClassLoader 多態:此方式並非真正的多態,而是根據ClassLoader類的加載順序,在應用中構建一個和系統類同樣包名的類(方法也同名,可以不做實現),編譯時使用的應用中的類,運行時加載的是系統中的類,從而實現僞多態;
- AIDL 通過 傳輸 ParcelFileDescriptor,它繼承Parcelable 序列化,因此可以AIDL傳輸
- ShareMemory 繼承Parcelable,可以直接AIDL傳輸
demo爲MainActivity和一個遠程服務(指定了process的service)之間的通信,步驟如下:
1、創建aidl文件IMemoryAidlInterface
interface IMemoryAidlInterface {
ParcelFileDescriptor getParcelFileDescriptor();
}
1、創建一個服務並在manifest文件中指定process
public class MemoryFetchService extends Service {
private static final String TAG = "MemoryFetchService";
private static final String SHM_FILE_NAME = "test_memory";
@Override
public IBinder onBind(Intent intent) {
return new MemoryFetchStub();
}
static class MemoryFetchStub extends IMemoryAidlInterface.Stub {
@Override
public ParcelFileDescriptor getParcelFileDescriptor() throws RemoteException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) {
MemoryFile memoryFile = null;
try {
memoryFile = new MemoryFile(SHM_FILE_NAME, 1024);
memoryFile.getOutputStream().write(new byte[]{1, 2, 3, 4, 5});
Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
FileDescriptor des = (FileDescriptor) method.invoke(memoryFile);
return ParcelFileDescriptor.dup(des);
} catch (Exception e) {
Log.d(TAG, "getParcelFileDescriptor: exception : " + e.toString());
}
}else {
//TODO use SharedMemory to get fd
}
return null;
}
}
}
<service android:name=".MemoryFetchService"
android:process=":ashm"/>
在服務MemoryFetchService中創建共享內存虛擬文件並設置size,寫入內容爲一個數組。
在MainActivity中綁定服務,讀取共享內存文件中的內容:
private void bind() {
Intent intent = new Intent(MainActivity.this, MemoryFetchService.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
byte[] content = new byte[10];
IMemoryAidlInterface iMemoryAidlInterface
= IMemoryAidlInterface.Stub.asInterface(service);
try {
ParcelFileDescriptor parcelFileDescriptor = iMemoryAidlInterface.getParcelFileDescriptor();
FileDescriptor descriptor = parcelFileDescriptor.getFileDescriptor();
FileInputStream fileInputStream = new FileInputStream(descriptor);
int read = fileInputStream.read(content);
Log.d(TAG, "onServiceConnected: read == " + read);
} catch (Exception e) {
Log.d(TAG, "onServiceConnected: exception: " + e.toString());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Service.BIND_AUTO_CREATE);
}
自己的工具類:
package com.people.libsdk.memoryfile;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
/**
* 對memoryFile類的擴展
* 1.從memoryFile對象中獲取FileDescriptor,ParcelFileDescriptor
* 2.根據一個FileDescriptor和文件length實例化memoryFile對象
* Created by wuzr on 2016/7/16.
*/
public class MemoryFileHelper {
/**
* 創建共享內存對象
*
* @param name 描述共享內存文件名稱
* @param length 用於指定創建多大的共享內存對象
* @return MemoryFile 描述共享內存對象
*/
public static MemoryFile createMemoryFile(String name, Integer length) {
try {
return new MemoryFile(name, length);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static MemoryFile openMemoryFile(ParcelFileDescriptor pfd, int length, int mode) {
if (pfd == null) {
throw new IllegalArgumentException("ParcelFileDescriptor 不能爲空");
}
FileDescriptor fd = pfd.getFileDescriptor();
return openMemoryFile(fd, length, mode);
}
/**
* 打開共享內存,一般是一個地方創建了一塊共享內存
* 另一個地方持有描述這塊共享內存的文件描述符,調用
* 此方法即可獲得一個描述那塊共享內存的MemoryFile
* 對象
*
* @param fd 文件描述
* @param length 共享內存的大小
* @param mode PROT_READ = 0x1只讀方式打開,
* PROT_WRITE = 0x2可寫方式打開,
* PROT_WRITE|PROT_READ可讀可寫方式打開
* @return MemoryFile
*/
private static MemoryFile openMemoryFile(FileDescriptor fd, int length, int mode) {
MemoryFile memoryFile = null;
try {
memoryFile = new MemoryFile("tem", 1);
memoryFile.close();
Class<?> c = MemoryFile.class;
Method native_mmap = null;
Method[] ms = c.getDeclaredMethods();
for (int i = 0; ms != null && i < ms.length; i++) {
if (ms[i].getName().equals("native_mmap")) {
native_mmap = ms[i];
}
}
ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mFD", fd);
ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mLength", length);
long address = (long) ReflectUtil.invokeMethod(null, native_mmap, fd, length, mode);
ReflectUtil.setField("android.os.MemoryFile", memoryFile, "mAddress", address);
} catch (Exception e) {
e.printStackTrace();
}
return memoryFile;
}
/**
* 獲取memoryFile的ParcelFileDescriptor
*
* @param memoryFile 描述一塊共享內存
* @return ParcelFileDescriptor
*/
public static ParcelFileDescriptor getParcelFileDescriptor(MemoryFile memoryFile) {
if (memoryFile == null) {
throw new IllegalArgumentException("memoryFile 不能爲空");
}
ParcelFileDescriptor pfd;
FileDescriptor fd = getFileDescriptor(memoryFile);
pfd = (ParcelFileDescriptor) ReflectUtil.getInstance("android.os.ParcelFileDescriptor", fd);
return pfd;
}
/**
* 獲取memoryFile的FileDescriptor
*
* @param memoryFile 描述一塊共享內存
* @return 這塊共享內存對應的文件描述符
*/
public static FileDescriptor getFileDescriptor(MemoryFile memoryFile) {
if (memoryFile == null) {
throw new IllegalArgumentException("memoryFile 不能爲空");
}
FileDescriptor fd;
fd = (FileDescriptor) ReflectUtil.invoke("android.os.MemoryFile", memoryFile, "getFileDescriptor");
return fd;
}
public static byte[] readFileDescriptor(FileDescriptor fileDescriptor) {
FileInputStream input = new FileInputStream(fileDescriptor);
try {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 4];
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
byte[] all = output.toByteArray();
input.close();
output.close();
return all;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
package com.people.libsdk.memoryfile;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 反射工具類
* Created by wuzr on 2016/6/27.
*/
public class ReflectUtil {
/**
*根據類名,參數實例化對象
* @param className 類的路徑全名
* @param params 構造函數需要的參數
* @return 返回T類型的一個對象
*/
public static Object getInstance(String className,Object ... params){
if(className == null || className.equals("")){
throw new IllegalArgumentException("className 不能爲空");
}
try {
Class<?> c = Class.forName(className);
if(params != null){
int plength = params.length;
Class[] paramsTypes = new Class[plength];
for (int i = 0; i < plength; i++) {
paramsTypes[i] = params[i].getClass();
}
Constructor constructor = c.getDeclaredConstructor(paramsTypes);
constructor.setAccessible(true);
return constructor.newInstance(params);
}
Constructor constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 執行instance的方法
* @param className 類的全名
* @param instance 對應的對象,爲null時執行類的靜態方法
* @param methodName 方法名稱
* @param params 參數
*/
public static Object invoke(String className,Object instance,String methodName,Object ... params){
if(className == null || className.equals("")){
throw new IllegalArgumentException("className 不能爲空");
}
if(methodName == null || methodName.equals("")){
throw new IllegalArgumentException("methodName不能爲空");
}
try {
Class<?> c = Class.forName(className);
if(params != null){
int plength = params.length;
Class[] paramsTypes = new Class[plength];
for(int i = 0;i < plength;i++){
paramsTypes[i] = params[i].getClass();
}
Method method = c.getDeclaredMethod(methodName, paramsTypes);
method.setAccessible(true);
return method.invoke(instance, params);
}
Method method = c.getDeclaredMethod(methodName);
method.setAccessible(true);
return method.invoke(instance);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 執行指定的對方法
* @param instance 需要執行該方法的對象,爲空時,執行靜態方法
* @param m 需要執行的方法對象
* @param params 方法對應的參數
* @return 方法m執行的返回值
*/
public static Object invokeMethod(Object instance,Method m,Object ... params){
if(m == null){
throw new IllegalArgumentException("method 不能爲空");
}
m.setAccessible(true);
try {
return m.invoke(instance,params);
} catch (Exception e){
e.printStackTrace();
}
return null;
}
/**
* 取得屬性值
* @param className 類的全名
* @param fieldName 屬性名
* @param instance 對應的對象,爲null時取靜態變量
* @return 屬性對應的值
*/
public static Object getField(String className,Object instance,String fieldName){
if(className == null || className.equals("")){
throw new IllegalArgumentException("className 不能爲空");
}
if(fieldName == null || fieldName.equals("")){
throw new IllegalArgumentException("fieldName 不能爲空");
}
try {
Class c = Class.forName(className);
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(instance);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 設置屬性
* @param className 類的全名
* @param fieldName 屬性名
* @param instance 對應的對象,爲null時改變的是靜態變量
* @param value 值
*/
public static void setField(String className,Object instance,String fieldName,Object value){
if(className == null || className.equals("")){
throw new IllegalArgumentException("className 不能爲空");
}
if(fieldName == null || fieldName.equals("")){
throw new IllegalArgumentException("fieldName 不能爲空");
}
try {
Class<?> c = Class.forName(className);
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(instance, value);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根據方法名,類名,參數獲取方法
* @param className 類名,全名稱
* @param methodName 方法名
* @param paramsType 參數類型列表
* @return 方法對象
*/
public static Method getMethod(String className,String methodName,Class ... paramsType){
if(className == null || className.equals("")){
throw new IllegalArgumentException("className 不能爲空");
}
if(methodName == null || methodName.equals("")){
throw new IllegalArgumentException("methodName不能爲空");
}
try {
Class<?> c = Class.forName(className);
return c.getDeclaredMethod(methodName,paramsType);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
01.寫入數據
try {
MemoryFile pushMemoryFile = MemoryFileHelper.createMemoryFile("pushPreview", yuvData.length);
pushMemoryFile.writeBytes(yuvData, 0, 0, yuvData.length);
ParcelFileDescriptor pdf = MemoryFileHelper.getParcelFileDescriptor(pushMemoryFile);
// Log.e(TAG, "pushCameraData time: " + (System.currentTimeMillis() - startTime));
iService.pushCameraData(pdf, yuvData.length, width, height, startTime);
pushMemoryFile.close();
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "pushCameraData sendByMemory Exception: " + e.toString());
}
02 獲取數據
public void onCallbackPeopleDetection(ParcelFileDescriptor pfd, int length, long tag) {
try {
MemoryFile memoryFile = MemoryFileHelper.openMemoryFile(pfd, length, 1);
FileDescriptor fd = MemoryFileHelper.getFileDescriptor(memoryFile);
byte[] data = MemoryFileHelper.readFileDescriptor(fd);
Log.i(TAG, "onCallbackPeopleDetection: " + new String(data));
Log.i(TAG, "onCallbackPeopleDetection ,time: " + (System.currentTimeMillis() - tag) + ",length= " + data.length);
memoryFile.close();
} catch (Exception e) {
Log.e(TAG, "onCallbackPeopleDetection error: " + e.toString());
}
}
03 AIDL
interface IClient {
/**
* detect people
* response
*/
void onCallbackPeopleDetection(in ParcelFileDescriptor pfd,int length,long tag);
}
別人寫的:
https://github.com/LovelyLittleDemon/ShareMemoryRemote