每当文件被添加到特定文件夹时,我希望收到一个事件,例如下载文件夹。为了实现这一点,我尝试了3种不同的方法,但没有成目标设备是Android 15+。您对这3种方法中的任何一种方法有任何经验吗?可以帮助您处理工作样本吗?
APPROACH 1 - FileObserver:
在后台服务中,我按照here所述为顶层文件夹添加了一个递归文件观察器。在Android 4/5上它的工作但在Android 6上没有事件被触发(known issue)除此之外,似乎在Android 4/5上文件观察者不可靠。在某些时候,stopWatching()方法被调用从那时起,将不会收到任何活动。
在服务的onStartCommand(..)中:
new MyFileObserver(Constants.DOWNLOAD_PATH, true).startWatching();
APPROACH 2 - 内容观察员:
我尝试为我的用例调整内容观察者(如here所述),但我从未收到任何事件。
在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);
}
}
APPROACH 3 - BroadcastReceiver:
使用BroadcastReceiver,我尝试获取ON_DOWNLOAD_COMPLETE_EVENT(如here所述。但没有任何反应。
在服务的StartCommand(...)中:
registerReceiver(new DownloadListenerService(), new IntentFilter(
DownloadManager.ACTION_DOWNLOAD_COMPLETE));
DownloadListenerService:
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>
答案 0 :(得分:2)
实际上,目前还没有针对Android 6.0及更高版本的已知解决方案。在Android 4/5 / 5.1中,FileObserver
工作正常,但对于Android 6,当文件添加到外部目录时,您无法从系统获得任何类型的响应。知道这个FileObserver
在Android 6中完全没用。
但最终你可以在Android系统中检测到添加的内容,内容观察器在Android 6中运行良好。也许这可以解决您的问题,直到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上的下载文件夹:)
答案 1 :(得分: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>
答案 2 :(得分:0)
您的第三种方法是最好的方法,也是最有效方法。 但是请尝试在Manifest中的接收器标记中使用完整的包名称。 问题是该动作被发送到未找到的接收器。 例如:
<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>