java - Android 在尝试读取文本或文档文件时没有响应

标签 java android inputstream filereader android-anr-dialog

这与 my previous question有关 在我更改 readFile 并使其从运行在 android 11 及更高版本中的设备的 URI 中读取后,我在尝试读取文件时遇到了 ANR 错误

gif 显示错误

这是我的完整代码

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_DOC = 1;

    private static final String TAG = "MainActivity";

    private ActivityMainBinding activityMainBinding = null;

    private File file;
    private Uri selectedFileURI;
    BufferedReader bufferedReader;
    InputStream inputStream;
    FileReader fileReader;

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater());

        setContentView(activityMainBinding.getRoot());


    }

    @Override
    protected void onStart() {
        super.onStart();
        activityMainBinding.textView.setMovementMethod(new ScrollingMovementMethod());
        activityMainBinding.browseButton.setOnClickListener(view -> {


            browseDocuments();
        });

        activityMainBinding.read.setOnClickListener(view -> {
            if (TextUtils.isEmpty(activityMainBinding.editTextPath.getText())) {
                activityMainBinding.editTextPath.setError("The file path cannot be empty");
            } else {
                readFile();

            }
        });

        activityMainBinding.clear.setOnClickListener(view -> activityMainBinding.textView.setText(null));
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_DOC && resultCode == Activity.RESULT_OK) {

            try {

                if (data != null) {

                    selectedFileURI = data.getData();
                    file = new File(selectedFileURI.getPath());
                    activityMainBinding.editTextPath.setText(file.getAbsolutePath());
                    Log.d(TAG, "onActivityResult: " + file.getAbsolutePath());

                } else {
                    Toast.makeText(this, "Allow permission for storage access!", Toast.LENGTH_SHORT).show();
                }

                String mimeType = getContentResolver().getType(selectedFileURI);
                Log.i("Type of file", mimeType + "");
            } catch (Exception exception) {

                if (exception.getMessage() != null) {

                    Log.e("test Exception", exception.getMessage());

                } else if (exception.getCause() != null) {
                    Log.e("test Exception", Objects.requireNonNull(exception.getCause()).toString());
                }


            }
        }

    }

    public String getPath(Uri uri) {
        String[] projection = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
        if (cursor == null) return null;
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        String s = cursor.getString(column_index);
        cursor.close();
        return s;
    }


    private void readFile() {
        try {

            StringBuilder sb = new StringBuilder();
            String line;

            if (SDK_INT >= Build.VERSION_CODES.R) {

                inputStream = getContentResolver().openInputStream(selectedFileURI);
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

            } else {
                fileReader = new FileReader(file);
                bufferedReader = new BufferedReader(fileReader);
            }
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line).append("\n");
            }

            activityMainBinding.textView.setText(sb.toString());

            if(inputStream != null) {
                inputStream.close();
            }else if(bufferedReader != null) {
                bufferedReader.close();
            }else if(fileReader != null) {
            fileReader.close();
            }

        } catch (IOException e) {
            Log.e("IOException", e.getMessage());
            Log.e("IOException2", e.getCause() + "");
            Log.e("IOException3", "exception", e);
            Toast.makeText(MainActivity.this, "Cannot read this file", Toast.LENGTH_LONG).show();

        }

    }


    private boolean checkPermission() {
        if (SDK_INT >= Build.VERSION_CODES.R) {
            return Environment.isExternalStorageManager();
        } else {
            int result = ContextCompat.checkSelfPermission(this, READ_EXTERNAL_STORAGE);
            int result1 = ContextCompat.checkSelfPermission(this, WRITE_EXTERNAL_STORAGE);
            return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
        }
    }

    private void requestPermission() {
        if (SDK_INT >= Build.VERSION_CODES.R) {
            try {
                Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
                intent.addCategory("android.intent.category.DEFAULT");
                intent.setData(Uri.parse(String.format("package:%s", getApplicationContext().getPackageName())));
                startActivityForResult(intent, 1);
            } catch (Exception e) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
                startActivityForResult(intent, 1);
            }
        } else {

            ActivityCompat.requestPermissions(this, new String[]{READ_EXTERNAL_STORAGE,
                    WRITE_EXTERNAL_STORAGE}, 1);
        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case REQUEST_CODE_DOC:
                if (grantResults.length > 0) {
                    boolean READ_EXTERNAL_STORAGE = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    boolean WRITE_EXTERNAL_STORAGE = grantResults[1] == PackageManager.PERMISSION_GRANTED;

                    if (READ_EXTERNAL_STORAGE && WRITE_EXTERNAL_STORAGE) {
                        readFile();


                    } else {
                        Toast.makeText(this, "Allow permission for storage access!", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
        }
    }

    private void browseDocuments() {

        if (!checkPermission()) {
            requestPermission();
        } else {


            String[] mimeTypes =
                    {"text/plain", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                            "application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation",
                            "application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                            "textView/plain",
                            "application/pdf"};

            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            setResult(Activity.RESULT_OK);

            intent.setType("*/*");
            intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);


            startActivityForResult(Intent.createChooser(intent, "ChooseFile"), REQUEST_CODE_DOC);
        }
    }

}

