Android 几种进程通信方式
跨进程通信要求把方法调用及其数据分解至操作系统可以识别的程度,并将其从本地进程和地址空间传输至远程进程和地址空间,然后在远程进程中重新组装并执行该调用。
然后,返回值将沿相反方向传输回来。
Android 为我们提供了以下几种进程通信机制(供开发者使用的进程通信 API)对应的文章链接如下:
-
文件
-
AIDL (基于 Binder)
-
Binder
-
Messenger (基于 Binder)
-
ContentProvider (基于 Binder)
-
Socket
在上述通信机制的基础上,我们只需集中精力定义和实现 RPC 编程接口即可。
如何选择这几种通信方式
这里再对比总结一下:
- 只有允许不同应用的客户端用 IPC 方式调用远程方法,并且想要在服务中处理多线程时,才有必要使用
AIDL
- 如果需要调用远程方法,但不需要处理并发 IPC,就应该通过实现一个
Binder
创建接口 - 如果您想执行 IPC,但只是传递数据,不涉及方法调用,也不需要高并发,就使用
Messenger
来实现接口 - 如果需要处理一对多的进程间数据共享(主要是数据的 CRUD),就使用
ContentProvider
- 如果要实现一对多的并发实时通信,就使用
Socket
image.png
IPC分析:
android IPC的核心方式是binder,但是android binder的传输限制1M(被很多进程共享),在较大数据交换一般通过文件,但是效率很低,因此介绍下新的内存共享方式:
ShareMemory
具体实现:
通过binder把MemoryFile的ParcelFileDescriptor 传到Service;
在服务端通过ParcelFileDescriptor 读取共享内存数据;
- 客户端 LocalClient.java
通过MemoryFile获取ParcelFileDescriptor,通过Binder把ParcelFileDescriptor(int类型)传递到服务端
package com.yinlib.client;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import com.yinlib.service.aidl.IRemoteService;
import java.io.FileDescriptor;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Created by jayzwang on 7/12/18.
*/
public class LocalClient {
public static final String TAG = LocalClient.class.getSimpleName();
public static final String SERVICE_ACTION = "com.yinlib.service.remoteservice";
public static final String SERVICE_PACKAGE = "com.yinlib.service";
private IRemoteService Client ;
private Context mContext;
private boolean mHasBind;
private IServiceListener mServiceListener;
private MemoryFile mServiceShareMemory;
private FileDescriptor mServiceShareFile;
private ParcelFileDescriptor mParceServiceShareFile;
private int mFD = -1;
int CONTENT_SIZE = 640*480;
// int CONTENT_SIZE = 200;
private byte[] mContent = new byte[CONTENT_SIZE];
private byte[] mContentCopy = new byte[CONTENT_SIZE];
public interface IServiceListener{
void onServiceConnect();
void onServiceDisconnect();
}
public LocalClient(Context context) {
mContext = context.getApplicationContext();
createMemFile();
}
private void createMemFile(){
try {
mServiceShareMemory = new MemoryFile("com.yinlib.service" + System.currentTimeMillis(), mContent.length);
Method method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
FileDescriptor fd = (FileDescriptor) method.invoke(mServiceShareMemory);
mParceServiceShareFile = ParcelFileDescriptor.dup(fd);
if(mServiceShareMemory != null){
mServiceShareMemory.allowPurging(false);
}
}catch (Exception e){
e.printStackTrace();
}
}
//connect to arservice, call onStart
public boolean connectService(){
boolean isBindService = mContext.getApplicationContext().bindService(new Intent(SERVICE_ACTION).setPackage(SERVICE_PACKAGE), mServiceConnect, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT | Context.BIND_WAIVE_PRIORITY | Context.BIND_ABOVE_CLIENT);
Log.d(TAG,"bind services isBindService : " + isBindService);
return isBindService;
}
//disconnet service, call onDestroy
public void disConnectService(){
if(!mHasBind){
return;
}
mContext.unbindService(mServiceConnect);
mHasBind = false;
}
public void setServiceListener(IServiceListener listener){
mServiceListener = listener;
}
public void dataFlow(int value){
Arrays.fill(mContent, (byte)value);
if(mHasBind){
try {
Log.d(TAG, "Client dataFlow start mContent: " + mContent[0]);
long time = System.currentTimeMillis();
mServiceShareMemory.writeBytes(mContent,0,0, mContent.length);
Log.d(TAG, "Client dataFlow start mContentCopy: " + mContentCopy[0]);
Log.d(TAG, "Client dataFlow writeBytes : " + (System.currentTimeMillis()- time));
Client .dataFlow(mParceServiceShareFile, mContent.length);
Log.d(TAG, "Client dataFlow create and flowTime : " + (System.currentTimeMillis()- time));
time = System.currentTimeMillis();
Log.d(TAG, "Client dataFlow release : " + (System.currentTimeMillis()- time));
}catch (Exception e){
e.printStackTrace();
}
}
}
public int getStatus(){
int status = 0;
if(mHasBind){
try {
status = Client .getStatus();
Log.d(TAG, "Client getStatus : " + status);
}catch (Exception e){
e.printStackTrace();
}
}
return status;
}
private ServiceConnection mServiceConnect = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Client = IRemoteService.Stub.asInterface(iBinder);
mHasBind = true;
Log.d(TAG, "onServiceConnected");
onConnect();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mHasBind = false;
Log.d(TAG, "onServiceDisconnected");
onDisonnect();
}
};
private void onConnect(){
if(mServiceListener == null){
return;
}
mServiceListener.onServiceConnect();
}
private void onDisonnect(){
releaseShareMemory();
if(mServiceListener == null){
return;
}
mServiceListener.onServiceDisconnect();
}
private void releaseShareMemory(){
try {
mParceServiceShareFile.close();
}catch (Exception e){
e.printStackTrace();
}
if(mServiceShareMemory == null){
return;
}
mServiceShareMemory.close();
mServiceShareMemory = null;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
releaseShareMemory();
}
}
- 服务端
RemoteService 获取到ParcelFileDescriptor 之后,有两种方式
第一种:通过FileInputStream 读取ParcelFileDescriptor 的FD,此种方式的问题在于,每次读取之后FD的游标都在文件最后(也就是说第二次读取结果是不低的,必须重置FD的游标)
第二种:就是通过反射,直接ParcelFileDescriptor构建MemoryFile,然后读取,此种方式问题在于26和27实现的不同,代码如下:
package com.yinlib.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.MemoryFile;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import com.yinlib.service.aidl.IRemoteService;
import com.yinlib.service.util.MemoryFileHelper;
/**
* $todo$
*
* @user Jay Wang
* @date 2018-07-31 11:31
*/
public class RemoteService extends Service {
public static final String TAG = RemoteService.class.getSimpleName();
private byte[] mPackageContent;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new RemoteServiceBind();
}
private void createBufferIfNeed(int length){
if(length <= 0){
Log.d(TAG, "createBufferIfNeed length <= 0");
}
if(mPackageContent != null && mPackageContent.length == length){
return ;
}
mPackageContent = new byte[length];
}
public class RemoteServiceBind extends IRemoteService.Stub
{
public RemoteServiceBind() {
super();
}
@Override
public IBinder asBinder() {
return super.asBinder();
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws
RemoteException {
return super.onTransact(code, data, reply, flags);
}
@Override
public void dataFlow(ParcelFileDescriptor data, int length) throws RemoteException {
try {
long time = System.currentTimeMillis();
createBufferIfNeed(length);
MemoryFile memoryFile = MemoryFileHelper.openMemoryFile(data, mPackageContent.length, MemoryFileHelper.OPEN_READWRITE);
memoryFile.readBytes(mPackageContent, 0, 0, mPackageContent.length);
memoryFile.close();
Log.d(TAG, "Service dataFlow data: " + mPackageContent[mPackageContent.length - 1] + " time : " + (System.currentTimeMillis() - time));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public int getStatus() throws RemoteException {
Log.d(TAG, "Service getStatus");
return 1;
}
}
}
package com.yinlib.service.util;
import android.os.Build;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
/**
* 对memoryFile类的扩展
* 1.从memoryFile对象中获取FileDescriptor,ParcelFileDescriptor
* 2.根据一个FileDescriptor和文件length实例化memoryFile对象
* Created by wangguijie on 2018/7/30.
*/
public class MemoryFileHelper {
/**
* 创建共享内存对象
* @param name 描述共享内存文件名称
* @param length 用于指定创建多大的共享内存对象
* @return MemoryFile 描述共享内存对象
*/
public static MemoryFile createMemoryFile(String name, int 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 is null");
}
FileDescriptor fd = pfd.getFileDescriptor();
return openMemoryFile(fd,length,mode);
}
private static final int PROT_READ = 0x1;
private static final int PROT_WRITE = 0x2;
public static final int OPEN_READONLY = PROT_READ;
public static final int OPEN_READWRITE = PROT_READ |PROT_WRITE;
/**
* 打开共享内存,一般是一个地方创建了一块共享内存
* 另一个地方持有描述这块共享内存的文件描述符,调用
* 此方法即可获得一个描述那块共享内存的MemoryFile
* 对象
* @param fd 文件描述
* @param length 共享内存的大小
* @param mode PROT_READ = 0x1只读方式打开,
* PROT_WRITE = 0x2可写方式打开,
* PROT_WRITE|PROT_READ可读可写方式打开
* @return MemoryFile
*/
public static MemoryFile openMemoryFile(FileDescriptor fd,int length,int mode){
if (mode != OPEN_READONLY && mode != OPEN_READWRITE)
throw new IllegalArgumentException("invalid mode, only support OPEN_READONLY and OPEN_READWRITE");
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) {
return openMemoryFileV26(fd, length, mode);
}
return openMemoryFileV27(fd, mode);
}
private static MemoryFile openMemoryFileV27(FileDescriptor fd, int mode) {
MemoryFile memoryFile = null;
try {
memoryFile = new MemoryFile("service.remote",1);
memoryFile.close();
Class<?> c = Class.forName("android.os.SharedMemory");
Object sharedMemory = InvokeUtil.newInstanceOrThrow(c,fd);
//SharedMemory sm;
ByteBuffer mapping = null;
if (mode == OPEN_READONLY) {
mapping = (ByteBuffer) InvokeUtil.invokeMethod(sharedMemory, "mapReadOnly");
} else {
mapping = (ByteBuffer) InvokeUtil.invokeMethod(sharedMemory, "mapReadWrite");
}
InvokeUtil.setValueOfField(memoryFile, "mSharedMemory", sharedMemory);
InvokeUtil.setValueOfField(memoryFile, "mMapping",mapping);
return memoryFile;
} catch (Exception e) {
throw new RuntimeException("openMemoryFile failed!", e);
}
}
public static MemoryFile openMemoryFileV26(FileDescriptor fd,int length,int mode){
MemoryFile memoryFile = null;
try {
memoryFile = new MemoryFile("service.remote",1);
memoryFile.close();
Class<?> c = MemoryFile.class;
InvokeUtil.setValueOfField(memoryFile, "mFD", fd);
InvokeUtil.setValueOfField(memoryFile, "mLength",length);
long address = (long) InvokeUtil.invokeStaticMethod(c, "native_mmap", fd, length, mode);
InvokeUtil.setValueOfField(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 is null");
}
ParcelFileDescriptor pfd = null;
try {
FileDescriptor fd = getFileDescriptor(memoryFile);
pfd = (ParcelFileDescriptor) InvokeUtil.newInstanceOrThrow(ParcelFileDescriptor.class, fd);
return pfd;
} catch (Exception e) {
throw new RuntimeException("InvokeUtil.newInstanceOrThrow failed", e);
}
}
/**
* 获取memoryFile的FileDescriptor
* @param memoryFile 描述一块共享内存
* @return 这块共享内存对应的文件描述符
*/
public static FileDescriptor getFileDescriptor(MemoryFile memoryFile) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
if(memoryFile == null){
throw new IllegalArgumentException("memoryFile is null");
}
FileDescriptor fd;
fd = (FileDescriptor) InvokeUtil.invokeMethod(memoryFile, "getFileDescriptor");
return fd;
}
}
package com.yinlib.service.util;
import android.text.TextUtils;
import android.util.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
/**
* 反映工具类
* Created by wangguijie
*/
public final class InvokeUtil {
private static final String TAG = "InvokeUtil";
public static <T> T newEmptyInstance(Class<? extends T> paramClass)
{
try
{
return newEmptyInstanceOrThrow(paramClass);
}
catch (Exception localException)
{
Log.w(TAG, "Meet exception when make instance as a " + paramClass.getSimpleName(),
localException);
}
return null;
}
private static Object getDefaultValue(Class<?> paramClass)
{
if ((Integer.TYPE.equals(paramClass)) || (Integer.class.equals(paramClass))
|| (Byte.TYPE.equals(paramClass)) || (Byte.class.equals(paramClass))
|| (Short.TYPE.equals(paramClass)) || (Short.class.equals(paramClass))
|| (Long.TYPE.equals(paramClass)) || (Long.class.equals(paramClass))
|| (Double.TYPE.equals(paramClass)) || (Double.class.equals(paramClass))
|| (Float.TYPE.equals(paramClass)) || (Float.class.equals(paramClass)))
return Integer.valueOf(0);
if ((Boolean.TYPE.equals(paramClass)) || (Boolean.class.equals(paramClass)))
return Boolean.valueOf(false);
if ((Character.TYPE.equals(paramClass)) || (Character.class.equals(paramClass)))
return Character.valueOf('\000');
return null;
}
public static <T> T newEmptyInstanceOrThrow(Class<? extends T> paramClass) throws IllegalAccessException,
InvocationTargetException, InstantiationException {
int i = 0;
Constructor[] constructors = paramClass.getDeclaredConstructors();
if ((constructors == null) || (constructors.length == 0))
throw new IllegalArgumentException("Can't get even one available constructor for " + paramClass);
for (Constructor constructor: constructors) {
constructor.setAccessible(true);
Class[] arrayOfClass = constructor.getParameterTypes();
if ((arrayOfClass == null) || (arrayOfClass.length == 0)) {
return (T) constructor.newInstance(new Object[0]);
}
}
Constructor localConstructor = constructors[0];
localConstructor.setAccessible(true);
Class[] paramClasses = localConstructor.getParameterTypes();
Object[] params = new Object[paramClasses.length];
while (i < paramClasses.length)
{
params[i] = getDefaultValue(paramClasses[i]);
i++;
}
return (T) localConstructor.newInstance(params);
}
public static <T> T newInstanceOrThrow(Class<? extends T> clz, Object...params) throws IllegalAccessException,
InvocationTargetException, InstantiationException {
Constructor[] constructors = clz.getDeclaredConstructors();
if ((constructors == null) || (constructors.length == 0))
throw new IllegalArgumentException("Can't get even one available constructor for " + clz);
Class[] paramClasses = new Class[params.length];
Constructor found = null;
for (Constructor constructor: constructors) {
Class[] arrayOfClass = constructor.getParameterTypes();
if (arrayOfClass.length != params.length)
continue;
if (params.length == 0) {
found = constructor;
break;
}
boolean matched = true;
for (int i = 0; i < params.length; i++) {
int v = instanceOf(params[0], arrayOfClass[i]);
if (v == INSTANCE_DENIED) {
matched = false;
break;
}
}
if (matched) {
found = constructor;
break;
}
}
if (found != null) {
found.setAccessible(true);
return (T) found.newInstance(params);
}
throw new NoSuchElementException("no Constructor match it!!");
}
public static Object invokeMethod(Object o, String methodName, Object...params)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = matchMethod(o.getClass(), methodName, params);
if (method == null)
throw new NoSuchMethodException("class " + o.getClass().getCanonicalName() +
" cannot find method " + methodName);
Object out = method.invoke(o, params);
return out;
}
public static Object invokeStaticMethod(String className, String methodName, Object...params)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException {
Class clz = Class.forName(className);
Method method = matchMethod(clz, methodName, params);
if (method == null)
throw new NoSuchMethodException("class " + className +
" cannot find method " + methodName);
Object out = method.invoke(null, params);
return out;
}
public static Object invokeStaticMethod(Class clz, String methodName, Object...params)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = matchMethod(clz, methodName, params);
if (method == null)
throw new NoSuchMethodException("class " + clz.getCanonicalName() +
" cannot find method " + methodName);
Object out = method.invoke(null, params);
return out;
}
private static Class getObjectClass(Object o) {
Class clz = o.getClass();
Class inner = wrappedClass(clz);
if (inner != null && inner.isPrimitive()) {
return inner;
}
return clz;
}
public static boolean isWrapClass(Class clz) {
Class inner = wrappedClass(clz);
if (inner != null)
return inner.isPrimitive();
return false;
}
public static Class wrappedClass(Class clz) {
try {
return ((Class) clz.getField("TYPE").get(null));
} catch (Exception e) {
return null;
}
}
public static Method[] methodsForName(Class clz, String name) {
Method[] methods = clz.getDeclaredMethods();
if (methods == null || methods.length == 0)
return null;
List<Method> out = new ArrayList<>();
for (Method method: methods) {
if (method.getName().equals(name)) {
out.add(method);
}
}
if (out.size() == 0)
return null;
return out.toArray(new Method[0]);
}
public static Method matchMethod(Class clz, String name, Object...params) {
Method[] methods = methodsForName(clz, name);
if (methods == null || methods.length == 0)
return null;
Method found = null;
int maxMatch = 0;
for (Method method: methods) {
int v = matchMethodParameterTypes(method, params);
if ( v > maxMatch) {
maxMatch = v;
found = method;
}
}
if (maxMatch == METHOD_MATCH_NONE)
return null;
if ((maxMatch & METHOD_MATCH_PUBLIC) == 0 ) {
found.setAccessible(true);
}
return found;
}
private final static int INSTANCE_DENIED = 0;
private final static int INSTANCE_OK = 1;
private final static int INSTANCE_CONV = 2;
private static int instanceOf(Object o, Class<?> clz) {
if ( o == null ) {
// 基本类型不允许null对象
if (clz.isPrimitive()) return INSTANCE_DENIED;
// 空对象可匹配任何对象类型
return INSTANCE_OK;
}
if (clz.isPrimitive()) {
if (clz == void.class)
return INSTANCE_DENIED;
Class wclz = wrappedClass(o.getClass());
// 非封装类型对象
if (wclz == null)
return INSTANCE_DENIED;
// 基本类型与封装类型完全匹配
if (wclz == clz)
return INSTANCE_OK;
// 基本类型与封装类型完全不匹配
if (clz == long.class && wclz == int.class)
return INSTANCE_CONV;
if (clz == double.class && (wclz == float.class || wclz == long.class || wclz == int.class) )
return INSTANCE_CONV;
if (clz == float.class && wclz == int.class)
return INSTANCE_CONV;
if (clz == int.class && (wclz == byte.class || wclz == short.class || wclz == char.class) )
return INSTANCE_CONV;
return INSTANCE_DENIED;
}
return clz.isInstance(o)?INSTANCE_OK: INSTANCE_DENIED;
}
private final static int METHOD_MATCH_NONE = 0;
private final static int METHOD_MATCH_PUBLIC = 0x01;
private final static int METHOD_MATCH_PARAMS_TYPE = 0x02;
private final static int METHOD_MATCH_STRICTLY = METHOD_MATCH_PUBLIC | METHOD_MATCH_PARAMS_TYPE;
private static int matchMethodParameterTypes(Method method, Object...params) {
Class[] types = method.getParameterTypes();
int tlen = ArrayUtils.length(types);
int plen = ArrayUtils.length(params);
int value = METHOD_MATCH_NONE;
if (tlen != plen) {
return METHOD_MATCH_NONE;
}
if (plen > 0) {
int[] pos = new int[plen];
int size = 0;
for (int i= 0; i< plen; i++) {
Object p = params[i];
int v = instanceOf(p, types[i]);
if (v == INSTANCE_DENIED) {
return METHOD_MATCH_NONE;
}
else if (v == INSTANCE_OK)
continue;
else
pos[size++] = i;
}
if (size > 0) {
for (int index: pos) {
Object p = params[index];
if (p instanceof Number) {
Number n = (Number) p;
if (types[index] == int.class) {
params[index] = n.intValue();
}
else if (types[index] == long.class) {
params[index] = n.longValue();
}
else if (types[index] == double.class) {
params[index] = n.doubleValue();
}
else if (types[index] == float.class) {
params[index] = n.floatValue();
}
else if (types[index] == byte.class) {
params[index] = n.byteValue();
}
else if (types[index] == short.class) {
params[index] = n.shortValue();
}
}
else if (p instanceof Character) {
char c = (Character)p;
if (types[index] == int.class) {
params[index] = (int)c;
}
else if (types[index] == long.class) {
params[index] = (long)c;
}
else if (types[index] == byte.class) {
params[index] = (byte)c;
}
else if (types[index] == short.class) {
params[index] = (short)c;
}
}
}
}
}
value |= METHOD_MATCH_PARAMS_TYPE;
if (Modifier.isPublic(method.getModifiers())) {
value |= METHOD_MATCH_PUBLIC;
}
return value;
}
public static Object valueOfField(Object o, String fieldName) throws NoSuchFieldException, IllegalAccessException {
if (TextUtils.isEmpty(fieldName))
throw new IllegalArgumentException("param fieldName is empty");
Class clz = o.getClass();
Field field = fieldByNameRecursive(clz, fieldName);
if (!Modifier.isPublic (field.getModifiers()) ) {
field.setAccessible(true);
}
return field.get(o);
}
@SuppressWarnings("unchecked")
public static <T> Object valueOfField(T o, Class superClass, String fieldName) throws NoSuchFieldException, IllegalAccessException {
if (TextUtils.isEmpty(fieldName))
throw new IllegalArgumentException("param fieldName is empty");
Class clz = o.getClass();
if (superClass == null)
superClass = clz;
else if (!superClass.isAssignableFrom(clz))
throw new IllegalArgumentException("superClass not match the object o " + clz.getCanonicalName());
Field field = superClass.getDeclaredField(fieldName);
if (!Modifier.isPublic (field.getModifiers()) ) {
field.setAccessible(true);
}
return field.get(o);
}
public static Object valueOfStaticField(Class clz, String fieldName) throws NoSuchFieldException, IllegalAccessException {
if (TextUtils.isEmpty(fieldName))
throw new IllegalArgumentException("param fieldName is empty");
Field field = fieldByNameRecursive(clz, fieldName);
if (!Modifier.isPublic (field.getModifiers()) ) {
field.setAccessible(true);
}
return field.get(null);
}
public static void setValueOfField(Object o, String fieldName, Object value) throws NoSuchFieldException,
IllegalAccessException {
if (TextUtils.isEmpty(fieldName))
throw new IllegalArgumentException("param fieldName is empty");
Class clz = o.getClass();
Field field = fieldByNameRecursive(clz, fieldName);
if (!Modifier.isPublic (field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
field.setAccessible(true);
}
field.set(o, value);
}
public static void setStaticValueOfField(Class clz, String fieldName, Object value) throws NoSuchFieldException,
IllegalAccessException {
if (TextUtils.isEmpty(fieldName))
throw new IllegalArgumentException("param fieldName is empty");
Field field = fieldByNameRecursive(clz, fieldName);
if (!Modifier.isPublic (field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
field.setAccessible(true);
}
field.set(null, value);
}
public static Field[] fieldsByClassRecursive(Class clz, Class memberClz) throws NoSuchFieldException {
Class target = clz;
ArrayList<Field> all = null;
while (!target.equals(Object.class)) {
Field[] fields = target.getDeclaredFields();
if (!ArrayUtils.empty(fields)) {
for (Field field: fields) {
if (field.getDeclaringClass().equals(memberClz)) {
if (all == null)
all = new ArrayList<>();
all.add(field);
}
}
}
target = clz.getSuperclass();
}
if (CollectionUtils.isEmpty(all))
throw new NoSuchFieldException("no such field for class " + memberClz.getName());
return all.toArray(new Field[0]);
}
public static Field fieldByNameRecursive(Class clz, String fieldName) throws NoSuchFieldException {
Class target = clz;
while (!target.equals(Object.class)) {
try {
Field field = target.getDeclaredField(fieldName);
return field;
} catch (NoSuchFieldException e) {
target = clz.getSuperclass();
}
}
throw new NoSuchFieldException(fieldName);
}
// public static void printAllFields(Class clz) {
//
// Class target = clz;
// String prefix = "===";
// int depth = 1;
// String p = null;
// while (!target.equals(Object.class)) {
// Field[] fields = target.getDeclaredFields();
// p = StringUtils.repeat(prefix, depth);
// NLog.i(TAG, "%s%s Fields:", p, target.getName());
// if (fields != null && fields.length > 0) {
// for(int i= 0; i< fields.length; i++) {
// NLog.i(TAG,"%s Field[%d]: %s%s %s", p, i,
// modifiers(fields[i].getModifiers()),
// className(fields[i].getType()),
// fields[i].getName()
// );
// }
// }
// }
// }
private static String className(Class clz) {
if (clz.isPrimitive()) {
Class s = wrappedClass(clz);
return s != null? s.getName(): null;
} else {
return clz.getName();
}
}
private static String modifiers(int modifiers) {
StringBuilder sb = new StringBuilder();
if (Modifier.isPublic(modifiers)) {
sb.append("public ");
}
else if (Modifier.isPrivate(modifiers)) {
sb.append("private ");
}
else if (Modifier.isProtected(modifiers)) {
sb.append("protected ");
}
if (Modifier.isFinal(modifiers)) {
sb.append("final ");
}
if (Modifier.isStatic(modifiers)) {
sb.append("static ");
}
if (Modifier.isVolatile(modifiers)) {
sb.append("volatile ");
}
return sb.toString();
}
/* public static void test(String a) {
System.out.println("test " + a);
}*/
public static void main(String[] args) {
String test = "okabc";
try {
Object value = 1f;
System.out.println(String.class.isInstance(value));
Object o = InvokeUtil.invokeMethod(test, "equals", 1);
System.out.println(o);
InvokeUtil.invokeStaticMethod(InvokeUtil.class, "test", new Object[]{null});
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
Android P(9.0)反射限制:
上述反射的方式在android P上被限制(android 9.0禁止通过放射调用系统的的非公开方法),此路不同(If they cut off one head, two more shall take it's place... Hail Hydra.),还有千万条路
- ShareMemory android O(8.0)之后增加新的共享内存方式,SharedMemory.java 此类继承Parcelable,可以作为IPC通讯传输的数据;
- ClassLoader 多态:此方式并非真正的多态,而是根据ClassLoader类的加载顺序,在应用中构建一个和系统类同样包名的类(方法也同名,可以不做实现),编译时使用的应用中的类,运行时加载的是系统中的类,从而实现伪多态;
GitHub:https://github.com/LovelyLittleDemon/ShareMemoryRemote
ShareMemory优点:
binder 限制(binder的android上的限制1M,而且是被多个进程共享的);
binder 在android进程中经过一次内存copy,内存共享通过mmap,0次copy效率更高;
网友评论