前言
需求是,从分用户中,获取主用户中下载目录(sd卡Download目录)下的文件/目录,并复制到分用户中的下载目录(sd卡Download目录),开始的思路是,通过File类读取主用户的文件/目录,再写入分用户的下载目录。但是,再使用的时候发现几个问题:
- File不能跨用户读取。
- 就算你有准确的文件sd路径,也无法跨进程读取到文件的流。
那么,既然从File无法入手,我们就从Uri入手了。解决方案:
- 先获取要复制文件/目录的uri。
- 再通过getContentResolver().openInputStream(uri)拷贝复制;(更正一下,是可以通过file还读取到的。)
PS:跨用户操作需要系统权限。
具体示例:
假设,我们要复制主用户中的图片到分用户中。
void cpoyMainUserToCurrentUser(){
ContentResolver mContentResolver = getContentResolver();
//通过MediaStore去查询图片
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
Uri mainUri = maybeAddUserId(uri, 0);//将uri转为主用户下uri. 0为主用户id
//开始先查询主用户中的图片
Cursor mCursor = mContentResolver.query(mainUri, null,
MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?",
new String[] { "image/jpeg", "image/png" }, MediaStore.Images.Media.DEFAULT_SORT_ORDER);
Map<Uri, String> imageInfoMap = new HashMap<>();//存入图片的uri和名称
while (mCursor.moveToNext()) {
try {
//图片路径
String path = mCursor.getString(mCursor
.getColumnIndex(MediaStore.Images.Media.DATA));
//图片名称
String display_name = mCursor.getString(mCursor
.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
//图片id
int _id = mCursor.getInt(mCursor
.getColumnIndex(MediaStore.MediaColumns._ID));
/*******
* 得到图片的Uri,这个就是我们复制的关键
******/
Uri imageUri = Uri.withAppendedPath(mainUri, "" + _id);
/*****
* 接下来去从主用户复制到当前用户
*****/
FileOutputStream fos = null;
try {
//将图片复制到当前用户下的DICM图片目录下,防止命名重复,这里简单处理了一下
fos = new FileOutputStream(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath()+"/"+System.currentTimeMillis()+"_"+display_name);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
InputStream is = null ;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
is = getContentResolver().openInputStream(imageUri);
byte [] buffer = new byte [ 1024 ];
int len = is.read(buffer);
while (len >= 0 ){
baos.write(buffer, 0 , len);
len = is.read(buffer);
}
baos.writeTo(fos);
Log.e("cpoyMainUserToCurrentUser", "复制完成了");
} catch (IOException e) {
e.printStackTrace();
}finally {
if (is != null ) {
is.close();
}
if(fos !=null ){
fos.close();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
//通过反射来使用maybeAddUserId方法
private Uri maybeAddUserId(Uri uri, int userId){
Uri ret = uri;
try {
Class c = ContentProvider.class;
Method method = null;
method = c.getMethod("maybeAddUserId", new Class[]{ Uri.class, int.class});
method.setAccessible(true);
Object obj = method.invoke(null, uri, userId);
ret = (Uri) obj;
}catch (Exception e) {
e.printStackTrace();
}
return ret;
}
上面我们可以通过MediaStore可以获取图片,那如果我们想要操作某某些目录或分类怎么获取uri呢?比如下载目录?比如视频,或者某个目录,这块就要去研究一下DocumentsUI了。
导入Download目录下文件/目录
基于源码内置的下载app中能很好的显示各个目录和文件,所以下面简单分析一下DocumentsUI源码。不求熟悉整个DocumentsUI,但至少知道Download目录的uri和子uri的使用方式。这样,才能继续备份Download目录下文件这个需求。
源码是8.0的
我们先从xml入手,先看AndroidManifest.xml。
LauncherActivity是主activity,顺便说一下DocumentsUI在桌面显示的icon名称是下载,但是app的名称是文件。我们看下LauncherActivity.java。
###LauncherActivity.java###
@Override
60 protected void onCreate(Bundle savedInstanceState) {
61 super.onCreate(savedInstanceState);
62
63 launch();
64
65 finish();
66 }
private void launch() {
69 ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
...........
83 startTask();
84 }
98 private void startTask() {
99 Intent intent = createLaunchIntent(this); //常见要启动的intent
........................
107 startActivity(intent);
108 }
/**********创建要启动的activity*****************/
132 public static final Intent createLaunchIntent(Activity activity) {
133 Intent intent = new Intent(activity, FilesActivity.class); //启动FilesActivity activity
134 intent.setData(buildLaunchUri());
135
136 // Relay any config overrides bits present in the original intent.
137 Intent original = activity.getIntent();
138 if (original != null) {
139 copyExtras(original, intent);
140 if (original.hasExtra(Intent.EXTRA_TITLE)) {
141 intent.putExtra(
142 Intent.EXTRA_TITLE,
143 original.getStringExtra(Intent.EXTRA_TITLE));
144 }
145 }
146 return intent;
147 }
从上面可以看到,最终启动了FilesActivity,继承BaseActivity。我们继续从xml开始分析。
###FilesActivity.java###
public FilesActivity() {
super(R.layout.files_activity, TAG);
}
###values/layouts.xml###
<resources>
<item name="documents_activity" type="layout">@layout/drawer_layout</item>
<item name="files_activity" type="layout">@layout/drawer_layout</item>
</resources>
可见FilesActivity使用的是R.layout.files_activity 即 drawer_layout.xml。
###BaseActivity.java###
public void onCreate(Bundle icicle) {
// Record the time when onCreate is invoked for metric.
mStartTime = new Date().getTime();
super.onCreate(icicle);
final Intent intent = getIntent();
addListenerForLaunchCompletion();
setContentView(mLayoutId);
..................
mDrawer = DrawerController.create(this, mInjector.config); // Drawerlayout的控制类
..............
mProviders = DocumentsApplication.getProvidersCache(this); //ProvidersCache
..............
}
###ProvidersCache.java 获取###
@Override
protected Void doInBackground(Void... params) {
final long start = SystemClock.elapsedRealtime();
mTaskRoots.put(mRecentsRoot.authority, mRecentsRoot);
final PackageManager pm = mContext.getPackageManager();
// Pick up provider with action string
final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
final List<ResolveInfo> providers = pm.queryIntentContentProviders(intent, 0);
for (ResolveInfo info : providers) {
handleDocumentsProvider(info.providerInfo);
}
....................
}
ProvidersCache中通过UpdateTask中异步通过DocumentsContract.PROVIDER_INTERFACE来获取根目录uri。如上图中图片,视频,音频,最近,下载等等对应的uri,后面再通过这些uri,获取到其子目录/文件,一层套一层。
这边举一个点击左侧滑栏 下载 的逻辑分析。
###BaseActivity.java###
284 @Override
285 public void onRootPicked(RootInfo root) {
286 // Clicking on the current root removes search
287 mSearchManager.cancelSearch();
....................
307 // Recents is always in memory, so we just load it directly.
308 // Otherwise we delegate loading data from disk to a task
309 // to ensure a responsive ui.
310 if (mProviders.isRecentsRoot(root)) {
311 refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
312 } else {
313 mInjector.actions.getRootDocument(
314 root,
315 TimeoutTask.DEFAULT_TIMEOUT,
316 doc -> mInjector.actions.openRootDocument(doc));
317 }
318 }
if是进入右侧最近选项,else是其他选项(图片,下载等等)的。不过最终都会调用到refreshCurrentRootAndDirectory中
###BaseActivity.java###
@Override
public final void refreshCurrentRootAndDirectory(int anim) {
// The following call will crash if it's called before onCreateOptionMenu() is called in
// which we install menu item to search view manager, and there is a search query we need to
// restore. This happens when we're still initializing our UI so we shouldn't cancel the
// search which will be restored later in onCreateOptionMenu(). Try finding a way to guard
// refreshCurrentRootAndDirectory() from being called while we're restoring the state of UI
// from the saved state passed in onCreate().
mSearchManager.cancelSearch();
refreshDirectory(anim);
final RootsFragment roots = RootsFragment.get(getFragmentManager()); //RootsFragment展示的内容部分
if (roots != null) {
roots.onCurrentRootChanged();
}
...................
}
RootsFragment就是我们要展示内容的主区域,我们进去看一下。
###RootsFragment.java###
.......
private ListView mList;
private RootsAdapter mAdapter;
private LoaderCallbacks<Collection<RootInfo>> mCallbacks;
.....
mCallbacks = new LoaderCallbacks<Collection<RootInfo>>() {
@Override
public Loader<Collection<RootInfo>> onCreateLoader(int id, Bundle args) {
return new RootsLoader(activity, providers, state);
}
@Override
public void onLoadFinished(
Loader<Collection<RootInfo>> loader, Collection<RootInfo> result) {
if (!isAdded()) {
return;
}
Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
List<Item> sortedItems = sortLoadResult(result, handlerAppIntent); //sortLoadResult ,将根目录排序
mAdapter = new RootsAdapter(activity, sortedItems, mDragListener);
mList.setAdapter(mAdapter);
onCurrentRootChanged();
}
@Override
public void onLoaderReset(Loader<Collection<RootInfo>> loader) {
mAdapter = null;
mList.setAdapter(null);
}
};
* @param handlerAppIntent When not null, apps capable of handling the original intent will
* be included in list of roots (in special section at bottom).
*/
private List<Item> sortLoadResult(
Collection<RootInfo> roots, @Nullable Intent handlerAppIntent) {
final List<Item> result = new ArrayList<>();
final List<RootItem> libraries = new ArrayList<>();
final List<RootItem> others = new ArrayList<>();
for (final RootInfo root : roots) {
final RootItem item = new RootItem(root, mActionHandler); //mActionHandler
Activity activity = getActivity();
if (root.isHome() && !Shared.shouldShowDocumentsRoot(activity)) {
continue;
} else if (root.isLibrary()) {
libraries.add(item);
} else {
others.add(item);
}
}
................
}
上面中root记录根目录信息,mActionHandler继承AbstractActionHandler的,AbstractActionHandler提供了一些文档操作,包括根据父uri获取到子uri和其他信息。
@Override
public Loader<DirectoryResult> onCreateLoader(int id, Bundle args) {
Context context = mActivity;
if (mState.stack.isRecents()) {
if (DEBUG) Log.d(TAG, "Creating new loader recents.");
return new RecentsLoader(context, mProviders, mState, mInjector.features);
} else {
Log.e("ddddddddddddd---000", mSearchMgr.isSearching()+","+mState.stack.getRoot().toString());
Uri contentsUri = mSearchMgr.isSearching()
? DocumentsContract.buildSearchDocumentsUri(
mState.stack.getRoot().authority,
mState.stack.getRoot().rootId,
mSearchMgr.getCurrentSearch())
: DocumentsContract.buildChildDocumentsUri(
mState.stack.peek().authority,
mState.stack.peek().documentId);
if (mInjector.config.managedModeEnabled(mState.stack)) {
contentsUri = DocumentsContract.setManageMode(contentsUri);
}
if (DEBUG) Log.d(TAG,
"Creating new directory loader for: "
+ DocumentInfo.debugString(mState.stack.peek()));
return new DirectoryLoader(
mInjector.features,
context,
mState.stack.getRoot(),
mState.stack.peek(),
contentsUri,
mState.sortModel,
mDirectoryReloadLock,
mSearchMgr.isSearching());
}
}
else中通过authority和documentId得到保存了uri、cursor等信息的对象DirectoryLoader, 通过获取到对应的uri,完成后再调用Model保存,我们进入DirectoryLoader看看这个loader。
###DirectoryLoader.java###
@Override
88 public final DirectoryResult loadInBackground() {
........................
96 final ContentResolver resolver = getContext().getContentResolver();
97 final String authority = mUri.getAuthority();
98
99 final DirectoryResult result = new DirectoryResult();
100 result.doc = mDoc;
102 ContentProviderClient client = null;
103 Cursor cursor;
104 try {
105 client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority);
106 if (mDoc.isInArchive()) {
107 ArchivesProvider.acquireArchive(client, mUri);
108 }
109 result.client = client;
110
111 Resources resources = getContext().getResources();
112 if (mFeatures.isContentPagingEnabled()) {
113 Bundle queryArgs = new Bundle();
114 mModel.addQuerySortArgs(queryArgs);
115
116 // TODO: At some point we don't want forced flags to override real paging...
117 // and that point is when we have real paging.
118 DebugFlags.addForcedPagingArgs(queryArgs);
119
120 cursor = client.query(mUri, null, queryArgs, mSignal);
121 } else {
122 cursor = client.query(
123 mUri, null, null, null, mModel.getDocumentSortQuery(), mSignal);
124 }
125
126 if (cursor == null) {
127 throw new RemoteException("Provider returned null");
128 }
129
130 cursor.registerContentObserver(mObserver);
131
132 cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
133
134 if (mSearchMode && !mFeatures.isFoldersInSearchResultsEnabled()) {
135 // There is no findDocumentPath API. Enable filtering on folders in search mode.
136 cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
137 }
138
139 // TODO: When API tweaks have landed, use ContentResolver.EXTRA_HONORED_ARGS
140 // instead of checking directly for ContentResolver.QUERY_ARG_SORT_COLUMNS (won't work)
141 if (mFeatures.isContentPagingEnabled()
142 && cursor.getExtras().containsKey(ContentResolver.QUERY_ARG_SORT_COLUMNS)) {
143 if (VERBOSE) Log.d(TAG, "Skipping sort of pre-sorted cursor. Booya!");
144 } else {
145 cursor = mModel.sortCursor(cursor);
146 }
147 result.cursor = cursor;
148 } catch (Exception e) {
149 Log.w(TAG, "Failed to query", e);
150 result.exception = e;
151 } finally {
152 synchronized (this) {
153 mSignal = null;
154 }wenj
155 // TODO: Remove this call.
156 ContentProviderClient.releaseQuietly(client);
157 }
158
159 return result;
160 }
161
获取到目录的cursor,并保存到DirectoryResult中,下面我们看看view,即ListDocumentHolder
###ListDocumentHolder.java###
@Override
public void bind(Cursor cursor, String modelId) {
assert (cursor != null);
mModelId = modelId;
mDoc.updateFromCursor(cursor, getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY));
mIconHelper.stopLoading(mIconThumb);
.................
}
###DocumentInfo###
public void updateFromCursor(Cursor cursor, String authority) {
this.authority = authority;
this.documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
this.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
this.displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
this.lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
this.flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
this.summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
this.size = getCursorLong(cursor, Document.COLUMN_SIZE);
this.icon = getCursorInt(cursor, Document.COLUMN_ICON);
this.deriveFields();
///M: DRM refactory, get drm info from cursor
this.isDrm = getCursorInt(cursor, MediaStore.MediaColumns.IS_DRM) > 0;
this.drmMethod = getCursorInt(cursor, MtkMediaStore.MediaColumns.DRM_METHOD);
this.data = getCursorString(cursor, MediaStore.MediaColumns.DATA);
}
在bind中通过cursor获取目录下各个目录/文件的名称,大小,类型等等信息。
好了,这个时候,我们大体知道了DocumentsUI中文件的存储形式,是一个树状结构。下面我们来实现一下我们刚开始的需求,备份下载中的文件。
void cpoyMainDownloadToCurrentUser(){
DownloadRoot downloadRoot = getCurrentDownloadRoot(getApplicationContext());//获取当期下载download的信息
String parentDocumentId = downloadRoot.documentId;
//获取download目录下的文件和文件夹
List<DownloadFileEntry> fileEntries = getDownloadFiles();//获取住用户下载download目录信息
//备份这些文件和整个文件夹
for (DownloadFileEntry entry : fileEntries){
try {
Uri parentDocumentUri = DocumentsContract.buildDocumentUri(entry.authority, parentDocumentId);
Uri documentUri = DocumentsContract.buildDocumentUri(entry.authority, entry.documentId);
Log.e("uri", "documentUri=" + documentUri + ", parentDocumentUri=" + parentDocumentUri);
String downloadPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
if (entry.type==1){//文件夹
//判断本地有相同命名的文件夹
int fileIndex = 0;
File file = new File(downloadPath+"/"+entry.name);
while (file.isDirectory()){
fileIndex++;
file = new File(downloadPath+"/"+fileIndex+"_"+entry.name);
}
file.mkdir();
int sucess = copyDir(entry, file.getAbsolutePath());
}else {
//判断本地有相同命名的文件
int fileIndex = 0;
File file = new File(downloadPath+"/"+entry.name);
while (file.exists()){
fileIndex++;
file = new File(downloadPath+"/"+fileIndex+"_"+entry.name);
}
int sucess = copyFile(entry, file.getAbsolutePath());
}
//Build.VERSION.SDK_INT >= 24中DocumentsContract也有封装了一些方法,如重命名,移动,删除等
public class DownloadRoot {
public String authority;
public String rootId;
public int flags;
public int icon;
public String title;
public String summary;
public String documentId;
public long availableBytes;
public String mimeType;
@Override
public String toString() {
return authority+","+rootId+","+documentId+","+title+","+mimeType+","+summary+","+availableBytes+","+flags+","+icon;
}
public static DownloadRoot fromDownloadRootCursor(String authority, Cursor cursor){
final DownloadRoot root = new DownloadRoot();
root.authority = authority;
root.rootId = getCursorString(cursor, DocumentsContract.Root.COLUMN_ROOT_ID);
root.flags = getCursorInt(cursor, DocumentsContract.Root.COLUMN_FLAGS);
root.icon = getCursorInt(cursor, DocumentsContract.Root.COLUMN_ICON);
root.title = getCursorString(cursor, DocumentsContract.Root.COLUMN_TITLE);
root.summary = getCursorString(cursor, DocumentsContract.Root.COLUMN_SUMMARY);
root.documentId = getCursorString(cursor, DocumentsContract.Root.COLUMN_DOCUMENT_ID);
root.availableBytes = getCursorLong(cursor, DocumentsContract.Root.COLUMN_AVAILABLE_BYTES);
root.mimeType = getCursorString(cursor, DocumentsContract.Root.COLUMN_MIME_TYPES);
// root.deriveFields();
return root;
}
public Uri getUri() {
return DocumentsContract.buildRootUri(authority, rootId);
}
public static String getCursorString(Cursor cursor, String columnName) {
if (cursor == null)
return null;
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getString(index) : null;
}
/**
* Missing or null values are returned as -1.
*/
public static long getCursorLong(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
String value = null;
if (index == -1) return -1;
/// M: seldom NPE, no side-effect of using default size
try {
value = cursor.getString(index);
} catch (NullPointerException e) {
e.printStackTrace();
return -1;
}
if (value == null) return -1;
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return -1;public class DownloadRoot {
public String authority;
public String rootId;
public int flags;
public int icon;
public String title;
public String summary;
public String documentId;
public long availableBytes;
public String mimeType;
@Override
public String toString() {
return authority+","+rootId+","+documentId+","+title+","+mimeType+","+summary+","+availableBytes+","+flags+","+icon;
}
public static DownloadRoot fromDownloadRootCursor(String authority, Cursor cursor){
final DownloadRoot root = new DownloadRoot();
root.authority = authority;
root.rootId = getCursorString(cursor, DocumentsContract.Root.COLUMN_ROOT_ID);
root.flags = getCursorInt(cursor, DocumentsContract.Root.COLUMN_FLAGS);
root.icon = getCursorInt(cursor, DocumentsContract.Root.COLUMN_ICON);
root.title = getCursorString(cursor, DocumentsContract.Root.COLUMN_TITLE);
root.summary = getCursorString(cursor, DocumentsContract.Root.COLUMN_SUMMARY);
root.documentId = getCursorString(cursor, DocumentsContract.Root.COLUMN_DOCUMENT_ID);
root.availableBytes = getCursorLong(cursor, DocumentsContract.Root.COLUMN_AVAILABLE_BYTES);
root.mimeType = getCursorString(cursor, DocumentsContract.Root.COLUMN_MIME_TYPES);
// root.deriveFields();
return root;
}
public Uri getUri() {
return DocumentsContract.buildRootUri(authority, rootId);
}
public static String getCursorString(Cursor cursor, String columnName) {
if (cursor == null)
return null;
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getString(index) : null;
}
/**
* Missing or null values are returned as -1.
*/
public static long getCursorLong(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
String value = null;
if (index == -1) return -1;
/// M: seldom NPE, no side-effect of using default size
try {
value = cursor.getString(index);
} catch (NullPointerException e) {
e.printStackTrace();
return -1;
}
if (value == null) return -1;
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return -1;
}
}
/**
* Missing or null values are returned as 0.
*/
public static int getCursorInt(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getInt(index) : 0;
}
public boolean isDirectory() {
return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}
}public class DownloadRoot {
public String authority;
public String rootId;
public int flags;
public int icon;
public String title;
public String summary;
public String documentId;
public long availableBytes;
public String mimeType;
@Override
public String toString() {
return authority+","+rootId+","+documentId+","+title+","+mimeType+","+summary+","+availableBytes+","+flags+","+icon;
}
public static DownloadRoot fromDownloadRootCursor(String authority, Cursor cursor){
final DownloadRoot root = new DownloadRoot();
root.authority = authority;
root.rootId = getCursorString(cursor, DocumentsContract.Root.COLUMN_ROOT_ID);
root.flags = getCursorInt(cursor, DocumentsContract.Root.COLUMN_FLAGS);
root.icon = getCursorInt(cursor, DocumentsContract.Root.COLUMN_ICON);
root.title = getCursorString(cursor, DocumentsContract.Root.COLUMN_TITLE);
root.summary = getCursorString(cursor, DocumentsContract.Root.COLUMN_SUMMARY);
root.documentId = getCursorString(cursor, DocumentsContract.Root.COLUMN_DOCUMENT_ID);
root.availableBytes = getCursorLong(cursor, DocumentsContract.Root.COLUMN_AVAILABLE_BYTES);
root.mimeType = getCursorString(cursor, DocumentsContract.Root.COLUMN_MIME_TYPES);
// root.deriveFields();
return root;
}
public Uri getUri() {
return DocumentsContract.buildRootUri(authority, rootId);
}
public static String getCursorString(Cursor cursor, String columnName) {
if (cursor == null)
return null;
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getString(index) : null;
}
/**
* Missing or null values are returned as -1.
*/
public static long getCursorLong(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
String value = null;
if (index == -1) return -1;
/// M: seldom NPE, no side-effect of using default size
try {
value = cursor.getString(index);
} catch (NullPointerException e) {
e.printStackTrace();
return -1;
}
if (value == null) return -1;
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return -1;
}
}
/**
* Missing or null values are returned as 0.
*/
public static int getCursorInt(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getInt(index) : 0;
}
public boolean isDirectory() {
return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}
}
}
}
/**
* Missing or null values are returned as 0.
*/
public static int getCursorInt(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getInt(index) : 0;
}
public boolean isDirectory() {
return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
List<DownloadFileEntry> queryDocuments(String authority, String parentDocumentId){
List<DownloadFileEntry> retsul = new ArrayList<>();
Uri mUri = DocumentsContract.buildChildDocumentsUri(authority, parentDocumentId);
mUri = maybeAddUserId(mUri, 0);
ContentResolver resolver = getContentResolver();
ContentProviderClient client = resolver.acquireUnstableContentProviderClient (mUri.getAuthority());
Cursor c = null;
try {
c = client.query(mUri, null, null, null, null);
if (c!=null){
Log.e("====parentDocumentId", parentDocumentId+","+mUri+","+c.getCount());
while (c.moveToNext()){
DownloadFileEntry itemInfo = getEntryFromCursor(c, authority);
Uri parentDocumentUri = DocumentsContract.buildDocumentUri(authority, parentDocumentId);
Uri documentUri = DocumentsContract.buildDocumentUri(authority, itemInfo.documentId);
Log.e("queryDocuments","documentUri="+documentUri+", parentDocumentUri="+parentDocumentUri);
retsul.add(itemInfo);
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (c!=null) {
c.close();
}
}
return retsul;
}
private List<DownloadFileEntry> getDownloadFiles() {
List<DownloadFileEntry> fileEntries = new ArrayList<>();
Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
final List<ResolveInfo> providers = getPackageManager().queryIntentContentProviders(intent, 0);
for (ResolveInfo info : providers) {
String authority = info.providerInfo.authority;
Uri rootsUri = DocumentsContract.buildRootsUri(authority);
if ("com.android.providers.downloads.documents".equals(authority)){
//只获取download的目录霞的目录。
ContentResolver resolver = getContentResolver();
ContentProviderClient client = null;
Cursor cursor = null;
try {
client = resolver.acquireUnstableContentProviderClient (authority);
cursor = client.query(rootsUri, null, null, null, null);
if (cursor!=null && cursor.moveToFirst()){
DownloadRoot root = DownloadRoot.fromDownloadRootCursor(authority, cursor);
Log.e("getDownloadFiles", root.toString()+","+root.getUri()+",");
fileEntries.addAll(queryDocuments(root.authority, root.documentId));
}
} catch (Exception e) {
} finally {
if (cursor!=null){
cursor.close();
}
}
}
}
return fileEntries;
}
//0:成功 ,1:失败
public int copyDir(DownloadFileEntry fromEntry, String toFileDir){
int result = 0;
//创建目录
File toList=new File(toFileDir);
if(!toList.exists()){
toList.mkdirs();
}
//要复制的文件目录Entry
List<DownloadFileEntry> fromList = queryDocuments(fromEntry.authority, fromEntry.documentId);
//判断文件是否存在
if(fromList==null){
return result;
}
//遍历要复制的全部文件
for(int i=0;i<fromList.size();i++){
DownloadFileEntry itemEntry = fromList.get(i);
if (itemEntry.type==1){
//文件夹
result = result | copyDir(itemEntry, toFileDir+"/ "+itemEntry.name+"/" );
}else {
//文件
result = result | copyFile(itemEntry, toFileDir+"/ "+itemEntry.name);
}
}
return result;
}
/**
* 拷贝文件
*/
public int copyFile(DownloadFileEntry entry, String toFile){
try {
ContentResolver resolver = getContentResolver();
String display_name = entry.name;
FileOutputStream fos = new FileOutputStream(toFile);
InputStream is = null ;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Uri partURI = DocumentsContract.buildDocumentUri(entry.authority, entry.documentId);
partURI = maybeAddUserId(partURI, 0);
is = resolver.openInputStream(partURI);
String dddd = is.toString();
Log.e("=====", entry.name+"----"+ dddd);
byte [] buffer = new byte [ 1024 ];
int len = is.read(buffer);
while (len >= 0 )
{
baos.write(buffer, 0 , len);
len = is.read(buffer);
}
baos.writeTo(fos);
Log.e("imprt img sucess", ""+display_name);
if (is != null ) {
try {
is.close();
} catch (IOException e) {
}
}
if(fos !=null ){
try {
fos.close();
} catch (IOException e) {
}
}
}catch (Exception e){
e.printStackTrace();
return 1;
}
return 0;
}
public DownloadFileEntry getEntryFromCursor(Cursor cursor, String authority) {
DownloadFileEntry info = new DownloadFileEntry();
info.authority = authority;
info.documentId = getCursorString(cursor, DocumentsContract.Document.COLUMN_DOCUMENT_ID);
info.mimeType = getCursorString(cursor, DocumentsContract.Document.COLUMN_MIME_TYPE);
info.name = getCursorString(cursor, DocumentsContract.Document.COLUMN_DISPLAY_NAME);
info.lastModify = getCursorLong(cursor, DocumentsContract.Document.COLUMN_LAST_MODIFIED);
info.bytesize = getCursorLong(cursor, DocumentsContract.Document.COLUMN_SIZE);
info.icon = getCursorInt(cursor, DocumentsContract.Document.COLUMN_ICON);
if (DocumentsContract.Document.MIME_TYPE_DIR.equals(info.mimeType)){
info.icon = R.mipmap.ic_launcher;
info.type = 1;
}else if (info.mimeType.startsWith("application/vnd.android.package-archive")){
info.icon = R.mipmap.ic_launcher;
}else if (info.mimeType.startsWith("image/")){
info.icon = R.mipmap.ic_launcher;
}else if (info.mimeType.startsWith("video/")){
info.icon = R.mipmap.ic_launcher;
}else if (info.mimeType.startsWith("audio/")){
info.icon = R.mipmap.ic_launcher;
}else {
info.icon = R.mipmap.ic_launcher;
}
return info;
}
/**
* Missing or null values are returned as 0.
*/
public static int getCursorInt(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getInt(index) : 0;
}
public static String getCursorString(Cursor cursor, String columnName) {
if (cursor == null)
return null;
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getString(index) : null;
}
/**
* Missing or null values are returned as -1.
*/
public static long getCursorLong(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
String value = null;
if (index == -1) return -1;
/// M: seldom NPE, no side-effect of using default size
try {
value = cursor.getString(index);
} catch (NullPointerException e) {
e.printStackTrace();
return -1;
}
if (value == null) return -1;
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return -1;
}
}
public class DownloadFileEntry implements Serializable {
public int icon;//图标
public String name;//名字
public String path;//路径
public long lastModify;//修改时间,时间戳
public String time;//显示年月日
public long bytesize;//文件大小
public String size;//文件大小,显示,kb, m, g
public int type;//类型, 0:文件,1:文件夹
public boolean isSelected = false;
public String documentId;
public String parentDocument;
public String authority;
public String mimeType;
@Override
public String toString() {
return parentDocument+","+documentId+","+authority+","+mimeType+","+name+","+type+","+path+","+size+","+time+","+icon;
}
}
public class DownloadRoot {
public String authority;
public String rootId;
public int flags;
public int icon;
public String title;
public String summary;
public String documentId;
public long availableBytes;
public String mimeType;
@Override
public String toString() {
return authority+","+rootId+","+documentId+","+title+","+mimeType+","+summary+","+availableBytes+","+flags+","+icon;
}
public static DownloadRoot fromDownloadRootCursor(String authority, Cursor cursor){
final DownloadRoot root = new DownloadRoot();
root.authority = authority;
root.rootId = getCursorString(cursor, DocumentsContract.Root.COLUMN_ROOT_ID);
root.flags = getCursorInt(cursor, DocumentsContract.Root.COLUMN_FLAGS);
root.icon = getCursorInt(cursor, DocumentsContract.Root.COLUMN_ICON);
root.title = getCursorString(cursor, DocumentsContract.Root.COLUMN_TITLE);
root.summary = getCursorString(cursor, DocumentsContract.Root.COLUMN_SUMMARY);
root.documentId = getCursorString(cursor, DocumentsContract.Root.COLUMN_DOCUMENT_ID);
root.availableBytes = getCursorLong(cursor, DocumentsContract.Root.COLUMN_AVAILABLE_BYTES);
root.mimeType = getCursorString(cursor, DocumentsContract.Root.COLUMN_MIME_TYPES);
// root.deriveFields();
return root;
}
public Uri getUri() {
return DocumentsContract.buildRootUri(authority, rootId);
}
public static String getCursorString(Cursor cursor, String columnName) {
if (cursor == null)
return null;
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getString(index) : null;
}
/**
* Missing or null values are returned as -1.
*/
public static long getCursorLong(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
String value = null;
if (index == -1) return -1;
/// M: seldom NPE, no side-effect of using default size
try {
value = cursor.getString(index);
} catch (NullPointerException e) {
e.printStackTrace();
return -1;
}
if (value == null) return -1;
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
return -1;
}
}
/**
* Missing or null values are returned as 0.
*/
public static int getCursorInt(Cursor cursor, String columnName) {
final int index = cursor.getColumnIndex(columnName);
return ((index != -1) && !cursor.isClosed()) ? cursor.getInt(index) : 0;
}
public boolean isDirectory() {
return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
}
}
更正一下:
上面说的,就算你有准确的文件sd路径,也无法跨进程读取到文件的流,是不对的,是可以通过file还读取到的。