0-Android使用Ashmem機制進行跨進程共享內存


Android使用Ashmem機制進行跨進程共享內存



導語:

Android系統中,提供了獨特的匿名共享內存子系統Ashmem(Anonymous Shared Memory),它以驅動程序的形式實現在內核空間中。它有兩個特點:

1.一是能夠輔助內存管理系統來有效地管理不再使用的內存塊

2.二是它通過Binder進程間通信機制來實現進程間的內存共享。

本文中,我們將通過實例來簡要介紹Android系統的匿名共享內存的使用方法,使得我們對Android系統的匿名共享內存機制有一個感性的認識,為進一步學習它的源代碼實現打下基礎。



案例原理:

Android系統的匿名共享內存子系統的主體是以驅動程序的形式存在。在系統運行時庫層和應用程序框架層提供了訪問接口,其中,在系統運行時庫層提供了C/C++調用接口,而在應用程序框架層提供了Java調用接口。

這里,我們將直接通過應用程序框架層提供的Java調用接口來說明匿名共享內存子系統Ashmem的使用方法,畢竟我們在Android開發應用程序時,是基於Java語言的,而實際上,應用程序框架層的Java調用接口是通過JNI方法來調用系統運行時庫層的C/C++調用接口,最后進入到內核空間的Ashmem驅動程序去的。

        

我們在這里舉的例子是一個名為Ashmem的應用程序,它包含了一個Server端和一個Client端實現,

1. Server端是以Service的形式實現的,在這里Service里面,創建一個匿名共享內存文件

2. Client是一個Activity,這個Activity通過Binder進程間通信機制獲得前面這個Service創建的匿名共享內存文件的句柄,從而實現共享。

在Android應用程序框架層,提供了一個MemoryFile接口來封裝了匿名共享內存文件的創建和使用,它實現在frameworks/base/core/java/android/os/MemoryFile.java文件中。

下面,我們就來看看Server端是如何通過MemoryFile類來創建匿名共享內存文件的以及Client是如何獲得這個匿名共享內存文件的句柄的。

        

在MemoryFile類中,提供了兩種創建匿名共享內存的方法,我們通過MemoryFile類的構造函數來看看這兩種使用方法:

    
    
    
            
  1. public class MemoryFile
  2. {
  3. ......
  4. /**
  5. * Allocates a new ashmem region. The region is initially not purgable.
  6. *
  7. * @param name optional name for the file (can be null).
  8. * @param length of the memory file in bytes.
  9. * @throws IOException if the memory file could not be created.
  10. */
  11. public MemoryFile(String name, int length) throws IOException {
  12. mLength = length;
  13. mFD = native_open(name, length);
  14. mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
  15. mOwnsRegion = true;
  16. }
  17. /**
  18. * Creates a reference to an existing memory file. Changes to the original file
  19. * will be available through this reference.
  20. * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail.
  21. *
  22. * @param fd File descriptor for an existing memory file, as returned by
  23. * {@link #getFileDescriptor()}. This file descriptor will be closed
  24. * by {@link #close()}.
  25. * @param length Length of the memory file in bytes.
  26. * @param mode File mode. Currently only "r" for read-only access is supported.
  27. * @throws NullPointerException if <code>fd</code> is null.
  28. * @throws IOException If <code>fd</code> does not refer to an existing memory file,
  29. * or if the file mode of the existing memory file is more restrictive
  30. * than <code>mode</code>.
  31. *
  32. * @hide
  33. */
  34. public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {
  35. if (fd == null) {
  36. throw new NullPointerException("File descriptor is null.");
  37. }
  38. if (!isMemoryFile(fd)) {
  39. throw new IllegalArgumentException("Not a memory file.");
  40. }
  41. mLength = length;
  42. mFD = fd;
  43. mAddress = native_mmap(mFD, length, modeToProt(mode));
  44. mOwnsRegion = false;
  45. }
  46. ......
  47. }  viewcopy


兩個構造函數的主要區別是第一個參數

1.第一種構造方法是以指定的字符串調用JNI方法native_open來創建一個匿名共享內存文件,得到一個文件描述符,接着就以這個文件描述符為參數調用JNI方法natvie_mmap把這個匿名共享內存文件映射在進程空間中,然后就可以通過這個映射后得到的地址空間來直接訪問內存數據了;


