Android调用C++实现共享内存(Native层)

Android下匿名共享内存java层接口利用MemoryFile实现进程间内存共享;利用MemoryFile可以参考这篇文章:https://blog.csdn.net/qq_24451593/article/details/80514566

MemoryFile是java层封装的接口,它实现共享内存主要调用了如下函数:

int fd = open("/dev/ashmem",O_RDWR);
ioctl(fd, ASHMEM_SET_NAME,name);
ioctl(fd,ASHMEM_SET_SIZE, size);
//addr为共享内存地址
addr = (char*)mmap(NULL, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

创建出来的fd转换成ParcelFileDescriptor通过AIDL传递给另一个进程;另一个进程将ParcelFileDescriptor转换成该进程对应fd,调用mmap来使用该共享内存。
注:进程A的fd与进程B的fd是不一样的,但是都对应同一文件
Android调用C++实现共享内存(Native层)_第1张图片

既然知道了MemoryFile实际调用函数,那么我们就可以在native层用C++代码实现共享内存。

(1)C++代码实现

android_shm.cpp

#include "android_shm.h"

#define ASHMEM_DEVICE  "/dev/ashmem"

//ret= 0 创建成功;ret=-1,失败;
//注:ret =1,共享内存已经存在,但是目前这个没用,暂时放这
int create_shared_memory(const char* name, U64 size, int node, char*& addr, U64& shm_id){
    U64 fd = open(ASHMEM_DEVICE, O_RDWR);
    if(fd < 0){
        return -1;
    }
    U64 len = ioctl(fd, ASHMEM_GET_SIZE, NULL);
    if(len > 0){             
        addr = (char*)mmap(NULL, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
   shm_id = fd;
   return 1;
    }else{
        int ret = ioctl(fd, ASHMEM_SET_NAME,name);
        if(ret < 0){
       close(fd);
            return -1;
        }
        ret = ioctl(fd,ASHMEM_SET_SIZE, size);
        if(ret < 0){
       close(fd);
            return -1;
        }
        addr = (char*)mmap(NULL, size , PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        shm_id = fd;
    }
    return 0;
}

int open_shared_memory(const char* name, int node, char*& addr, U64& shm_id){
    U64 size = ioctl(shm_id, ASHMEM_GET_SIZE,NULL);
    if(size > 0){ 
        addr = (char*)mmap(NULL, size , PROT_READ | PROT_WRITE, MAP_SHARED, shm_id, 0);
    }else{
        return -1;     
    }
    return 0;
}

int close_shared_memory(U64& shm_id, char*& addr){
    U64 size = ioctl(shm_id, ASHMEM_GET_SIZE, NULL);
    if(size <0){
        return -1;
    }  
    int ret = munmap((void*)addr, size);
    if(ret == -1){
        return -1;
    }
    ret = close(shm_id);
    if(ret == -1){
        return -1;
    }
    return 0;
}

android_shm.h

#ifndef _SYS_SHM_H
#define _SYS_SHM_H 1
#include 
#define SHM_HUGETLB    04000

#include 
#include 
#include 
#include 
#include 

/* Get common definition of System V style IPC.  */
#include 

/* Get system dependent definition of `struct shmid_ds' and more.  */
#include 

typedef unsigned long long U64;
__BEGIN_DECLS


extern int create_shared_memory(const char* name, U64 size, int node, char*& addr, U64& shm_id);

extern int open_shared_memory(const char* name, int node, char*& addr, U64& shm_id);

extern int close_shared_memory(U64& shm_id, char*& addr);


__END_DECLS

#endif /* sys/shm.h */

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := libandroid_shm
LOCAL_SRC_FILES := android_shm.cpp

include $(BUILD_SHARED_LIBRARY)

(2)ndk-build编译C++

参考我另一篇博客:https://blog.csdn.net/liny000/article/details/83020530

(3)将进程A的fd传给进程B

利用binder跨进程通信
1)服务类实现binder onTransact函数(两个进程传递ParcelFileDescriptor)
MainService.java

public class MainService extends Service {

    ParcelFileDescriptor pfd;

    @Override
    public IBinder onBind(Intent arg0) {
        return new MyBinder();
    }


    class MyBinder extends Binder {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case 0:
                    //pfd = data.readParcelable(null);
                    // 或者
                    pfd = data.readFileDescriptor();
                    break;
                case 1:
                    //reply.writeParcelable(pfd,0);
                    // 或者
                    reply.writeFileDescriptor(pfd.getFileDescriptor());
                    break;

                default:
                    break;
            }
            return true;
        }
    }

}

2)MainActivity 调用C++代码创建共享内存并写入内容,得到fd,转换为ParcelFileDescriptor 传递给MainActivity2
MainActivity.java

package com.example.linyuan.shared_memory;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.io.FileDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    Binder mBinder;
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mBinder = (Binder) service;
        }

        public void onServiceDisconnected(ComponentName className) {
            mBinder = null;
        }
    };


    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        mConnection = null;
        super.onDestroy();
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent it = new Intent(MainActivity.this, MainService.class);
        startService(it);
        bindService(it, mConnection, Context.BIND_AUTO_CREATE);

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        Button button1 = (Button)findViewById(R.id.button1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this,MainActivity2.class));
            }
        });
    }

    android.os.Parcel data = android.os.Parcel.obtain();
    android.os.Parcel reply = android.os.Parcel.obtain();
    public void write(View v){
        try {
            if(mBinder!=null){
                //  data.writeParcelable(pfd, 0);
                // 或者
                int fd = fdFromJNI();
                TextView tv = (TextView) findViewById(R.id.sample_text);
                tv.setText("fd:"+ new String(String.valueOf(fd)));
                ParcelFileDescriptor cfd = ParcelFileDescriptor.fromFd(fd);
                FileDescriptor fileDescriptor2 = cfd.getFileDescriptor();
                data.writeFileDescriptor(fileDescriptor2);
                mBinder.transact(0, data, reply, 0);
            }
        } catch (Exception e) {
            e.printStackTrace();
//            Toast.makeText(this, "写失败", 0).show();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native int fdFromJNI();
}


3)获取ParcelFileDescriptor,转换为fd,读取共享内存上内容
MainActivity2.java

public class MainActivity2 extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
    }

    private byte[] buffer = new byte[20];

    IBinder mBinder;
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // 非服务创建进程,获取的Binder只是一个代理Binder对象,不能直接转换
            // mBinder = (Binder) service;
            mBinder = service;
        }

        public void onServiceDisconnected(ComponentName className) {
            mBinder = null;
        }
    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_2);

        Intent it = new Intent(MainActivity2.this,MainService.class);
        startService(it);
        bindService(it, mConnection, Context.BIND_AUTO_CREATE);
    }

    public void read(View v) {
        TextView tv = (TextView) findViewById(R.id.tv);
        try {
            if (mBinder != null) {
                android.os.Parcel data = android.os.Parcel.obtain();
                android.os.Parcel reply = android.os.Parcel.obtain();
                mBinder.transact(1, data, reply, 0);
                //ParcelFileDescriptor pfd = reply.readParcelable(null);
                // 或者
                ParcelFileDescriptor pfd = reply.readFileDescriptor();
                if(pfd==null){
                    buffer = "ParcelFileDescriptor 空指针".getBytes();
                    tv.setText(new String(buffer));
                    return;
                }
                int fd = pfd.getFd();
                String buf;
                buf = read(fd);
                tv.setText(new String(buf));
            }

        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        mConnection = null;
        super.onDestroy();
    }

    public static   native String read(int fd);
}

native-lib.cpp 调用C++代码

#include 
#include 
#include "android_shm.h"

extern "C" JNIEXPORT jint JNICALL
Java_com_example_linyuan_shared_1memory_MainActivity_fdFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    char* buf;
    U64 ufd = 0;
    int ret = create_shared_memory("test1",1024,-1,buf,ufd);
    strcpy(buf,"shared_test你好");
    return ufd;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_linyuan_shared_1memory_MainActivity2_read(
        JNIEnv *env,
        jobject /* this */,jint fd) {
    std::string hello = "进程2 ";
    U64  ufd = (U64)fd;
    char* buf;
    open_shared_memory("test1",-1,buf,ufd);
    char c[20];
    sprintf(c,"%D",ufd);
    hello = hello +buf +"    fd:" +c;
    close_shared_memory(ufd,buf);
    return  env->NewStringUTF(hello.c_str());
}

需要在AndroidManifest.xml里添加如下,android:process=":activity_2"是确保另一个进程读取共享内存


    
        

        
    


    
        
    

整个工程文件(Android studio)下载:https://download.csdn.net/download/liny000/10763992

你可能感兴趣的:(Android)