Почему приложение Android зависает при копировании файлов? - PullRequest
0 голосов
/ 31 марта 2019

Я делаю простой файловый менеджер для Android. Итак, проблема в том, что когда я копирую файлы или папки в любое место файловой системы, приложение просто зависает, пока файл / папка не будет скопирована. Я должен заметить, что приложение не падает, не отправляет ошибки Logcat. Приложение просто не отвечает ни за какие действия во время копирования файлов. Как я вижу, в других проводниках файлов эта проблема не возникает, и вы можете выполнять любые действия в приложении во время копирования файлов.

Я начинаю копировать файлы во фрагмент. Я пытался использовать как собственные методы копирования файлов, так и методы библиотеки FileUtils, но результат тот же.

Основные вещи:

import android.support.v7.widget.Toolbar;
...
private Toolbar mToolbar;

метод onCreateView:

....
//Start copying files
mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem menuItem) {
            if(menuItem.getItemId() == R.id.paste_button ){
                if(fileActionMode.equals("copy")){
                    copy();
                }
            }
            if(menuItem.getItemId() == R.id.cancel_button){
                mToolbar.getMenu().removeItem(R.id.paste_button);
                mToolbar.getMenu().removeItem(R.id.cancel_button);
            }
            updateUI();
            return true;
        }
    });
...

Метод копирования:

 private void copy(){
    mToolbar.getMenu().removeItem(R.id.paste_button);
    mToolbar.getMenu().removeItem(R.id.cancel_button);
    File file = new File(initFilePath);
    if(file.isDirectory()){
        try {
            FileUtils.copyDirectoryToDirectory(file, new File(FileFoldersLab.get(getActivity()).getCurPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            updateUI();
        }
    }else if(file.isFile()){
        try {
            FileUtils.copyFileToDirectory(file, new File(FileFoldersLab.get(getActivity()).getCurPath()));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            updateUI();
        }
    }
}

Мой метод, который я использовал вместо FileUtils ....:

public void copyFile(File src) throws IOException{
    createFile(src.getName());
    try (InputStream in = new FileInputStream(src)) {
        try (OutputStream out = new FileOutputStream(mCurPath+File.separator+src.getName())) {
            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        }
    }
}

1 Ответ

0 голосов
/ 31 марта 2019

Приложение зависает, потому что ваш метод copy() является длительным процессом. Следует также помнить, что вы выполняете эту трудоемкую операцию в основном потоке , который также отвечает за поддержаниепользовательский интерфейс.

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

Теперь есть два способа справиться с этой ситуацией:

  1. Вы можете заставить пользователя приложения подождать, пока ваш метод copy() не будет завершен, показывая вращающийся загрузчик непосредственно передВаш метод copy() запускается и скрывает загрузчик, как только метод завершен. Этот метод загрузчика гарантирует, что пользователь не выполнит никакого события, связанного с пользовательским интерфейсом (например, нажатие кнопки), пока метод copy() не будет завершен. Вот кодизвините за код в Kotlin .

    class DialogUtil private constructor() {
    
    init {
    throw AssertionError()
    }
    
    companion object {
    
    private var progressDialog: ProgressDialog? = null
    
    fun showAlertDialog(context: Context, message: String?) {
        AlertDialog.Builder(context).setMessage(message)
                .setCancelable(false).setPositiveButton("OK") { 
     dialogInterface, _ -> dialogInterface.dismiss() }.show()
    }
    
    fun showProgressDialog(context: Context) {
        progressDialog = ProgressDialog(context)
        progressDialog!!.setProgressStyle(ProgressDialog.STYLE_SPINNER)
        progressDialog!!.requestWindowFeature(Window.FEATURE_NO_TITLE)
        progressDialog!!.setMessage("Please wait...")
        progressDialog!!.setCancelable(false)
        progressDialog!!.isIndeterminate = true
        progressDialog!!.show()
    }
    
    fun hideProgressDialog() {
        if (progressDialog != null) {
            progressDialog!!.dismiss()
        }
      }
     }
    }
    

Сделайте вспомогательный класс likВыше e для loader, а затем чуть выше строки, где начинается ваш метод copy(), вызовите этот загрузчик следующим образом

    DialogUtil.showProgressDialog(this)

Above this относится к контексту, поэтому, если метод copy()во фрагменте вам нужно передать activity (getActivity () в java) вместо него.

Как только ваш метод copy() завершится, вы можете скрыть вращающийся загрузчик следующим образом:

    DialogUtil.hideProgressDialog()

напишите эту строку чуть ниже вашего copy() метода.

Второй способ - запустить метод copy() в каком-то другом потоке, так что вашему потоку пользовательского интерфейса (основному потоку) не нужно заботиться о методе copy() и ваше приложение будет сохранено от замораживания. Одно из преимуществЭтот метод заключается в том, что даже если ваш copy() метод все еще выполняется, пользователь все равно может выполнять события, связанные с пользовательским интерфейсом, не испытывая дрожания или задержки. Я предлагаю вам использовать AsyncTask для выполнения copy()метод, поскольку он может выполнять длинные задачи в рабочем потоке, а затем может предоставить вам возможность уведомить пользователя о завершении задачи в основном потоке (UI).Код выглядит следующим образом:

private class MyTask extends AsyncTask<X, Y, Z>
{

 protected void onPreExecute(){
      //any specific setup before you start copy() method ,  runs on UI 
      // thread
 } 

 protected Z doInBackground(X...x){
     // your copy() method itself, runs on worker thread other than main UI 
     //  thread, don't perform any UI related activities from here , since 
    // it is the worker thread
 }

 protected void onProgressUpdate(Y y){
   //any event you wanna perform , while the task is in progress, runs on 
   //UI main thread
 }

 protected void onPostExecute(Z z){
  //the event you wanna perform once your copy() method is complete, runs 
  //on UI main thread
}
}

После определения класса вы можете запустить этот AsyncTask следующим образом:

    MyTask myTask = new MyTask();
    myTask.execute(x);

Куда бы я ни упомянул пользовательский интерфейсЭто означает, что оттуда вы можете выполнить любую задачу или операцию, связанную с пользовательским интерфейсом.Вы должны помнить, что события пользовательского интерфейса могут выполняться только из потока пользовательского интерфейса, если вы пытаетесь выполнить операции, связанные с пользовательским интерфейсом, из потока, отличного от потока пользовательского интерфейса (основной поток) приложение может неожиданно закрыться.

Я предлагаю сделать этот AsyncTask как внутренний класс, в самом фрагменте, вместо создания нового класса полностью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...