2.第二種構造方法是以指定的文件描述符來直接調用JNI方法natvie_mmap把這個匿名共享內存文件映射在進程空間中,然后進行訪問,而這個文件描述符就必須要是一個匿名共享內存文件的文件描述符,這是通過一個內部函數isMemoryFile來驗證的,而這個內部函數isMemoryFile也是通過JNI方法調用來進一步驗證的。前面所提到的這些JNI方法調用,最終都是通過系統運行時庫層進入到內核空間的Ashmem驅動程序中去,不過這里我們不關心這些JNI方法、系統運行庫層調用以及Ashmem驅動程序的具體實現,在接下來的兩篇文章中,我們將會着重介紹,這里我們只關注MemoryFile這個類的使用方法。

前面我們說到,我們在這里舉的例子包含了一個Server端和一個Client端實現,

1. Server端就是通過第一個構造函數來創建一個匿名共享內存文件


2. Client端過Binder進程間通信機制來向Server請求獲取這個匿名共享內存的文件描述符,有了這個文件描述符之后,就可以通過后面一個構造函數來共享這個內存文件了。 然后Client和Server之間就可以通過這個這個匿名內存共享數據了。



案例實現:

首先在源代碼工程的packages/experimental目錄下創建一個應用程序工程目錄Ashmem。它定義了一個路徑為shy.luo.ashmem的package,這個例子的源代碼主要就是實現在這里了。

將會逐一介紹這個package里面的文件。這里要用到的Binder進程間通信接口定義在src/shy/luo/ashmem/IMemoryService.java文件中:


    
    
    
            
  1. package shy.luo.ashmem;
  2. import android.util.Log;
  3. import android.os.IInterface;
  4. import android.os.Binder;
  5. import android.os.IBinder;
  6. import android.os.Parcel;
  7. import android.os.ParcelFileDescriptor;
  8. import android.os.RemoteException;
  9. public interface IMemoryService extends IInterface {
  10. public static abstract class Stub extends Binder implements IMemoryService {
  11. private static final String DESCRIPTOR = "shy.luo.ashmem.IMemoryService";
  12. public Stub() {
  13. attachInterface(this, DESCRIPTOR);
  14. }
  15. public static IMemoryService asInterface(IBinder obj) {
  16. if (obj == null) {
  17. return null;
  18. }
  19. IInterface iin = (IInterface)obj.queryLocalInterface(DESCRIPTOR);
  20. if (iin != null && iin instanceof IMemoryService) {
  21. return (IMemoryService)iin;
  22. }
  23. return new IMemoryService.Stub.Proxy(obj);
  24. }
  25. public IBinder asBinder() {
  26. return this;
  27. }
  28. @Override
  29. public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws android.os.RemoteException {
  30. switch (code) {
  31. case INTERFACE_TRANSACTION: {
  32. reply.writeString(DESCRIPTOR);
  33. return true;
  34. }
  35. case TRANSACTION_getFileDescriptor: {
  36. data.enforceInterface(DESCRIPTOR);
  37. ParcelFileDescriptor result = this.getFileDescriptor();
  38. reply.writeNoException();
  39. if (result != null) {
  40. reply.writeInt(1);
  41. result.writeToParcel(reply, 0);
  42. } else {
  43. reply.writeInt(0);
  44. }
  45. return true;
  46. }
  47. case TRANSACTION_setValue: {
  48. data.enforceInterface(DESCRIPTOR);
  49. int val = data.readInt();
  50. setValue(val);
  51. reply.writeNoException();
  52. return true;
  53. }
  54. }
  55. return super.onTransact(code, data, reply, flags);
  56. }
  57. private static class Proxy implements IMemoryService {
  58. private IBinder mRemote;
  59. Proxy(IBinder remote) {
  60. mRemote = remote;
  61. }
  62. public IBinder asBinder() {
  63. return mRemote;
  64. }
  65. public String getInterfaceDescriptor() {
  66. return DESCRIPTOR;
  67. }
  68. public ParcelFileDescriptor getFileDescriptor() throws RemoteException {
  69. Parcel data = Parcel.obtain();
  70. Parcel reply = Parcel.obtain();
  71. ParcelFileDescriptor result;
  72. try {
  73. data.writeInterfaceToken(DESCRIPTOR);
  74. mRemote.transact(Stub.TRANSACTION_getFileDescriptor, data, reply, 0);
  75. reply.readException();
  76. if (0 != reply.readInt()) {
  77. result = ParcelFileDescriptor.CREATOR.createFromParcel(reply);
  78. } else {
  79. result = null;
  80. }
  81. } finally {
  82. reply.recycle();
  83. data.recycle();
  84. }
  85. return result;
  86. }
  87. public void setValue(int val) throws RemoteException {
  88. Parcel data = Parcel.obtain();
  89. Parcel reply = Parcel.obtain();
  90. try {
  91. data.writeInterfaceToken(DESCRIPTOR);
  92. data.writeInt(val);
  93. mRemote.transact(Stub.TRANSACTION_setValue, data, reply, 0);
  94. reply.readException();
  95. } finally {
  96. reply.recycle();
  97. data.recycle();
  98. }
  99. }
  100. }
  101. static final int TRANSACTION_getFileDescriptor = IBinder.FIRST_CALL_TRANSACTION + 0;
  102. static final int TRANSACTION_setValue = IBinder.FIRST_CALL_TRANSACTION + 1;
  103. }
  104. public ParcelFileDescriptor getFileDescriptor() throws RemoteException;
  105. public void setValue(int val) throws RemoteException;
  106. }  view pcopy

