小米開原始檔管理器MiCodeFileExplorer-原始碼研究(4)-檔案操作工具類FileOperationHelper

小雷FansUnion發表於2015-10-28
檔案操作是非常通用的,註釋都寫在原始碼中了,不多說~
需要特別說明的是,任務的非同步執行和IOperationProgressListener。
拷貝和刪除等操作,是比較費時的,採用了非同步執行的方式~


Android非同步執行,我也是初次瞭解,在CSDN上找了一篇文章,後續寫個單獨的例子,單獨寫1篇介紹。
http://blog.csdn.net/xufenghappy6/article/details/7343899
非同步執行+事件通知 是一種比較流行的模式,比同步等待很多時候要好。


另外,特別需要說明的是,Java應用程式中、Android、Windows開發、Linux Shell都會有檔案File的概念,他們本質是一樣的。
檔案的核心概念基本一致,都是用的作業系統的檔案概念,不同作業系統之間的區別也不大。
建立、刪除、重新命名、複製、貼上,輸入-執行-輸出,也都一樣。


package net.micode.fileexplorer.util;


import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;


import net.micode.fileexplorer.model.FileInfo;
import android.os.AsyncTask;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
/**檔案操作工具類,執行檔案的建立、移動、貼上、重新命名、刪除等*/
public class FileOperationHelper {
	private static final String LOG_TAG = "FileOperation";
    //內部檔案集合,用來臨時儲存複製、移動等操作,使用者選擇的檔案集合
	private ArrayList<FileInfo> mCurFileNameList = new ArrayList<FileInfo>();


	private boolean mMoving;


	private IOperationProgressListener mOperationListener;


	private FilenameFilter mFilter;


	public interface IOperationProgressListener {
		void onFinish();


		void onFileChanged(String path);
	}


	public FileOperationHelper(IOperationProgressListener l) {
		mOperationListener = l;
	}


	public void setFilenameFilter(FilenameFilter f) {
		mFilter = f;
	}


	//根據路徑和檔名,建立檔案
	public boolean CreateFolder(String path, String name) {
		Log.v(LOG_TAG, "CreateFolder >>> " + path + "," + name);


		File f = new File(Util.makePath(path, name));
		if (f.exists())
			return false;


		return f.mkdir();
	}


	//拷貝若干個檔案,把檔案集合拷貝到“當前檔案集合中mCurFileNameList”,可以供“貼上操作”使用
	public void Copy(ArrayList<FileInfo> files) {
		copyFileList(files);
	}


	//貼上,把當前檔案集合中“mCurFileNameList”的檔案,拷貝到目標路徑下
	public boolean Paste(String path) {
		if (mCurFileNameList.size() == 0)
			return false;


		final String _path = path;
		//非同步執行某個任務
		asnycExecute(new Runnable() {
			@Override
			public void run() {
				for (FileInfo f : mCurFileNameList) {
					CopyFile(f, _path);
				}
				//通知操作變化
				mOperationListener.onFileChanged(Environment
						.getExternalStorageDirectory().getAbsolutePath());
                //貼上之後,需要清空mCurFileNameList
				clear();
			}
		});


		return true;
	}


	//是否可以“貼上”,mCurFileNameList有元素
	public boolean canPaste() {
		return mCurFileNameList.size() != 0;
	}


	//開始移動,標記“正在移動”,拷貝檔案集合
	public void StartMove(ArrayList<FileInfo> files) {
		if (mMoving)
			return;


		mMoving = true;
		copyFileList(files);
	}


	//移動狀態
	public boolean isMoveState() {
		return mMoving;
	}


	//能否移動,假設path為“C:/a/b”,f.filePath為“C:、/a/b/c/d.png”,不能移動
	//TODO 感覺不太靠譜啊,為啥不能移動到檔案的上級目錄呢?
	public boolean canMove(String path) {
		for (FileInfo f : mCurFileNameList) {
			if (!f.IsDir)
				continue;


			if (Util.containsPath(f.filePath, path))
				return false;
		}


		return true;
	}


	//清空當前檔案集合
	public void clear() {
		synchronized (mCurFileNameList) {
			mCurFileNameList.clear();
		}
	}


	//停止移動,移動檔案是非同步執行,結束後有事件通知
	public boolean EndMove(String path) {
		if (!mMoving)
			return false;
		mMoving = false;


		if (TextUtils.isEmpty(path))
			return false;


		final String _path = path;
		asnycExecute(new Runnable() {
			@Override
			public void run() {
				for (FileInfo f : mCurFileNameList) {
					MoveFile(f, _path);
				}


				mOperationListener.onFileChanged(Environment
						.getExternalStorageDirectory().getAbsolutePath());


				clear();
			}
		});


		return true;
	}


	public ArrayList<FileInfo> getFileList() {
		return mCurFileNameList;
	}


