原文: https://blog.csdn.net/luoshengyang/article/details/6666491
包名: com.ashmem.client
已知问题:
1. MemoryFile没有获取FileDescriptor的方法, 只能反射调取
2. 没有办法通过fd去创建MemoryFile, 只能通过String name的方式创建, 用FileInputStream取代
3. 第一次setVal读回来是对的, 第二次setVal读回来的不正确.
aidl文件, Client/app/src/main/aidl/com/ashmem/client/IMemoryService.aidl:
// IMemoryService.aidl
package com.ashmem.client;
// Declare any non-default types here with import statements
interface IMemoryService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void setValue(int val);
ParcelFileDescriptor getFileDescriptor();
}
生成的IMemoryService.java, Client/app/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out/com/ashmem/client/IMemoryService.java:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.ashmem.client;
// Declare any non-default types here with import statements
public interface IMemoryService extends android.os.IInterface
{
/** Default implementation for IMemoryService. */
public static class Default implements com.ashmem.client.IMemoryService
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
}
@Override public void setValue(int val) throws android.os.RemoteException
{
}
@Override public android.os.ParcelFileDescriptor getFileDescriptor() throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ashmem.client.IMemoryService
{
private static final java.lang.String DESCRIPTOR = "com.ashmem.client.IMemoryService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ashmem.client.IMemoryService interface,
* generating a proxy if needed.
*/
public static com.ashmem.client.IMemoryService asInterface(android.os.IBinder obj)
{
...
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
...
}
private static class Proxy implements com.ashmem.client.IMemoryService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
...
}
@Override public void setValue(int val) throws android.os.RemoteException
{
...
}
@Override public android.os.ParcelFileDescriptor getFileDescriptor() throws android.os.RemoteException
{
...
}
public static com.ashmem.client.IMemoryService sDefaultImpl;
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getFileDescriptor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
public static boolean setDefaultImpl(com.ashmem.client.IMemoryService impl) {
...
}
public static com.ashmem.client.IMemoryService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public void setValue(int val) throws android.os.RemoteException;
public android.os.ParcelFileDescriptor getFileDescriptor() throws android.os.RemoteException;
}
实现的Service端, Client/app/src/main/java/com/ashmem/client/MemoryService.java:
package com.ashmem.client;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MemoryService extends IMemoryService.Stub {
private final static String LOG_TAG = "MemoryService";
private MemoryFile memoryFile = null;
static int value = 0;
public MemoryService() {
value = -1;
try {
memoryFile = new MemoryFile("Ashmem", 4);
setValue(0);
} catch (IOException e) {
Log.i(LOG_TAG, "Failed to create memory file.");
e.printStackTrace();
}
}
@Override
public void setValue(int val) {
if(memoryFile == null)
return;
byte[] buffer = new byte[4];
buffer[0] = (byte) ((val >>> 24) & 0xFF);
buffer[1] = (byte) ((val >>> 16) & 0xFF);
buffer[2] = (byte) ((val >>> 8) & 0xFF);
buffer[3] = (byte) (val & 0xFF);
try {
Log.i(LOG_TAG, "Set value " + val + " to memory file. ");
memoryFile.writeBytes(buffer, 0, 0, 4);
} catch (IOException e) {
Log.i(LOG_TAG, "Failed to write bytes to memory file.");
e.printStackTrace();
}
}
@Override
public ParcelFileDescriptor getFileDescriptor() {
Log.i(LOG_TAG, "Get File Descriptor.");
FileDescriptor fileDescriptor = null;
ParcelFileDescriptor dup = null;
Method method = null;
try {
method = MemoryFile.class.getDeclaredMethod("getFileDescriptor");
} catch (NoSuchMethodException ex) {
ex.printStackTrace();
}
try {
fileDescriptor = (FileDescriptor)method.invoke(memoryFile);
}catch (IllegalAccessException ea) {
} catch (InvocationTargetException ei) {
}
try {
dup = ParcelFileDescriptor.dup(fileDescriptor);
} catch (IOException e) {
e.printStackTrace();
}
return dup;
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
}
实现的Client端, Client/app/src/main/java/com/ashmem/client/MainActivity.java:
package com.ashmem.client;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
public class MainActivity extends AppCompatActivity {
private final static String LOG_TAG = "MainActivity";
private IMemoryService memoryService = null;
private FileInputStream fileInputStream = null;
private ByteBuffer mMapping;
private int val;
Button readButton = null;
Button writeButton = null;
EditText editText = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent("com.ashmem.client.server");
intent.setPackage("com.ashmem.client");
bindService(intent, new MyConnection(), BIND_AUTO_CREATE);
readButton = findViewById(R.id.read);
writeButton = findViewById(R.id.write);
editText = findViewById(R.id.edit);
editText.setText("123");
readButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(null != memoryService) {
if(null == fileInputStream) {
getInputStream();
if(null == fileInputStream) {
return;
}
}
if(null != fileInputStream) {
try {
byte[] buffer = new byte[4];
fileInputStream.read(buffer, 0, 4);
fileInputStream.close();
fileInputStream = null;
int val = (buffer[0] << 24) | ((buffer[1] & 0xFF) << 16) | ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF);
Log.i(LOG_TAG, "Read: " + Integer.toString(val));
editText.setText(Integer.toString(val));
} catch (IOException ex) {
ex.printStackTrace();
}
} else {
Log.e(LOG_TAG, "fileInputStream is null");
}
}
}
});
writeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(null != memoryService) {
try {
int vall = Integer.parseInt(editText.getText().toString());
Log.i(LOG_TAG, "Set va;: " + vall);
memoryService.setValue(vall);
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
}
});
}
private void getInputStream() {
if(null == memoryService) {
return;
}
try {
ParcelFileDescriptor parcelFileDescriptor = memoryService.getFileDescriptor();
if(null == parcelFileDescriptor) {
return ;
}
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
if(null == fileDescriptor) {
return ;
}
fileInputStream = new FileInputStream(fileDescriptor);
} catch (RemoteException er) {
er.printStackTrace();
}
}
class MyConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(LOG_TAG, "onServiceConnected");
memoryService = IMemoryService.Stub.asInterface(service);
getInputStream();
}
@Override
public void onServiceDisconnected(ComponentName name) {
memoryService = null;
}
}
}
AndroidManifest文件, Client/app/src/main/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ashmem.client">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".Server"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.ashmem.client.server" />
</intent-filter>
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Layout文件, Client/app/src/main/res/layout/activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="value"
android:id="@+id/view1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edit"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/read"
android:text="Read"
android:orientation="horizontal"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/edit"
app:layout_constraintRight_toLeftOf="@id/write"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/write"
android:orientation="horizontal"
android:text="Write"
app:layout_constraintTop_toBottomOf="@id/edit"
app:layout_constraintLeft_toRightOf="@id/read"
app:layout_constraintRight_toRightOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
build.gradle文件:
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.ashmem.client"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
顶层build.gradle文件:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}