這里主要是定義了IMemoryService接口,它里面有兩個調用接口:
      
      
      
              
  1. public ParcelFileDescriptor getFileDescriptor() throws RemoteException;
  2. public void setValue(int val) throws RemoteException;
同時,還分別定義了用於Server端實現的IMemoryService.Stub基類和用於Client端使用的代理IMemoryService.Stub.Proxy類。
       
有了Binder進程間通信接口之后,接下來就是要在Server端實現一個本地服務了。這里,Server端實現的本地服務名為MemoryService,實現在src/shy/luo/ashmem/MemoryService.java文件中:
        
        
        
                
  1. package shy.luo.ashmem;
  2. import java.io.FileDescriptor;
  3. import java.io.IOException;
  4. import android.os.Parcel;
  5. import android.os.MemoryFile;
  6. import android.os.ParcelFileDescriptor;
  7. import android.util.Log;
  8. public class MemoryService extends IMemoryService.Stub {
  9. private final static String LOG_TAG = "shy.luo.ashmem.MemoryService";
  10. private MemoryFile file = null;
  11. public MemoryService() {
  12. try {
  13. file = new MemoryFile("Ashmem", 4);
  14. setValue(0);
  15. }
  16. catch(IOException ex) {
  17. Log.i(LOG_TAG, "Failed to create memory file.");
  18. ex.printStackTrace();
  19. }
  20. }
  21. public ParcelFileDescriptor getFileDescriptor() {
  22. Log.i(LOG_TAG, "Get File Descriptor.");
  23. ParcelFileDescriptor pfd = null;
  24. try {
  25. pfd = file.getParcelFileDescriptor();
  26. } catch(IOException ex) {
  27. Log.i(LOG_TAG, "Failed to get file descriptor.");
  28. ex.printStackTrace();
  29. }
  30. return pfd;
  31. }
  32. public void setValue(int val) {
  33. if(file == null) {
  34. return;
  35. }
  36. byte[] buffer = new byte[4];
  37. buffer[0] = (byte)((val >>> 24) & 0xFF);
  38. buffer[1] = (byte)((val >>> 16) & 0xFF);
  39. buffer[2] = (byte)((val >>> 8) & 0xFF);
  40. buffer[3] = (byte)(val & 0xFF);
  41. try {
  42. file.writeBytes(buffer, 0, 0, 4);
  43. Log.i(LOG_TAG, "Set value " + val + " to memory file. ");
  44. }
  45. catch(IOException ex) {
  46. Log.i(LOG_TAG, "Failed to write bytes to memory file.");
  47. ex.printStackTrace();
  48. }
  49. }
  50. }  view pcopy

