现象:

  • 公司开发的文件管理器,在App端上增删改文件时,如果这台Android设备连接了PC,将无法看到增删改后的文件:
    • 当我们将一台Android设备连接到PC,以MTP设备的方式进行读取时。
    • 使用一些市面上比较流行的文件管理器,甚至是厂商自带的文件管理器,进行创建文件、文件夹、复制、粘贴、删除等操作时,PC不会对我们的修改进行同步
    • 这就造成,如果我们使用手机下载的东西,可能会无法实时地将其连接电脑,复制出来进行使用。
    • 比较笨的办法是可以对Android设备进行重启。

出现原因:

  • MTP设备连接电脑的时候,PC读取的并不是MTP设备的磁盘文件数据。而是通过Android设备的MTP服务进行映射出来。
  • 该映射的数据源时Android的Provider中的一个media数据库:/data/data/com.android.provider.media/databases/external.db
    • 该数据库中有一个files表,在这个表中,所有记录都将会被MTP服务进行映射到PC的文件管理器上,同时我们在PC上做的修改,也首先对服务器中的files表进行修改,随后才会触发系统的磁盘操作。
  • 由此可知,如果files表中没有的数据,则无法在MTP中得到呈现
  • 也就是说,如果一个Apk,对Android设备的磁盘文件进行了操作,但是没有通知files表进行更新的话,那么改动不会通知MTP服务,则PC端不会有任何响应:
    • 如:Apk删除文件,该文件在磁盘中已经被删除,但是files表中依然有记录,则PC端依旧显示该文件。但是已经无法再操作。

问题分析:

  • Android 4.4以前,我们可以使用Intent.ACTION_MEDIA_MOUNTED,让Android系统重新扫描一遍SD卡,这样PC就能同步文件管理器的操作了。
    • 缺点是,该广播只要已发送,Android系统就会全盘扫描,其实很耗电也很费性能。
  • Android 4.4以后,google可能是发现了每次调用这个intent都会重新扫描SD卡,非常耗费系统资源,所以禁止非系统应用使用这个intent了。
    • 其实到这里,如果你的应用是系统级别的应用,你大可以发送这个广播,让系统帮你处理,但是无奈很多App都不可能拥有系统应用的权限
  • 同时,google为了解决我们这个需求,提供了一个方案:
    • 使用下述方法,可以指定一个文件的路径,随后系统会将这个文件进行扫描,扫描过后,PC也能同步这个文件了。
public static void scanFile(Context context, String[] paths, String[] mimeTypes, OnScanCompletedListener callback) {
    ClientProxy client = new ClientProxy(paths, mimeTypes, callback);
    MediaScannerConnection connection = new MediaScannerConnection(context, client);
    client.mConnection = connection;
    connection.connect();
}
  • 但是这种方案,有一个非常严重的问题:
  • 如果我们扫描的是一个文件夹,系统会默认这个文件夹是一个文件,于是将其当成文件进行入库,此时就会造成一个现象:
  • PC中显示该文件夹为文件,Android设备中则显示为文件夹。
  • 这都是external.db数据库闹的鬼啊!!!!!!

需要解决的疑问:

  • 我们不能使用scanFile()这个这个方法进行扫描文件夹了,那么该怎么做呢?
    • 使用ContentResolver可以对external数据库进行增删操作。
      • 我们发现,files表中,有这么一个字段: format
        • 该字段表示的是该条数据的类型是文件还是目录。那么我们可以通过判断我们要操作的数据是目录还是文件,就能决定format的类型,从而解决之前的问题了。
  • 解决方案:
    • 使用ContentResolver将文件、文件夹记录更新到Android的files数据表中。
    • 如果是目录,则数据库记录的“format”为 0x3000
    • 而如果是文件,“format”我们可以不用管,我们只需要更新文件对应的mimetype就行

注意事项:

  • ContentResolver可以使用update()方法进行更新数据库。
    • 但是此操作进行更新数据库,不会通知MTP服务重新读取数据库中的内容。换言之,就是update方法不能保证PC同步
  • 出现原因:
    • MTPService中有两个方法:
void sendObjectAdded(int objectHandle);
void sendObjectRemoved(int objectHandle);

  • 这两个方法的执行,会通知MTP设备,数据库进行了增删操作。唯独没有update操作。所以这就造成了update方式无效的原因。
  • 因此,如果是对文件进行剪贴、重命名的操作,使用update的方式,将依旧无法更新通知PC端。
  • 解决方案: - 遇到剪贴、重命名操作,先将老数据从表中删除,在重新将新数据插入到数据表中即可。 - 如果涉及大量目录、文件的工作,可以进行批处理操作,使用LIKE子句进行删除

 评论