美文网首页
Android 8 explorer文件管理器不显示内置sdca

Android 8 explorer文件管理器不显示内置sdca

作者: 梧叶已秋声 | 来源:发表于2019-12-05 18:55 被阅读0次

在DocumentUI中,内置sdcard的数据来源于ExternalStorageProvider这个类。

//frameworks\base\packages\ExternalStorageProvider\src\com\android\externalstorage\ExternalStorageProvider.java
public class ExternalStorageProvider extends FileSystemProvider {
   ........   
       public void updateVolumes() {
            synchronized (mRootsLock) {
                updateVolumesLocked();
          }
       }

      private void updateVolumesLocked() {
         mRoots.clear();
         VolumeInfo primaryVolume = null;
         final int userId = UserHandle.myUserId();
         final List<VolumeInfo> volumes = mStorageManager.getVolumes();
          ........   
    }
   ........   
 }

数据来源于mStorageManager.getVolumes()。

但是在StorageManager中是找不到getVolumes(0)这个函数的。

@SystemService(Context.STORAGE_SERVICE)
public class StorageManager{
  ..........
  /** {@hide} */
    public @NonNull List<VolumeInfo> getVolumes() {
        try {
            return Arrays.asList(mStorageManager.getVolumes(0));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
  ..........
}

这个存在于IStorageManager .aidl文件中

interface IStorageManager {
  ..........
 VolumeInfo[] getVolumes(int flags) = 45;
  ..........
}

实际调用的是StorageManagerService中的函数。mVolumes 是关键,实际是StorageManagerService中产生的数据。addInternalVolumeLocked中的mVolumes.put(internal.id, internal)产生的数据。

//frameworks\base\services\core\java\com\android\server\StorageManagerService.java
public class StorageManagerService extends IStorageManager.Stub{
  ..........
  /** Map from volume ID to disk */
    @GuardedBy("mLock")
    protected final ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
  ..........
  private void addInternalVolumeLocked() {
        // Create a stub volume that represents internal storage
        final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
                VolumeInfo.TYPE_PRIVATE, null, null);
        internal.state = VolumeInfo.STATE_MOUNTED;
        internal.path = Environment.getDataDirectory().getAbsolutePath();
        mVolumes.put(internal.id, internal);
    }
  ..........
    @Override
    public VolumeInfo[] getVolumes(int flags) {
        synchronized (mLock) {
            final VolumeInfo[] res = new VolumeInfo[mVolumes.size()];
            for (int i = 0; i < mVolumes.size(); i++) {
                res[i] = mVolumes.valueAt(i);
            }
            return res;
        }
    }
  ..........
}

ExternalStorageProvider实际并不产生数据,它是一个数据的提供者。那么ExternalStorageProvider是如何提供数据的呢?
ExternalStorageProvider 由于继承了FileSystemProvider ,而FileSystemProvider 中定义了queryDocument和queryChildDocuments,返回的是Cursor。传递的是Cursor。

//frameworks\base\core\java\com\android\internal\content\FileSystemProvider.java
public abstract class FileSystemProvider extends DocumentsProvider {
  ..........
  @Override
    public Cursor queryDocument(String documentId, String[] projection)
            throws FileNotFoundException {
        final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
        includeFile(result, documentId, null);
        return result;
    }

    @Override
    public Cursor queryChildDocuments(
            String parentDocumentId, String[] projection, String sortOrder)
            throws FileNotFoundException {

        final File parent = getFileForDocId(parentDocumentId);
        final MatrixCursor result = new DirectoryCursor(
                resolveProjection(projection), parentDocumentId, parent);
        for (File file : parent.listFiles()) {
            includeFile(result, null, file);
        }
        return result;
    }
  ..........

explorer直接打开应用显示的是FilesActivity,但是文件内容显示部分是用的DirectoryFragment,这里用的是一个recyclerview,用到的Adapter是ModelBackedDocumentsAdapter。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\documentsui\dirlist\DirectoryFragment.java
public class DirectoryFragment extends Fragment
        implements ItemDragListener.DragHost, SwipeRefreshLayout.OnRefreshListener {
  ..........

  mAdapter = new DirectoryAddonsAdapter(
                mAdapterEnv, new ModelBackedDocumentsAdapter(mAdapterEnv, mIconHelper));
  ..........

而通过打印log我发现,ModelBackedDocumentsAdapter中onBindViewHolder并没有被调用,打印log发现原因是ModelBackedDocumentsAdapter.getItemCount = 0,因此判断数据的产生有问题。最后发现uri不正确导致的,内部sdcard存储的uri应该是带primary的,但是实际生成的是带home的数据。通过打印log,查找uri产生的地方,最后发现是ActionHandler中的loadHomeDir函数。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\documentsui\AbstractActionHandler.java
/**
 * Provides support for specializing the actions (openDocument etc.) to the host activity.
 */
public abstract class AbstractActionHandler<T extends Activity & CommonAddons>
        implements ActionHandler {
  ..........
        protected final void loadHomeDir() {
        loadRoot(Shared.getDefaultRootUri(mActivity));
    }
  ..........
}
//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\documentsui\base\Shared.java
public final class Shared {
  ..........
    /**
     * Returns the default directory to be presented after starting the activity.
     * Method can be overridden if the change of the behavior of the the child activity is needed.
     */
    public static Uri getDefaultRootUri(Activity activity) {
        return shouldShowDocumentsRoot(activity)
                ? DocumentsContract.buildHomeUri()
                : DocumentsContract.buildRootUri(
                        Providers.AUTHORITY_DOWNLOADS, Providers.ROOT_ID_DOWNLOADS);
    }
  ..........
 }

//frameworks\base\core\java\android\provider\DocumentsContract.java
public final class DocumentsContract {
  ..........
  /**
     * Builds URI for user home directory on external (local) storage.
     * {@hide}
     */
    public static Uri buildHomeUri() {
        // TODO: Avoid this type of interpackage copying. Added here to avoid
        // direct coupling, but not ideal.
        return DocumentsContract.buildRootUri(EXTERNAL_STORAGE_PROVIDER_AUTHORITY, "home");
    }

  ..........
    /**
     * {@hide}
     */
    public static Uri buildPrimaryUri() {
        // TODO: Avoid this type of interpackage copying. Added here to avoid
        // direct coupling, but not ideal.
        return DocumentsContract.buildRootUri(EXTERNAL_STORAGE_PROVIDER_AUTHORITY, "primary");
    }
  ..........
}

最后,把Shared 类中的getDefaultRootUri函数中的buildHomeUri改成buildPrimaryUri即可,这样产生的就是能查询内部sdcard的uri。

AbstractActionHandler中创建了uri,并把uri传递给了DirectoryLoader,通过uri等参数,让DirectoryLoader 返回fragment显示所属的数据。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\documentsui\DirectoryLoader.java
public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
  ..........
    @Override
    public final DirectoryResult loadInBackground() {
         final DirectoryResult result = new DirectoryResult();
        result.doc = mDoc;
         ..........
         cursor = client.query(mUri, null, queryArgs, mSignal);
         ..........
         cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
         ..........
          result.cursor = cursor;
         ..........
         return result;
    }
  ..........
}

参考链接:
谷歌原生DocumentUI文件浏览的原理
从跨用户文件拷贝说起DocumentUI记录
documentsUI源码分析
DocumentsUI 文件打开
RecyclerView不调用onCreateViewHolder和onBindViewHolder的解决方法

相关文章

网友评论

      本文标题:Android 8 explorer文件管理器不显示内置sdca

      本文链接:https://www.haomeiwen.com/subject/uczdgctx.html