1、這里的MemoryService類實現了IMemoryService.Stub類,表示這是一個Binder服務的本地實現。
2、在構造函數中,通過指定文件名和文件大小來創建了一個匿名共享內存文件,即創建MemoryFile的一個實例,並保存在類成員變量file中。
3、這個匿名共享內存文件名為"Ashmem",大小為4個節字,剛好容納一個整數,我們這里舉的例子就是要說明如果創建一個匿名共享內存來在兩個進程間實現共享一個整數了。當然,在實際應用中,可以根據需要創建合適大小的共享內存來共享有意義的數據。

這里還實現了IMemoryService.Stub的兩個接口getFileDescriptor和setVal,一個用來獲取匿名共享內存文件的文件描述符,一個來往匿名共享內存文件中寫入一個整數,其中,接口getFileDescriptor的返回值是一個ParcelFileDescriptor。在Java中,是用FileDescriptor類來表示一個文件描述符的,而ParcelFileDescriptor是用來序列化FileDescriptor的,以便在進程間調用時傳輸。
        

定義好本地服務好,就要定義一個Server來啟動這個服務了。這里定義的Server實現在src/shy/luo/ashmem/Server.java文件中:

    
    
    
            
  1. package shy.luo.ashmem;
  2. import android.app.Service;
  3. import android.content.Intent;
  4. import android.os.IBinder;
  5. import android.util.Log;
  6. import android.os.ServiceManager;
  7. public class Server extends Service {
  8. private final static String LOG_TAG = "shy.luo.ashmem.Server";
  9. private MemoryService memoryService = null;
  10. @Override
  11. public IBinder onBind(Intent intent) {
  12. return null;
  13. }
  14. @Override
  15. public void onCreate() {
  16. Log.i(LOG_TAG, "Create Memory Service...");
  17. memoryService = new MemoryService();
  18. try {
  19. ServiceManager.addService("AnonymousSharedMemory", memoryService);
  20. Log.i(LOG_TAG, "Succeed to add memory service.");
  21. } catch (RuntimeException ex) {
  22. Log.i(LOG_TAG, "Failed to add Memory Service.");
  23. ex.printStackTrace();
  24. }
  25. }
  26. @Override
  27. public void onStart(Intent intent, int startId) {
  28. Log.i(LOG_TAG, "Start Memory Service.");
  29. }
  30. @Override
  31. public void onDestroy() {
  32. Log.i(LOG_TAG, "Destroy Memory Service.");
  33. }
  34. }

這個Server繼承了Android系統應用程序框架層提供的Service類,當它被啟動時,運行在一個獨立的進程中。當這個Server被啟動時,它的onCreate函數就會被調用,然后它就通過ServiceManager的addService接口來添加MemoryService了:
       
       
       
               
  1. memoryService = new MemoryService();
  2. try {
  3. ServiceManager.addService("AnonymousSharedMemory", memoryService);
  4. Log.i(LOG_TAG, "Succeed to add memory service.");
  5. } catch (RuntimeException ex) {
  6. Log.i(LOG_TAG, "Failed to add Memory Service.");
  7. ex.printStackTrace();
  8. }
