15

每当将文件添加到特定文件夹(例如下载文件夹)时,我都希望收到一个事件。为此,我尝试了 3 种不同的方法,但均未成功。目标设备是 Android 15+。您对这 3 种方法中的任何一种都有任何经验,并且可以提供工作样本吗?

方法 1 - FileObserver:

在后台服务中,我为顶层文件夹添加了一个递归文件观察器,如此处所述。在 Android 4/5 上它可以工作,但在 Android 6 上没有触发任何事件(已知问题)最重要的是,在 Android 4/5 上,文件观察器似乎不可靠。在某些时候会调用 stopWatching() 方法,从那时起将不会收到任何事件。

在服务的 onStartCommand(..) 中:

    new MyFileObserver(Constants.DOWNLOAD_PATH, true).startWatching();

方法 2 - 内容观察者:

我尝试为我的用例调整内容观察器(如此所述),但我从未收到任何事件。

在服务的 onStart 中:

 getContentResolver().registerContentObserver( Uri.parse("content://download/"), true,
            new MyObserver(myHandler));

.

public class MyObserver extends ContentObserver {
  // left blank below constructor for this Contact observer example to work
  // or if you want to make this work using Handler then change below registering  //line
  public MyObserver(Handler handler) {
    super(handler);
  }

  @Override
  public void onChange(boolean selfChange) {
    this.onChange(selfChange, null);
    Log.e("", "~~~~~~ on change" + selfChange);
    // Override this method to listen to any changes
  }

  @Override
  public void onChange(boolean selfChange, Uri uri) {
    // depending on the handler you might be on the UI
    // thread, so be cautious!
    Log.e("", "~~~~~~ on change uri" + selfChange);
  }
}

方法 3 - 广播接收器:

使用 BroadcastReceiver 我尝试获取 ON_DOWNLOAD_COMPLETE_EVENT (如此处所述但没有任何反应。

在服务的 StartCommand(...) 上:

 registerReceiver(new DownloadListenerService(), new IntentFilter(
            DownloadManager.ACTION_DOWNLOAD_COMPLETE));

下载监听服务:

public class DownloadListenerService extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, Intent intent) {
        System.out.println("got here");
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = settings.edit();

        String action = intent.getAction();
        if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
            String downloadPath = intent.getStringExtra(DownloadManager.COLUMN_URI);
            editor.putString("downloadPath", downloadPath);
            editor.commit();
        }
    }
}

显现:

  <receiver
            android:name=".DownloadListenerService"
            android:icon="@mipmap/ic_launcher"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
            </intent-filter>
        </receiver>
4

3 回答 3

2

实际上,目前还没有针对 Android 6.0 及更高版本的解决方法。在 Android 4/5/5.1 中,FileObserver大多数情况下工作正常,但对于 Android 6,当文件添加到外部目录时,您根本无法从系统获得任何类型的响应。知道这FileObserver一点在 Android 6 中完全没用。

但最终您可以使用在 Android 6 中也可以正常工作的 Content Observer 检测到 Android 系统中添加的内容。也许这可以解决您的问题,直到 Google 提供修复程序。

这是我目前使用 ContenObserver 的方式:

mycontentobserver = new MyContentObserver(handler,**Your path**,this);
getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, mycontentobserver);

比 MyContentOberserver.class 我只检查我的特定路径中最后编辑的文件,如果它们不超过 5-10 秒,我假设这触发了 ContentObserver 事件。

更新编辑:

这就是它应该为您工作的方式:

在您的BackgroundService.class中:

mycontentobserver = new MyContentObserver(handler,**Your download folder path**,this);
getContentResolver().registerContentObserver(MediaStore.Files.getContentUri("external"), true, mycontentobserver);

而不是在MyContentObserver.class内部:

public MyContentObserver(Handler handler, String workpath,  ContentModificationService workcontext) {
    super(handler);
    downloadfolderpath = workpath;
    contentcontext = workcontext;
}

@Override
public void onChange(boolean selfChange, Uri uri) {

   if(downloadfolderpath != null) {
      File file = new File(downloadfolder);
      if (file.isDirectory()) {
         listFile = file.listFiles();
         if (listFile != null && listFile.length > 0) {
            
            // Sort files from newest to oldest (this is not the best Method to do it, but a quick on)
            Arrays.sort(listFile, new Comparator<File>() {
               public int compare(File f1, File f2) {
               return      Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
               }
            });

            if (listFile[listFile.length - 1].lastModified() >= System.currentTimeMillis() - 5000) { //adjust the time (5000 = 5 seconds) if you want.

               //Some file was added !! Yeah!
               //Use the contentcontext to launch some Method in you Service
               //For example:
               contentcontext.SomeMethodToContinue();
            }
         }
      }
   }
}

我希望这会有所帮助,如果它对你有用,现在让我。它适用于我在 android 6.1 上的下载文件夹:)

于 2016-04-04T11:55:55.380 回答
0

我已经在我的 android 应用程序中尝试了您的APPROACH 3-BroadcastReceiver,它对我有用。请仔细阅读可能对您有帮助的代码。您可以根据需要更改图片网址。

我的活动和广播接收器类:

package com.example.sudiproy.downloadingpath;

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import com.google.gson.Gson;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private DownloadManager downloadManager;
    private Button startDownload;
    private Button checkStatusOfDownload;
    private long downloadReference;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startDownload = (Button) findViewById(R.id.btn_download);
        checkStatusOfDownload = (Button) findViewById(R.id.btn_check_status);
        startDownload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
                Uri Download_Uri = Uri.parse("http://demo.mysamplecode.com/Sencha_Touch/CountryServlet?start=0&limit=999");
                DownloadManager.Request request = new DownloadManager.Request(Download_Uri);
                //Set title to be displayed if notification is enabled
                request.setTitle("My Data Download");
                //Set the local destination for the downloaded file to a path within the application's external files directory
                request.setDestinationInExternalFilesDir(MainActivity.this, Environment.DIRECTORY_DOWNLOADS, "CountryList.json");
                //Enqueue a new download and same the referenceId
                downloadReference = downloadManager.enqueue(request);

                IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
                registerReceiver(downloadReceiver, filter);
            }
        });
        checkStatusOfDownload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                DownloadManager.Query myDownloadQuery = new DownloadManager.Query();
                myDownloadQuery.setFilterById(downloadReference);
                //Query the download manager about downloads that have been requested.
                Cursor cursor = downloadManager.query(myDownloadQuery);
                if (cursor.moveToFirst()) {
                    checkStatus(cursor);
                }

            }
        });


    }

    private void checkStatus(Cursor cursor) {
        int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
        int status = cursor.getInt(columnIndex);
        //column for reason code if the download failed or paused
        int columnReason = cursor.getColumnIndex(DownloadManager.COLUMN_REASON);
        int reason = cursor.getInt(columnReason);
        //get the download filename
        int filenameIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME);
        String filename = cursor.getString(filenameIndex);

        String statusText = "";
        String reasonText = "";


        switch (status) {
            case DownloadManager.STATUS_FAILED:
                statusText = "STATUS FAILED";
                break;
            case DownloadManager.STATUS_PAUSED:
                statusText = "STATUS_PAUSED";
                break;
            case DownloadManager.STATUS_PENDING:
                statusText = "STATUS_PENDING";
                break;
            case DownloadManager.STATUS_RUNNING:
                statusText = "STATUS_RUNNING";
                break;
            case DownloadManager.STATUS_SUCCESSFUL:
                statusText = "STATUS_SUCCESSFUL";
                reasonText = "Filename:\n" + filename;
                break;
        }
    }


    private BroadcastReceiver downloadReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
            if (downloadReference == referenceId) {
                int ch;
                ParcelFileDescriptor file;
                StringBuffer strContent = new StringBuffer("");
                StringBuffer countryData = new StringBuffer("");
                try {
                    file = downloadManager.openDownloadedFile(downloadReference);
                    FileInputStream fileInputStream
                            = new ParcelFileDescriptor.AutoCloseInputStream(file);

                    while ((ch = fileInputStream.read()) != -1)
                        strContent.append((char) ch);

                    JSONObject responseObj = new JSONObject(strContent.toString());
                    JSONArray countriesObj = responseObj.getJSONArray("countries");

                    for (int i = 0; i < countriesObj.length(); i++) {
                        Gson gson = new Gson();
                        String countryInfo = countriesObj.getJSONObject(i).toString();
                        Country country = gson.fromJson(countryInfo, Country.class);
                        countryData.append(country.getCode() + ": " + country.getName() + "\n");
                    }
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (JSONException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }


            }
        }
    };
}

我的 Pojo 课程:

package com.example.sudiproy.downloadingpath;


public class Country {

    String code = null;
    String name = null;

    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

我的清单声明:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sudiproy.downloadingpath">
    <uses-permission android:name="android.permission.INTERNET">
    </uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
        </uses-permission>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


    </application>

</manifest>
于 2016-04-19T07:39:22.803 回答
0

你的第三种方法是最好的也是最有效的一种。但是,请尝试在清单中的接收者标签中使用完整的包名称。问题是该操作被发送到未找到的接收器。例如:

<receiver
    android:name="com.example.mypackage.receivers.DownloadListenerService"
    android:icon="@mipmap/ic_launcher"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
    </intent-filter>
</receiver>
于 2016-04-21T03:23:26.807 回答