54

これは、Android フォーラムでの一般的なユーザーの質問として始まりました。しかし、それは必然的にプログラミングの問題になりました。これが私の問題です。

AndroidにはMediaScannerというサービスがあり、SDカードがマウント解除されて再マウントされるといつでも(私は信じています)バックグラウンドで実行されます。このサービスは、カード上のすべてのメディア ファイルのデータを収集し、音楽アプリケーションからクエリできる SQLite DB を提供します。ほとんどの音楽アプリケーションは、SD カードのスキャンに関連するバッテリーの消耗を抑えるため、このサービスを使用しています。

Androidを使い始めてから、SDカードから削除しても、デバイスに同期したM3UプレイリストがこのSQLite DBに残るという問題が一貫してありました。カードには約10個のm3uファイルしかないにもかかわらず、使用する音楽アプリに約40個のプレイリストのコレクションが表示されるようになりました. 残りのプレイリストは再生されず、空です。音楽アプリから削除することで手動で削除できますが、これを行うのはうんざりです。これらのゴースト プレイリストを削除するには、もっと良い方法が必要です。

Android マーケットには SDRescan と Music Sc​​anner の 2 つのアプリがあります。これらはまさにこれを行うと思われますが、どちらも機能しません。

MediaStore データベースを更新または削除してゼロから始める独自のアプリの作成に着手しましたが、あまり進んでいません。次のコードを実行する Android アプリがあります。

sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, 
        Uri.parse("file://" + Environment.getExternalStorageDirectory()))); 

SDカードをスキャンする方法としてオンラインでこのコードの例をいくつか見つけましたが、まったく運がありません。任意のヒント?

完全なコード:

package com.roryok.MediaRescan;

import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;

public class MediaRescan extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, 
                Uri.parse("file://" + Environment.getExternalStorageDirectory()))); 
        setContentView(R.layout.main);
    }

    //Rescan the sdcard after copy the file
    private void rescanSdcard() throws Exception{     
      Intent scanIntent = new Intent(Intent.ACTION_MEDIA_MOUNTED, 
                Uri.parse("file://" + Environment.getExternalStorageDirectory()));   
      IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
      intentFilter.addDataScheme("file");     
      sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, 
                Uri.parse("file://" + Environment.getExternalStorageDirectory())));    
    }
}
4

3 に答える 3

58

使いやすい「単一ファイル ベース」のソリューションを次に示します。

ファイルの追加:

ファイルを追加するたびに、次を使用してMediaStoreコンテンツ プロバイダーに通知します。

sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(newMediaFile)));

ファイルの削除:

同様に、ファイルを削除するときは、次を使用してMediaStoreコンテンツ プロバイダーに通知します。

getContentResolver().delete(uri, null, null)  // (Credit goes to [DDSports][1])
于 2013-02-13T08:52:11.427 に答える
19

わかりました、私はそれをやった。

カードを再スキャンするのではなく、アプリはメディアストア内のすべてのプレイリストを繰り返し処理し、_data フィールドの長さをチェックします。M3U ファイルが関連付けられていないすべてのリストで、このフィールドは常に空であることがわかりました。次に、元の Android 音楽アプリのソース コードを見つけ、delete メソッドを見つけて、それを使用して長さ 0 のプレイリストを削除するだけでした。アプリの名前を PlaylistPurge に変更しました (「再スキャンしないため」 ' もう)、以下のコードを投稿しています。

私はおそらく、これをどこか、マーケットまたは自分のサイト ( http://roryok.com ) で公開する予定です。

package com.roryok.PlaylistPurge;

import java.util.ArrayList;
import java.util.List;

import android.app.ListActivity;
import android.content.ContentUris;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;

public class PlaylistPurge extends ListActivity {

    private List<String> list = new ArrayList<String>();
    private final String [] STAR= {"*"};

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ListAdapter adapter = createAdapter();
        setListAdapter(adapter);
    }

    /**
     * Creates and returns a list adapter for the current list activity
     * @return
     */
    protected ListAdapter createAdapter()
    {
        // return play-lists
        Uri playlist_uri= MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;    
        Cursor cursor= managedQuery(playlist_uri, STAR, null,null,null);
        cursor.moveToFirst();
        for(int r= 0; r<cursor.getCount(); r++, cursor.moveToNext()){
            int i = cursor.getInt(0);
            int l = cursor.getString(1).length();
            if(l>0){
                // keep any playlists with a valid data field, and let me know
                list.add("Keeping : " + cursor.getString(2) + " : id(" + i + ")");
            }else{
                // delete any play-lists with a data length of '0'
                Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, i);
                getContentResolver().delete(uri, null, null);
                list.add("Deleted : " + cursor.getString(2) + " : id(" + i + ")");
            }
        }       
        cursor.close();
        // publish list of retained / deleted playlists
        ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);

        return adapter;
    }
}

アップデート:

これは、アプリに関する私のブログの投稿へのリンクですhttp://roryok.com/blog/index.php/2010/07/23/clearing-out-deleted-playlists-in-android/

更新 2: 2013 年 4 月 9 日火曜日

この投稿から私のブログへのトラフィックが多くなり、感謝のメールがたくさん届きました。役に立ってよかったです!潜在的なユーザーは、私のくだらないアプリが現在、実行するとすぐにクラッシュすることを知っておく必要がありますが、実際には本来の機能を実行します! 私はいつも戻ってこのクラッシュ動作を修正し、それを市場に出すつもりでした.2013年が私がそれをする年になることを願っています.

于 2010-07-22T14:28:31.223 に答える
13

次のコードを使用して、特定のファイルの再スキャンを要求できます。

注: 渡される MIME TYPE は重要です。「*/*」を使用すると、MP3 ID3 タグに加えた変更が SQLite で適切に更新されないことに気付きましたが、「audio/mp3」を使用すると機能しました

MediaScannerConnection.scanFile(
    context, 
    new String[]{ pathToFile1, pathToFile2 }, 
    new String[]{ "audio/mp3", "*/*" }, 
    new MediaScannerConnectionClient()
    {
        public void onMediaScannerConnected()
        {
        }
        public void onScanCompleted(String path, Uri uri)
        {
        }
    });
于 2012-12-19T22:57:57.563 に答える