	//非同步執行某個任務
	//android的類AsyncTask對執行緒間通訊進行了包裝,提供了簡易的程式設計方式來使後臺執行緒和UI執行緒進行通訊:後臺執行緒執行非同步任務,並把操作結果通知UI執行緒。
	//可以參考http://blog.csdn.net/xufenghappy6/article/details/7343899
	private void asnycExecute(Runnable r) {
		final Runnable _r = r;
		new AsyncTask() {
			@Override
			protected Object doInBackground(Object... params) {
				synchronized (mCurFileNameList) {
					_r.run();
				}
				if (mOperationListener != null) {
					mOperationListener.onFinish();
				}


				return null;
			}
		}.execute();
	}


	//某個路徑是否被選中
	public boolean isFileSelected(String path) {
		synchronized (mCurFileNameList) {
			for (FileInfo f : mCurFileNameList) {
				if (f.filePath.equalsIgnoreCase(path))
					return true;
			}
		}
		return false;
	}


	//檔案重新命名
	public boolean Rename(FileInfo f, String newName) {
		if (f == null || newName == null) {
			Log.e(LOG_TAG, "Rename: null parameter");
			return false;
		}


		File file = new File(f.filePath);
		String newPath = Util.makePath(Util.getPathFromFilepath(f.filePath),
				newName);
		final boolean needScan = file.isFile();
		try {
			boolean ret = file.renameTo(new File(newPath));
			if (ret) {
				if (needScan) {
					mOperationListener.onFileChanged(f.filePath);
				}
				mOperationListener.onFileChanged(newPath);
			}
			return ret;
		} catch (SecurityException e) {
			Log.e(LOG_TAG, "Fail to rename file," + e.toString());
		}
		return false;
	}


	//刪除若干檔案,先copy檔案集合,再非同步執行刪除操作,刪除完成後,有通知
	public boolean Delete(ArrayList<FileInfo> files) {
		copyFileList(files);
		asnycExecute(new Runnable() {
			@Override
			public void run() {
				for (FileInfo f : mCurFileNameList) {
					DeleteFile(f);
				}


				mOperationListener.onFileChanged(Environment
						.getExternalStorageDirectory().getAbsolutePath());


				clear();
			}
		});
		return true;
	}


	//刪除1個檔案
	protected void DeleteFile(FileInfo f) {
		if (f == null) {
			Log.e(LOG_TAG, "DeleteFile: null parameter");
			return;
		}


		File file = new File(f.filePath);
		boolean directory = file.isDirectory();
		if (directory) {
			for (File child : file.listFiles(mFilter)) {
				if (Util.isNormalFile(child.getAbsolutePath())) {
					DeleteFile(Util.GetFileInfo(child, mFilter, true));
				}
			}
		}


		file.delete();


		Log.v(LOG_TAG, "DeleteFile >>> " + f.filePath);
	}


	//執行1個檔案的拷貝,如果檔案是目錄,拷貝整個目錄,可能有遞迴Copy
	private void CopyFile(FileInfo f, String dest) {
		if (f == null || dest == null) {
			Log.e(LOG_TAG, "CopyFile: null parameter");
			return;
		}


		File file = new File(f.filePath);
		if (file.isDirectory()) {


			// directory exists in destination, rename it
			String destPath = Util.makePath(dest, f.fileName);
			File destFile = new File(destPath);
			int i = 1;
			while (destFile.exists()) {
				destPath = Util.makePath(dest, f.fileName + " " + i++);
				destFile = new File(destPath);
			}


			for (File child : file.listFiles(mFilter)) {
				if (!child.isHidden()
						&& Util.isNormalFile(child.getAbsolutePath())) {
					CopyFile(Util.GetFileInfo(child, mFilter, Settings
							.instance().getShowDotAndHiddenFiles()), destPath);
				}
			}
		} else {
			String destFile = Util.copyFile(f.filePath, dest);
		}
		Log.v(LOG_TAG, "CopyFile >>> " + f.filePath + "," + dest);
	}


	//移動檔案,通過重新命名的方式,移動的
	private boolean MoveFile(FileInfo f, String dest) {
		Log.v(LOG_TAG, "MoveFile >>> " + f.filePath + "," + dest);


		if (f == null || dest == null) {
			Log.e(LOG_TAG, "CopyFile: null parameter");
			return false;
		}


		File file = new File(f.filePath);
		String newPath = Util.makePath(dest, f.fileName);
		try {
			return file.renameTo(new File(newPath));
		} catch (SecurityException e) {
			Log.e(LOG_TAG, "Fail to move file," + e.toString());
		}
		return false;
	}


	//把檔案集合copy到mCurFileNameList中,同步~
	private void copyFileList(ArrayList<FileInfo> files) {
		synchronized (mCurFileNameList) {
			mCurFileNameList.clear();
			for (FileInfo f : files) {
				mCurFileNameList.add(f);
			}
		}
	}


}

相關文章