最佳答案

那是因为你在主 UI 线程上读取文件会阻塞它并导致 ANR 直到完成,你需要在后台线程上完成这项工作,我建议看看 this question 上的答案。即使它已经“大约 10 年了”但它是开始尝试的好地方,您会发现一些方法可以在后台线程中执行该过程,例如 AsyncTaskCallbacksExecutors 所有这些方法都可以帮助您解决问题,但我将在我的回答中重点关注最新和推荐的方法是使用 "RX Java"我建议看看这个 RxJava Tutorial 您将了解更多。

让我们开始解决这个问题

  1. 将 Rx Java 依赖项添加到您的项目 在 build.gradle(app) 中
  implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    // Because RxAndroid releases are few and far between, it is recommended you also
    // explicitly depend on RxJava's latest version for bug fixes and new features.
    // (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version)
    implementation 'io.reactivex.rxjava3:rxjava:3.0.3'
  1. 在阅读按钮 onClick 中创建一个新的 Observable 并在其上调用方法 readFilesubscribeOn 它定义了 observable 将在其上工作的线程 我选择了 Schedulers.computation() 因为您将无法确定文档/文本文件的大小或者这个过程需要多长时间,但是你可以选择其他线程,比如 Schedulers.io(),在 observeOn 你添加了观察者将在它上面工作的线程,在你的案例,它是主线程,最后调用 subscribe 来连接 observable 和观察者,我还建议在你的布局上添加 progressBar 以在读取文件时显示它并在完成过程时隐藏它<
activityMainBinding.read.setOnClickListener(view -> {
            if (TextUtils.isEmpty(activityMainBinding.editTextPath.getText())) {
                activityMainBinding.editTextPath.setError("The file path cannot be empty");
            } else {
                

                Observable.fromCallable(this::readFile)
                        .subscribeOn(Schedulers.computation())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new Observer<Object>() {
                            @Override public void onSubscribe(Disposable d) {
                                activityMainBinding.progressBar.setVisibility(View.VISIBLE);
                            }

                            @Override public void onNext(Object o) {
                                if(o instanceof StringBuilder){
                                    activityMainBinding.textView.setText((StringBuilder) o);
                                }
                            }

                            @Override public void onError(Throwable e) {

                            }

                            @Override public void onComplete() {
                                activityMainBinding.progressBar.setVisibility(View.INVISIBLE);
                            }
                        });

            }
        });

此外,如果文件是 PDF,我建议您使用 AndroidPdfViewer 库它让你更容易,你不需要所有这些权限来阅读 PDF 文件,你可以检查这些 this article了解更多信息。

关于java - Android 在尝试读取文本或文档文件时没有响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74013658/

相关文章:

java - 基于注解的 ServiceLocatorFactoryBean?

用于限制返回对象的可用方法的 Java 设计模式

android - 从android中的运动事件获取所有x和y坐标

android - 将联系人信息加载到 ListView 时如何避免重复的联系人姓名(数据)?

java - 从输入流中获取文件名 (Java)

java - 在Java中打开URL获取内容

java - 如何在搜索 javadoc 时获取最新的搜索结果

java - 使用 java 从 dir 命令中删除卷信息行

android - 对话框 View 必须包含一个 id 为@android :id/edit 的 EditText

java - Java 中文件结尾和字节值 -1 之间的区别?