這樣,當這個Server成功啟動了,Client就可以通過ServiceManager的getService接口來獲取這個MemoryService了。 接着,我們就來看Client端的實現。Client端是一個Activity,實現在src/shy/luo/ashmem/Client.java文件中:
        
        
        
                
  1. package shy.luo.ashmem;
  2. import java.io.FileDescriptor;
  3. import java.io.IOException;
  4. import shy.luo.ashmem.R;
  5. import android.app.Activity;
  6. import android.content.Intent;
  7. import android.os.Bundle;
  8. import android.os.MemoryFile;
  9. import android.os.ParcelFileDescriptor;
  10. import android.os.ServiceManager;
  11. import android.os.RemoteException;
  12. import android.util.Log;
  13. import android.view.View;
  14. import android.view.View.OnClickListener;
  15. import android.widget.Button;
  16. import android.widget.EditText;
  17. public class Client extends Activity implements OnClickListener {
  18. private final static String LOG_TAG = "shy.luo.ashmem.Client";
  19. IMemoryService memoryService = null;
  20. MemoryFile memoryFile = null;
  21. private EditText valueText = null;
  22. private Button readButton = null;
  23. private Button writeButton = null;
  24. private Button clearButton = null;
  25. @Override
  26. public void onCreate(Bundle savedInstanceState) {
  27. super.onCreate(savedInstanceState);
  28. setContentView(R.layout.main);
  29. IMemoryService ms = getMemoryService();
  30. if(ms == null) {
  31. startService(new Intent("shy.luo.ashmem.server"));
  32. } else {
  33. Log.i(LOG_TAG, "Memory Service has started.");
  34. }
  35. valueText = (EditText)findViewById(R.id.edit_value);
  36. readButton = (Button)findViewById(R.id.button_read);
  37. writeButton = (Button)findViewById(R.id.button_write);
  38. clearButton = (Button)findViewById(R.id.button_clear);
  39. readButton.setOnClickListener(this);
  40. writeButton.setOnClickListener(this);
  41. clearButton.setOnClickListener(this);
  42. Log.i(LOG_TAG, "Client Activity Created.");
  43. }
  44. @Override
  45. public void onResume() {
  46. super.onResume();
  47. Log.i(LOG_TAG, "Client Activity Resumed.");
  48. }
  49. @Override
  50. public void onPause() {
  51. super.onPause();
  52. Log.i(LOG_TAG, "Client Activity Paused.");
  53. }
  54. @Override
  55. public void onClick(View v) {
  56. if(v.equals(readButton)) {
  57. int val = 0;
  58. MemoryFile mf = getMemoryFile();
  59. if(mf != null) {
  60. try {
  61. byte[] buffer = new byte[4];
  62. mf.readBytes(buffer, 0, 0, 4);
  63. val = (buffer[0] << 24) | ((buffer[1] & 0xFF) << 16) | ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF);
  64. } catch(IOException ex) {
  65. Log.i(LOG_TAG, "Failed to read bytes from memory file.");
  66. ex.printStackTrace();
  67. }
  68. }
  69. String text = String.valueOf(val);
  70. valueText.setText(text);
  71. } else if(v.equals(writeButton)) {
  72. String text = valueText.getText().toString();
  73. int val = Integer.parseInt(text);
  74. IMemoryService ms = getMemoryService();
  75. if(ms != null) {
  76. try {
  77. ms.setValue(val);
  78. } catch(RemoteException ex) {
  79. Log.i(LOG_TAG, "Failed to set value to memory service.");
  80. ex.printStackTrace();
  81. }
  82. }
  83. } else if(v.equals(clearButton)) {
  84. String text = "";
  85. valueText.setText(text);
  86. }
  87. }
  88. private IMemoryService getMemoryService() {
  89. if(memoryService != null) {
  90. return memoryService;
  91. }
  92. memoryService = IMemoryService.Stub.asInterface(
  93. ServiceManager.getService("AnonymousSharedMemory"));
  94. Log.i(LOG_TAG, memoryService != null ? "Succeed to get memeory service." : "Failed to get memory service.");
  95. return memoryService;
  96. }
  97. private MemoryFile getMemoryFile() {
  98. if(memoryFile != null) {
  99. return memoryFile;
  100. }
  101. IMemoryService ms = getMemoryService();
  102. if(ms != null) {
  103. try {
  104. ParcelFileDescriptor pfd = ms.getFileDescriptor();
  105. if(pfd == null) {
  106. Log.i(LOG_TAG, "Failed to get memory file descriptor.");
  107. return null;
  108. }
  109. try {
  110. FileDescriptor fd = pfd.getFileDescriptor();
  111. if(fd == null) {
  112. Log.i(LOG_TAG, "Failed to get memeory file descriptor.");
  113. return null;
  114. }
  115. memoryFile = new MemoryFile(fd, 4, "r");
  116. } catch(IOException ex) {
  117. Log.i(LOG_TAG, "Failed to create memory file.");
  118. ex.printStackTrace();
  119. }
  120. } catch(RemoteException ex) {
  121. Log.i(LOG_TAG, "Failed to get file descriptor from memory service.");
  122. ex.printStackTrace();
  123. }
  124. }
  125. return memoryFile;
  126. }
  127. }  view pcopy
Client端的界面主要包含了三個按鈕Read、Write和Clear,以及一個用於顯示內容的文本框。

這個Activity在onCreate時,會通過startService接口來啟動我們前面定義的Server進程。調用startService時,需要指定要啟動的服務的名稱,這里就是"shy.luo.ashmem.server"了,后面我們會在程序的描述文件AndroidManifest.xml看到前面的Server類是如何和名稱"shy.luo.ashmem.server"關聯起來的。關於調用startService函數來啟動自定義服務的過程,可以參考Android系統在新進程中啟動自定義服務過程(startService)的原理分析一文。

        

