安卓如何实现读取.mtp格式的文档不乱码

802 阅读5分钟

前言:  绝大多数遇到逆境挣扎一番,不行就会颓废。只有有信念、有追求、有梦想的人才能有新的生命、新的开始,才有人格魅力让我们敬仰。

       最近在项目开发中,需要读取.mtp文件里面的格式,但是用正常的文本文件打开是乱码,当然也有专门的软件读出来不是乱码,而是一个个十六进制数据。

       比如用文本软件打开是这样子的:

     我们实际上想要这样子的:

       要实现的功能是这样子的,点击一个按钮访问手机内存,并且选择.mtp文档后返回并将数据解析出来。

       实现的效果是这样子的:

在我使用的mtp文档,是由别的软件生成的,用于下载都单片机中,而这样文档是可以通过安卓解析出来,把里面需要的代码通过安卓和USB_HID设备通信下进去的,因为每种文档里面的格式不一样,要获取到相应的数据可以跟硬件工程师那边去沟通,接下来就记录一下我是如何读取到.mtp文档中的数据并打印出来的。

目录

       一、创建MyFile类

       二、创建FileChooseUtil工具类

       三、实现

              3.1 跳转选择文件夹

              3.2 设置权限

              3.3 全部代码

        四、总结

一、创建MyFile类

        这个类用于读取文件,代码如下:

package com.example.readbin.MyFlie;

