Android开发--获取其他应用分享的文件并通过网络发送

  计划做一个在局域网下共享文件和消息的应用,android端的开发需要读取其他应用分享的文件并发送,本文描述了该功能的实现过程以及一些测试。

约定

  1. 未声明的变量为类变量
  2. 未指明所属类的方法在MainActivity类中

权限

我们需要实现的功能只需要申请网络通信的权限即可

 <uses-permission android:name="android.permission.INTERNET"/>

需要用到的主要Java类

类名 描述
Socket 建立tcp连接
ContentResolver 解析Uri对象获取共享文件的读取流(InputStream)
ParcelFileDescriptor 对FileDescriptor的封装,可以在进程间传递
FileDescriptor 文件描述符,用于读取和写入文件
Uri 通一资源标志符(Uniform Resource Identifier, URI), 在共享文件时表示目标文件

接收其他应用分享的文件(intent-filter配置)

为了接收其他应用分享的文件,我们需要在用于处理接收文件的Activity中加入以下intent-filter(为了简便,我这里直接在MainActivity里写)

 <activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    intent-filter>

    
        <action android:name="android.intent.action.SEND" />
        
        <category android:name="android.intent.category.DEFAULT"/>
        
        <data android:mimeType="*/*"/>
    intent-filter>
activity>

官方关于category的描述如下:

一个包含应处理 Intent 组件类型的附加信息的字符串。您可以将任意数量的类别描述放入一个 Intent 中,但大多数 Intent 均不需要类别。

从intent中读取分享的文件

有两种方式读取文件,第一种方式比较简单,推荐使用

方式一

public read_test1() {
    Uri data_uri;
    Intent intent = getIntent();
    if (intent == null) {
        Log.d(TAG, "sendFile: no data to send");
        return;
    }
    
    data_uri = intent.getParcelableExtra(intent.EXTRA_STREAM);
    if (data_uri == null) {
        Log.d(TAG, "sendFile: no data in intent");
        return;
    }
    // 4k缓存区
    byte[] buf = new byte[4096];
    ContentResolver resolver = getContentResolver();
    // 使用ContentResolver的openInputStream方法直接打开文件
    InputStream read_stream = resolver.openInputStream(final_data_url);
    
    while (true) {
        int len = read_stream.read(buf);
        if (len <= 0) {
            break;
        }
        /*
        打印数据的代码...
        */
    }
}

方式二

public read_test1() {
    Uri data_uri;
    Intent intent = getIntent();
    if (intent == null) {
        Log.d(TAG, "sendFile: no data to send");
        return;
    }
    
    data_uri = intent.getParcelableExtra(intent.EXTRA_STREAM);
    if (data_uri == null) {
        Log.d(TAG, "sendFile: no data in intent");
        return;
    }
    
    // 4k缓存区
    byte[] buf = new byte[4096];
    ContentResolver resolver = getContentResolver();
    // 使用ContentResolver的openFileDescriptor方法获取ParcelFileDescriptor对象
    ParcelFileDescriptor fd= resolver.openFileDescriptor(data_uri, "r");
    // 使用ParcelFileDescriptor的getFileDescriptor方法获取FileDescriptor对象
    // 利用FileDescriptor对象建立文件输入流(FileInputStream)
    FileInputStream read = new FileInputStream(fd.getFileDescriptor());
    
    while (true) {
        int len = read_stream.read(buf);
        if (len <= 0) {
            break;
        }
        /*
        打印数据的代码...
        */
    }
}

建立tcp连接

// 建立DataTrans类来处理建立连接和发送数据的部分
public class DataTrans {
    Socket session_s;
    
    // 连接
    public void connect(String ip, int port) throws IOException {
        session_s = new Socket(ip, port);
    }
    
    //检查是否连接
    public boolean isConected() {
        boolean connected = (session_s == null)? false:session_s.isConnected();
        return connected;
    }
    
    // 发送数据
    public void send(byte[] data) throws IOException {
        OutputStream sending_stream = session_s.getOutputStream();
        sending_stream.write(data);
    }
    
    // 断开连接
    public void disconnect() {
        try {
            session_s.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    // 对象销毁时关闭连接
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        if(session_s != null) {
            session_s.close();
        }
    }
}

读取文件并通过网络发送

注意,由于Android系统不允许在主线程上进行网络传输操作,需要新建线程来发送数据

public void sendFile() {
    Uri data_uri;
    Intent intent = getIntent();
    if (intent == null) {
        Log.d(TAG, "sendFile: no data to send");
        return;
    }

    data_uri = intent.getParcelableExtra(intent.EXTRA_STREAM);
    if (data_uri == null) {
        Log.d(TAG, "sendFile: no data in intent");
        return;
    }
    
    // 传入内部类的局部变量必须是final,建立新线程时使用了匿名内部类
    final Uri final_data_url = data_uri;
    Thread sending_thread = new Thread(new Runnable() {
        // sending thread
        @Override
        public void run() {
            try {
                byte[] buf = new byte[4096];
                // 建立连接
                if (!trans.isconnected()) {
                    trans.connect("127.0.0.1", 19999);
                }
                InputStream read_stream = getContentResolver().openInputStream(final_data_url);
                while (true) {
                    int len = read_stream.read(buf);
                    if (len <= 0) {
                        break;
                    }
                    // 发送数据
                    trans.send(buf);
                }
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "sending: error send file");
            }
            Log.d(TAG, "sending: file sended");
        }
    });
    sending_thread.start();
}

测试

服务器端是一个python脚本,监听19999端口,并将接收的数据保存在received_file
需要使用adb开启反向端口转发,把模拟器里的19999端口转发到pc的19999端口

adb reverse tcp:19999 tcp:19999

app目前太简陋了,放张图表示一下吧
Android开发--获取其他应用分享的文件并通过网络发送_第1张图片
红色框框的那个图片就是从手机传到电脑的。

参考资料

intent

share files

你可能感兴趣的:(Android)