Создайте FileDescriptor в памяти

API FileDescriptor в Android говорит:

Экземпляры класса дескриптора файла служат непрозрачным дескриптором базовой машинно-зависимой структуры, представляющей открытый файл, открытый сокет или другой источник или приемник байтов.

Я хочу создать объект FileDescriptor, используя ByteArrayOutputStream и ByteArrayInputStream

Кроме того, FileDescriptor является конечным классом и не может быть переопределен. Единственный конструктор говорит -

Создает (недопустимый) объект FileDescriptor.

Есть идеи, как использовать FileDescriptor в Android?

РЕДАКТИРОВАТЬ

Я хочу использовать его в MediaMuxer. Вместо записи в файл я хочу сохранить данные мультимедиа в памяти и скопировать их в сокет TCP для прямой трансляции. Так что мой FileDescriptor должен быть "приемником байтов".

3 ответа

Использовать LocalSocket, но будьте осторожны, поскольку это может сказаться на безопасности.

LocalSocketAndroid API для работы с сокетами домена Unix. Доменные сокеты похожи на сокеты TCP/IP, за исключением того, что они существуют только на устройстве, их нельзя использовать для связи по сети.

Вот простое, но небезопасное решение вашей проблемы:

// Create a unique name for the socket.
String name = "your.package.name-" + UUID.randomUUID();

// Bind a server to the socket.
final LocalServerSocket server = new LocalServerSocket(name);

// Connect a client to the socket.
LocalSocket client = new LocalSocket(LocalSocket.SOCKET_STREAM);
client.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.ABSTRACT));

// Start a thread to read from the server socket.
new Thread(new Runnable {
    @Override
    public void run() {
        LocalSocket socket = server.accept();

        // To read data sent by the client, read from socket.getInputStream().
        // To send data to the client, write to socket.getOutputStream().

        // After you are done you will need to call socket.close().
    }
}).start();

// Get the FileDescriptor associated with the client.
// You can use this FileDescriptor to write data to and/or read data
// sent from the server.
FileDescriptor fileDescriptor = client.getFileDescriptor();

// After you are done you will need to call server.close() and client.close().

Это создает сокет в абстрактном пространстве имен сокетов. Это небезопасно, потому что сокеты домена являются общесистемными, а сокеты в абстрактном пространстве имен не ограничены какой-либо системой разрешений. Единственное требование для подключения или привязки к имени в абстрактном пространстве имен - это знать имя, а имя сокета, используемое вашим приложением, может быть легко обнаружено злоумышленником посредством декомпиляции. Также существует вероятность того, что другое приложение может случайно использовать то же имя сокета. Таким образом, другое приложение может перехватить ваши данные или отправить неожиданные данные через сокет, и от этого трудно защититься.

Лучшее, но более сложное решение - создать сокет в пространстве имен файловой системы. API для этого очень странный, но этого можно добиться следующим образом:

// Create a unique name for the socket in your app's private data area.
// Note this example creates a file named like socket-xxxx in the root of
// your app's private data area. You might want to put it in a subdirectory.
String name = context
    .getFileStreamPath("socket-" + UUID.randomUUID())
    .getAbsolutePath();
LocalSocketAddress address = new LocalSocketAddress(name, LocalSocketAddress.Namespace.FILESYSTEM);

// Bind a server to the socket.
LocalSocket server = new LocalSocket(LocalSocket.SOCKET_STREAM);
server.bind(address);
final LocalServerSocket serverWrapper = new LocalServerSocket(server.getFileDescriptor());

// Connect a client to the socket.
LocalSocket client = new LocalSocket(LocalSocket.SOCKET_STREAM);
client.connect(address);

// Start a thread to read from the server socket.
new Thread(new Runnable {
    @Override
    public void run() {
        LocalSocket socket = serverWrapper.accept();

        // To read data sent by the client, read from socket.getInputStream().
        // To send data to the client, write to socket.getOutputStream().

        // After you are done you will need to call socket.close() and
        // serverWrapper.close().
    }
}).start();

// Get the FileDescriptor associated with the client.
// You can use this FileDescriptor to write data to and/or read data
// sent from the server.
FileDescriptor fileDescriptor = client.getFileDescriptor();

// After you are done you will need to call server.close() and client.close().

Это создает файл в файловой системе, но никакие данные, проходящие через сокет, никогда не записываются на диск, они полностью находятся в памяти. Файл - это просто имя, представляющее сокет, аналогично файлам в/devкоторые представляют устройства. Поскольку доступ к сокету осуществляется через файловую систему, он подчиняется обычным разрешениям файловой системы, поэтому легко ограничить доступ к сокету, поместив сокет в область личных данных вашего приложения.

Поскольку этот метод создает файл в файловой системе, было бы неплохо удалить файл после того, как вы закончите, а также, возможно, время от времени проверять и очищать старые сокеты, на случай, если ваше приложение выйдет из строя и оставит старые файлы. валяется.

Начиная с Android R(30), вы можете использовать MediaMuxer, и он работает!

https://developer.android.com/reference/android/system/Os#memfd_create(java.lang.String,%20int)

      private FileDescriptor getFileDescriptor(Context context) throws IOException, ErrnoException{
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        return Os.memfd_create("thumb", 0);
    } else {
        return new FileOutputStream(new File(context.getCacheDir(), "thumb.heif")).getFD();
    }
}
private void mux() {
    MediaMuxer mediaMuxer = new MediaMuxer(getFileDescriptor(context), MediaMuxer.OutputFormat.MUXER_OUTPUT_HEIF);
    // Muxing code omitted
    mediaMuxer.stop();
    StructStat structStat = Os.fstat(fileDescriptor);
    bytes = new byte[(int)structStat.st_size];
    Os.lseek(fileDescriptor, 0, OsConstants.SEEK_SET);
    Os.read(fileDescriptor, bytes, 0, bytes.length);
    mediaMuxer.release();

К вашему сведению: теоретически вы могли бы использовать Oreo, используя:

https://developer.android.com/reference/android/os/storage/StorageManager#openProxyFileDescriptor(int,%20android.os.ProxyFileDescriptorCallback,%20android.os.Handler)

По какой-то причине MediaMuxer разбивает куски FileDescriptor, созданного таким образом. Я использовал его в других местах, и он работал нормально.

Вы можете перенаправить System.out в ByteArrayOutputStream и использовать FileDescriptor.out в качестве filedescriptor

ByteArrayOutputStream buff = new ByteArrayOutputStream();
PrintStream old = System.out;
System.setOut(new PrintStream(buff));

... use FileDescriptor.out ...

System.setOut(old);
System.out.println(new String(buff.toByteArray()));
Другие вопросы по тегам