为什么Android应用程序在复制文件时冻结?

时间:2019-03-30 22:30:15

标签: java android file

我正在为Android开发一个简单的文件浏览器。因此,问题在于,当我将文件或文件夹复制到文件系统的任何位置时,该应用程序只会冻结,直到复制了文件/文件夹。我必须注意到该应用程序不会崩溃,也不会向Logcat发送错误。在复制文件时,该应用程序根本不响应任何操作。 如我所见,在其他文件浏览器中不会发生此问题,并且在复制文件时您可以在应用程序中执行任何操作。

我开始在Fragment中复制文件。我尝试同时使用自己的文件复制方法和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 :(得分:0)

由于您的copy()方法耗时,应用程序冻结。还应记住,您正在主线程上执行此耗时的操作,负责维护用户界面。

由于 UI线程忙于完成长时间的操作,因此避免了刷新和执行UI操作,因此该应用程序最终挂起并冻结。

现在有两种方法可以处理这种情况,

  1. 通过在copy()方法启动之前显示旋转的加载程序,并在方法一旦执行后隐藏加载程序,可以使App的用户等到您的copy()方法完成完成。此加载程序技术可确保用户在copy()方法完成之前不执行任何与UI相关的事件(例如单击按钮)。此处是用于显示加载程序的代码,抱歉,该代码位于科特林

    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()
        }
      }
     }
    }
    

为加载程序创建一个类似于上面的帮助程序类,然后在copy()方法开始的行上方,像这样调用此加载程序

    DialogUtil.showProgressDialog(this)

以上是上下文,因此,如果copy()方法位于片段中,则需要在其位置传递activity(java中的getActivity())。

完成copy()方法后,您可以像这样隐藏旋转的装载程序,

    DialogUtil.hideProgressDialog()

将此行写在您的copy()方法下面。

  1. 第二种方法是在其他某个线程中启动copy()方法,这样您的UI线程(主线程)无需关心copy()方法,并且您的应用程序从这种技术的一个优点是,即使您的copy()方法仍在进行中,用户仍然可以执行与UI相关的事件,而不会出现任何抖动或延迟。我建议您使用 AsyncTask >用于执行copy()方法,因为它可以在工作线程中执行长任务,然后可以为您提供通知用户有关main(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);

无论我在何处提到UI线程,您都可以从此处执行与UI相关的任何任务或操作。您应该记住,UI事件只能从UI线程执行,如果您尝试从 UI线程(主线程)以外的线程执行与UI相关的操作,则您的应用可能会意外关闭。

我建议将此AsyncTask作为片段本身的内部类,而不是完全创建一个新类。