內部函數getMemoryService用來獲取IMemoryService。如果是第一次調用該函數,則會通過ServiceManager的getService接口來獲得這個IMemoryService接口,然后保存在類成員變量memoryService中,以后再調用這個函數時,就可以直接返回memoryService了。

        

內部函數getMemoryFile用來從MemoryService中獲得匿名共享內存文件的描述符。同樣,如果是第一次調用該函數,則會通過IMemoryService的getFileDescriptor接口來獲得MemoryService中的匿名共享內存文件的描述符,然后用這個文件描述符來創建一個MemoryFile實例,並保存在類成員變量memoryFile中,以后再調用這個函數時,就可以直接返回memoryFile了。

        

有了memoryService和memoryFile后,我們就可以在Client端訪問Server端創建的匿名共享內存了。點擊Read按鈕時,就通過memoryFile的readBytes接口把共享內存中的整數讀出來,並顯示在文本框中;點擊Write按鈕時,就通過memoryService這個代理類的setVal接口來調用MemoryService的本地實現類的setVal服務,從而把文本框中的數值寫到Server端創建的匿名共享內存中去;點擊Clear按鈕時,就會清空文本框的內容。這樣,我們就可以通過Read和Write按鈕來驗證我們是否在Client和Server兩個進程中實現內存共享了。

       

現在,我們再來看看Client界面的配置文件,它定義在res/layout/main.xml文件中:

[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="vertical"  
  4.     android:layout_width="fill_parent"  
  5.     android:layout_height="fill_parent"  
  6.     >  
  7.     <LinearLayout  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:orientation="vertical"   
  11.         android:gravity="center">  
  12.         <TextView   
  13.             android:layout_width="wrap_content"  
  14.             android:layout_height="wrap_content"   
  15.             android:text="@string/value">  
  16.         </TextView>  
  17.         <EditText   
  18.             android:layout_width="fill_parent"  
  19.             android:layout_height="wrap_content"   
  20.             android:id="@+id/edit_value"  
  21.             android:hint="@string/hint">  
  22.         </EditText>  
  23.     </LinearLayout>  
  24.      <LinearLayout  
  25.         android:layout_width="fill_parent"  
  26.         android:layout_height="wrap_content"  
  27.         android:orientation="horizontal"   
  28.         android:gravity="center">  
  29.         <Button   
  30.             android:id="@+id/button_read"  
  31.             android:layout_width="wrap_content"  
  32.             android:layout_height="wrap_content"  
  33.             android:text="@string/read">  
  34.         </Button>  
  35.         <Button   
  36.             android:id="@+id/button_write"  
  37.             android:layout_width="wrap_content"  
  38.             android:layout_height="wrap_content"  
  39.             android:text="@string/write">  
  40.         </Button>  
  41.         <Button   
  42.             android:id="@+id/button_clear"  
  43.             android:layout_width="wrap_content"  
  44.             android:layout_height="wrap_content"  
  45.             android:text="@string/clear">  
  46.         </Button>  
  47.     </LinearLayout>  
  48. </LinearLayout>          
相關的字符串定義在res/values/strings.xml文件中:

[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <string name="app_name">Ashmem</string>  
  4.     <string name="value">Value</string>  
  5.     <string name="hint">Please input a value...</string>  
  6.     <string name="read">Read</string>  
  7.     <string name="write">Write</string>  
  8.     <string name="clear">Clear</string>  
  9. </resources>  

這樣,界面的相關配置文件就介紹完了。

我們還要再來看程序描述文件AndroidManifest.xml的相關配置,它位於Ashmem目錄下:

[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.       package="shy.luo.ashmem"  
  4.       android:sharedUserId="android.uid.system"  
  5.       android:versionCode="1"  
  6.       android:versionName="1.0">  
  7.     <application android:icon="@drawable/icon" android:label="@string/app_name">  
  8.         <activity android:name=".Client"  
  9.                   android:label="@string/app_name">  
  10.             <intent-filter>  
  11.                 <action android:name="android.intent.action.MAIN" />  
  12.                 <category android:name="android.intent.category.LAUNCHER" />  
  13.             </intent-filter>  
  14.         </activity>  
  15.         <service   
  16.             android:enabled="true"   
  17.             android:name=".Server"  
  18.             android:process=".Server" >  
  19.             <intent-filter>  
  20.                 <action android:name="shy.luo.ashmem.server"/>  
  21.                 <category android:name="android.intent.category.DEFAULT"/>  
  22.             </intent-filter>  
  23.         </service>  
  24.     </application>  
  25. </manifest>  
這里我們可以看到,下面的配置項把服務名稱"shy.luo.ashmem.server"和本地服務類Server關聯了起來:

[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1.    <service   
  2. android:enabled="true"   
  3. android:name=".Server"  
  4. android:process=".Server" >  
  5. <intent-filter>  
  6.             <action android:name="shy.luo.ashmem.server"/>  
  7.             <category android:name="android.intent.category.DEFAULT"/>  
  8.        </intent-filter>  
  9.    </service>  
這樣,我們就可以通過startService(new Intent("shy.luo.ashmem.server"))來啟動這個Server了。不過,在Android中,啟動服務是需要權限的,所以,下面這一行配置獲取了啟動服務需要的相應權限:

[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. android:sharedUserId="android.uid.system"  
最后,我們來看工程的編譯腳本文件Android.mk,它位於Ashmem目錄下:
[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. LOCAL_PATH:= $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. LOCAL_MODULE_TAGS :optional  
  5.   
  6. LOCAL_SRC_FILES += $(call all-subdir-java-files)  
  7.   
  8. LOCAL_PACKAGE_NAME :Ashmem  
  9.   
  10. LOCAL_CERTIFICATE :platform  
  11.   
  12. include $(BUILD_PACKAGE)  
  這里又有一個關鍵的地方:

[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. LOCAL_CERTIFICATE :platform  

因為我們需要在程序中啟動Service,所以要配置這一行,並且要把源代碼工程放在Android源代碼平台中進行編譯。

這樣,整個例子的源代碼實現就介紹完了,接下來就要編譯了。有關如何單獨編譯Android源代碼工程的模塊,以及如何打包system.img,請參考如何單獨編譯Android源代碼中的模塊一文。

執行以下命令進行編譯和打包:

[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Ashmem  
  2. USER-NAME@MACHINE-NAME:~/Android$ make snod  
這樣,打包好的Android系統鏡像文件system.img就包含我們前面創建的Ashmem應用程序了。

再接下來,就是運行模擬器來運行我們的例子了。關於如何在Android源代碼工程中運行模擬器,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文。       

執行以下命令啟動模擬器:

[html]   view plain  copy
  在CODE上查看代碼片 派生到我的代碼片
  1. USER-NAME@MACHINE-NAME:~/Android$ emulator         
模擬器啟動起,就可以在Home Screen上看到Ashmem應用程序圖標了:

 

 點擊Ashmem圖標,啟動Ashmem應用程序,界面如下:


這樣,我們就可以驗證程序的功能了,看看是否實現了在兩個進程中通過使用Android系統的匿名共享內存機制來共享內存數據的功能。

        

通過這個例子的學習,相信讀者對Android系統匿名共享內存子系統Ashmem有了一個大概的認識,但是,這種認識還是停留在表面上。我們在文章開始時就提到,Android系統匿名共享內存子系統Ashmem兩個特點,一是能夠輔助內存管理系統來有效地管理不再使用的內存塊,二是它通過Binder進程間通信機制來實現進程間的內存共享。第二個特點我們在上面這個例子中看到了,但是似乎還不夠深入,我們知道,在Linux系統中,文件描述符其實就是一個整數,它是用來索引進程保存在內核空間的打開文件數據結構的,而且,這個文件描述符只是在進程內有效,也就是說,在不同的進程中,相同的文件描述符的值,代表的可能是不同的打開文件,既然是這樣,把Server進程中的文件描述符傳給Client進程,似乎就沒有用了,但是不用擔心,在傳輸過程中,Binder驅動程序會幫我們處理好一切,保證Client進程拿到的文件描述符是在本進程中有效的,並且它指向就是Server進程創建的匿名共享內存文件。至於第一個特點,我們也准備在后續學習Android系統匿名共享內存子系統Ashmem時,再詳細介紹。

 









免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM