我正在为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);
}
}
}
}
答案 0 :(得分:0)
由于您的copy()
方法耗时,应用程序冻结。还应记住,您正在主线程上执行此耗时的操作,负责维护用户界面。
由于 UI线程忙于完成长时间的操作,因此避免了刷新和执行UI操作,因此该应用程序最终挂起并冻结。
现在有两种方法可以处理这种情况,
通过在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()
方法下面。
第二种方法是在其他某个线程中启动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作为片段本身的内部类,而不是完全创建一个新类。