import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class MyFile {
    
    public static boolean createMyFile(File file, String name){
        if (!file.exists()) {
            file.mkdirs();
        }
        // 建立缓存json数据源文件夹,在没网络的情况下从这里读取数据
        File new_file = new File(file + "/" + name);
        if (!new_file.exists()) {
           
new_file.getParentFile().mkdirs();
            try {
                new_file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    public static String readMyFile(File file){
        String content = "";
        try {
            FileInputStream inputStream =
new FileInputStream(file);
            byte[] bytes = new byte[1024];
            ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
            while (inputStream.read(bytes) != -1) {
               
arrayOutputStream.write(bytes, 0, bytes.length);
            }
            inputStream.close();
            arrayOutputStream.close();
            content = new String(arrayOutputStream.toByteArray());
        }
        catch (Exception e) {
        }
        return content;
    }

    public static byte[] readFile(String filename) throws IOException {
        //打开文件
        File file = new File(filename);
        //如果path是传递过来的参数,可以做一个非目录的判断
        if (file.isDirectory())
        {
            Log.d("TAG","The File is directory.");
            return null;
        }
        else if(!file.exists())
        {
            Log.d("TAG","The File isn't exist.");
            return null;
        }
        else
        {
            try {
                FileInputStream fis = new FileInputStream(filename);
                byte[] b = new byte[fis.available()];
                fis.read(b);
                fis.close();
                return b;
            } catch (FileNotFoundException e) {
                throw(e);
            } catch (IOException e) {
                throw(e);
            }
        }
    }

    public static boolean WriteMyFile(File file,String content){
        try {
            FileOutputStream fos = new FileOutputStream(file);
            OutputStreamWriter osw = new OutputStreamWriter(fos);
            osw.write(content);
            osw.flush();
            osw.close();
            fos.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

}

二、创建FileChooseUtil工具类

       这个工具类中可以获取到选择文件的路径,后面可以根据这个路径找到文件并读取出来。代码如下:

package com.example.readbin.MyFlie;

import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.widget.Toast;

public class FileChooseUtil {

    private Context context;
    private static FileChooseUtil util = null;

    private FileChooseUtil(Context context) {
        this.context = context;
    }

    public static FileChooseUtil getInstance(Context context) {
        if (util == null) {
            util = new FileChooseUtil(context);
        }
        return util;
    }

    /**
     * 获取选择的文件路径
     * @param uri
     * @return
     */
    public String getChooseFileResultPath(Uri uri) {
        String chooseFilePath = null;
        if ("file".equalsIgnoreCase(uri.getScheme())) {//使用第三方应用打开
            chooseFilePath = uri.getPath();
            Toast.makeText(context, chooseFilePath, Toast.LENGTH_SHORT).show();
            return chooseFilePath;
        }
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {//4.4以后
            chooseFilePath = getPath(context, uri);
        } else {//4.4以下下系统调用方法
            chooseFilePath = getRealPathFromURI(uri);
        }
        return chooseFilePath;
    }

    /**
     * 4.4以下系统获取路径方法
     * @param contentUri
     * @return
     */
    private String getRealPathFromURI(Uri contentUri) {
        String res = null;
        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
        if (null != cursor && cursor.moveToFirst()) {
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            res = cursor.getString(column_index);
            cursor.close();
        }
        return res;
    }

    /**
     * 专为Android4.4设计的从Uri获取文件绝对路径,以前的方法已不好使
     */
    @SuppressLint("NewApi")
    private String getPath(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            if (isExternalStorageDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                if ("primary".equalsIgnoreCase(type)) {
                    return Environment.getExternalStorageDirectory() + "/" + split[1];

                }
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
                return getDataColumn(context, contentUri, null, null);

            }
            // MediaProvider
            else if (isMediaDocument(uri)) {
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];

                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;

                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;

                }

                final String selection = "_id=?";
                final String[] selectionArgs = new String[]{split[1]};

                return getDataColumn(context, contentUri, selection, selectionArgs);

            }

        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {
            return getDataColumn(context, uri, null, null);

        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            uri.getPath();
        }
        return null;
    }

    private String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {column};
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int column_index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(column_index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    private boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    private boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    private boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }
}

三、实现

3.1 跳转选择文件夹

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);

//显示文件管理器列表
try {
    startActivityForResult(Intent.createChooser(intent, "请选择一个要上传的文件"),READ_REQUEST_CODE);
} catch (android.content.ActivityNotFoundException ex) {
    Toast.makeText(MainActivity.this, "请安装文件管理器", Toast.LENGTH_SHORT).show();
}

3.2 设置权限

       在AndroidManifest.xml下添加以下权限:

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

       对于安卓10要在中额外添加以下权限:

android:requestLegacyExternalStorage="true"

3.3 全部代码

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView mTvReadBin;
    private Button mBtnSelectFile;

    final int READ_REQUEST_CODE = 1;

    String path = "file:///android_asset/USB.bin";

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTvReadBin = findViewById(R.id.tv_bin);
        mBtnSelectFile = findViewById(R.id.btn_select_file);
        mBtnSelectFile.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_select_file:
                findProgammer();
                break;
        }
    }

    private  void findProgammer(){
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.setType("*/*");
        intent.addCategory(Intent.CATEGORY_OPENABLE);

        //显示文件管理器列表
        try {
            startActivityForResult(Intent.createChooser(intent, "请选择一个要上传的文件"),READ_REQUEST_CODE);
        } catch (android.content.ActivityNotFoundException ex) {
            Toast.makeText(MainActivity.this, "请安装文件管理器", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
        super.onActivityResult(requestCode, resultCode,resultData);

        if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
            // The document selected by the user won't be returned in the intent.
            // Instead, a URI to that document will be contained in the return intent
            // provided to this method as a parameter.
            // Pull that URI using resultData.getData().
            if (resultData != null) {
                Uri uri = resultData.getData();
                String filepath = FileChooseUtil.getInstance(this).getChooseFileResultPath(uri);
                Log.d("TAG","路径为:" + filepath);
//                filepath = FileChooseUtil.getInstance(this).getChooseFileResultPath(uri);
//                Log.d("TAG",filepath);
                try{
                    byte[] binData = MyFile.readFile(filepath);
                    mTvReadBin.setText("" + Arrays.toString(binData));
                    Log.d("TAG","允许访问");
                }
                catch (IOException e){
                    Log.d("TAG",e.getMessage());
                }
            }
        }
    }

}

布局代码如下:

<LinearLayout 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"
    android:orientation="vertical">
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/btn_select_file"
        android:text="选择文件"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:id="@+id/sc_data"
        android:layout_below="@+id/btn_select_file"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/tv_bin"
            android:text="Hello World!"/>
    </ScrollView>
</LinearLayout>

四、总结

        以上就是我如何读取.mtp文件中数据的方法,要注意的是文件中数据是一个一个十六进制的byte,并且不会有负数,但是显示的时候被转换成十进制进行显示,java中是采用补码来表示二进制数,所以转换的时候会有负数。如果以上内容对朋友你有用,点个赞呗~,如果有写错的地方欢迎指出,虚心修改。