MemoryFile一個可以幫助開發者"偷"內存的地方

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章