简体   繁体   中英

Refresh text file scanned by Media Scanner

My Android application is generating some data and writing them into a text file. The application appends the new information to this file at it produces new information.

I need to re-download this file from the PC form time to time. However, once I scan the file, and later modify it, I cannot see this change from the PC.

I tried scan/rescan the file using both MediaScannerConnection.scanFile() and sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(textFile)));

For example, if I scan the file before any data is written into it, the PC sees it, correctly, with size zero. But when I add text into the file and rescan it, the PC keeps seeing the file as size zero and I can only download an empty file.

How can I rescan/refresh this particular file without deleting and recreating it?

Been banging my head on the same issue for a month - finally have a grubby workaround that solved the problem. Not pretty but the only thing that works so far. I'm a java newb so expect there are better way to allow concurrent calls to block and wait. Feel free to suggest...

How it works:

  • create a function to call MediaScannerConnection.scanFile() that blocks until it completes.

  • rename the original file to file.tmp

  • call getContentResolver().delete() on the original file's Uri. This schedules the media data deletion, but doesn't wait for the media scanner to do it. The file's contents aren't lost as we've moved it.

  • call our scanFile() function to block until at least the delete has been actioned (thankfully the media scanner processes requests in order).

  • rename the temp file back to the original name again

  • call scanFile() to get the correct data into the media database.

It is critical to wait for the scans and deletes to complete, otherwise the media scanner (when it finally runs) wont realize the file has changed, and so wont update.

    static boolean mediaScanFile_inMediaScanFile = false; // multi-thread re-entrancy lock.
    static boolean mediaScanFile_completed = false;

    private static void mediaScanFile (Context context, String filepath)
    {
        while (mediaScanFile_inMediaScanFile) // wait till any active thread has exited.
        {
            try
            {
                Thread.sleep (50);
            } catch (InterruptedException ex)
            {
                // so it broke out early. Don't care.
                //Log.d(TAG, "Early exit from mediaScanFile_inMediaScanFile loop");
            }
        }
        mediaScanFile_inMediaScanFile = true; // grab exclusive lock.
        mediaScanFile_completed = false;
        //Log.i("updateFileInMediaLibrary", "Scanning " + filepath);

        // get the Media Scanner to re-add the file so that it's details are up-to-date.
        MediaScannerConnection.scanFile(
            context,
            new String[] {filepath},
            new String[] {"*/*"},
            new MediaScannerConnection.OnScanCompletedListener()
            {
                public void onScanCompleted(String path, Uri uri)
                {
                    //Log.i("updateFileInMediaLibrary", "Scanned " + path);
                    //Log.i("updateFileInMediaLibrary", "-> uri=" + uri);
                    mediaScanFile_completed = true;
                }
            });

        while (!mediaScanFile_completed)
        {
            try
            {
                Thread.sleep (50);
            } catch (InterruptedException ex)
            {
                // so it broke out early. Don't care.
                //Log.d(TAG, "Early exit from mediaScanFile_completed loop");
            }
        }
        //Log.i("updateFileInMediaLibrary", "Completed " + filepath);
        mediaScanFile_inMediaScanFile = false;
    }


    // Function to tell the USB Media Server about new or changed files, so that browsing the filesystem
    // from an external computer will see changes to files.
    public void updateFileInMediaLibrary(String filepath)
    {
        // Add or update the MediaLibrary entry for the file.
        File file = new File(filepath);
        Uri origUri = FileProvider.getUriForFile(this, "com.yourapp.fileprovider", file);
        Cursor cursor = getContentResolver().query(origUri,null,null,null,null);
        if (cursor.moveToFirst()) // an entry exists, that needs updating.
        {
            if (!file.exists())
            {
                getContentResolver().delete(origUri,null,null);
                mediaScanFile (this, filepath); // make sure the process has run, by waiting for a subsequent run to complete.
                return;
            }

            long new_size = file.length();
            //Log.i("updateFileInMediaLibrary", "new_size = "+new_size+" bytes");

            String tempFilePath = filepath+".tmp";

            File tempFile = new File(tempFilePath);

            //Log.i("updateFileInMediaLibrary","renaming original to temp: "+tempFilePath);
            file.renameTo(tempFile);
            // No need to call mediaScanFile() on tempFilePath as we don't care about seeing it. It will be gone again shortly.

            // delete original file entry.
            //Log.i("updateFileInMediaLibrary. Deleting original file entry: ", origUri+"");
            getContentResolver().delete(origUri,null,null);
            mediaScanFile (this, filepath); // make sure the process has run, by waiting for a subsequent run to complete.

            File original = new File(filepath);
            // move temp back to original name
            //Log.i("updateFileInMediaLibrary","Renaming temp back to original: "+filepath);
            tempFile.renameTo(original);
            mediaScanFile (this, filepath); // Finally get the correct details into the MTP database.

            //Log.i("updateFileInMediaLibrary", "Updated");
            return;
        }
        else // media scanner entry doesn't exist
        {
            if (file.exists())
            {
                mediaScanFile (this, filepath); // make sure the process has run, by waiting for a subsequent run to complete.
            }
            // else nothing to do so don't waste time trying.
        }
    }

Hope this helps someone :-)

Try notify data after modify the file:

private static void notifyData(Context context, URI fileUri) throws URISyntaxException {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        File f = new File(fileUri);
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);
        context.sendBroadcast(mediaScanIntent);
    }

Good luck.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM