Android 后台线程
Android background threading
我正在制作图像处理器应用程序。我需要扫描 phone 中的图片并列出它们的像素数。所以这会对性能产生很大影响,据我所知,我需要让它在后台线程上运行。
所以我的问题是,最好的方法是什么?我知道 IntentService 可能是最好的解决方案,但我不确定如何用它实现进度条,我需要 return 图片对象,然后更新 UI on shuffle 按钮。我正在使用 Glide 库进行更新,这样会很顺利。
阅读有关 Asynctasks 的文章时,我无意中看到评论说它很糟糕并会导致内存泄漏,应该避免使用它。 rXJava 目前太复杂了。
这是我的代码:
主要activity:
@OnClick(R.id.shuffle)
public void shuffleList() {
Collections.shuffle(listOfImageFiles);
recyclerViewAdapter = new PictureRecycleViewAdapter(listOfImageFiles, this);
recyclerView.swapAdapter(recyclerViewAdapter, false);
recyclerViewAdapter.notifyDataSetChanged();
}
@OnClick(R.id.scan)
public void processImages() {
//progress bar
listOfPictures = new ArrayList<>();
//Gets data from default camera roll directory. Note that some of the phone companies have different file paths. So instead of hardcoding string paths, I used this instead.
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File filePath = new File(path);
listOfImageFiles = scanPhotos(filePath);
// async?
for (File file : listOfImageFiles
) {
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
//int is sufficient for most today's pixels. long would be overkill - 4 vs 8 bytes
int pixels = bitmap.getHeight() * bitmap.getWidth();
listOfPictures.add(new Picture(file.getPath(), pixels));
}
}
public List<File> scanPhotos(File directory) {
List<File> listOfPictures = new ArrayList<>();
try {
File[] files = directory.listFiles();
for (File file : files
) {
if (file.isDirectory() && !file.isHidden()) {
listOfPictures.addAll(scanPhotos(file));
} else {
if (file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") || file.getName().endsWith(".png")) {
listOfPictures.add(file);
}
}
}
} catch (Exception e) {
Log.e(e.getMessage(), e.getMessage());
}
return listOfPictures;
}
AsyncTask
没问题,你只需要注意它的实现。
然而,对于更长的 运行 任务,有更好的选择。 IntentService
是个不错的选择。
当谈到使用 IntentService
时的响应式 UI 时,您可以添加两件事。
通知
创建一个持续通知,表明您的应用正在处理某事。这让用户知道他们的 CPU 周期正在被后台的东西吃掉,他们不太可能(?)对他们的设备 运行 变慢感到困惑和胡思乱想。
此外,当 Android 正在寻找要杀死的后台应用程序以释放内存时,它为您的应用程序提供了更多的存活空间。
事件总线
您可以使用 EventBus 库使 UI 报告变得极其简单。我个人是greenbot/EventBus的粉丝,但还有其他人。
例子
在你的Activity
中:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onProgressEvent(ProgressEvent event) {
mProgressBar.setProgress(event.value);
}
在你的IntentService
中:
EventBus.getDefault().post(new ProgressEvent(5000));
意图服务
IntentService
绝对是一种有效的方法。您可以使用 Broadcasts 将您的结果 return 发送到应用程序的另一个组件,无论是 Activity 还是其他服务,例如:
- 启动
IntentService
- 如果您需要一些参数,将它们放在服务意图的Extras中。
- 您的
IntentService
在后台线程上运行,直到计算完成。
- 完成后,发送一个广播,计算结果放在 intent extras 中。
- 在你的 activity 中注册一个
BroadcastReceiver
来监听你的计算结果广播。
- 在您的
Activity
中获取广播后,从 intent extras 中检索计算结果。
您还可以实施您的服务接收的广播,用于取消计算或更新参数等。
IntentService
的优点之一是您可以轻松地将它与 JobScheduler API 集成以延迟执行,直到满足某些系统条件。
备选方案
您可以使用总线库,例如 https://github.com/greenrobot/EventBus 在 Activity
和 Service
之间进行通信 - 唯一的问题是,EventBus 无法与远程服务(运行 在单独的进程中)。
就像你提到的,将 RxJava 与 IO 和 computation 调度器一起使用也是一个好主意。
AsyncTask
很好,只要你不将它与 activity 的硬引用联系起来——不要将它实现为 class 的内部Activity 如果你想返回结果,通过 WeakReference<T>
我正在制作图像处理器应用程序。我需要扫描 phone 中的图片并列出它们的像素数。所以这会对性能产生很大影响,据我所知,我需要让它在后台线程上运行。
所以我的问题是,最好的方法是什么?我知道 IntentService 可能是最好的解决方案,但我不确定如何用它实现进度条,我需要 return 图片对象,然后更新 UI on shuffle 按钮。我正在使用 Glide 库进行更新,这样会很顺利。
阅读有关 Asynctasks 的文章时,我无意中看到评论说它很糟糕并会导致内存泄漏,应该避免使用它。 rXJava 目前太复杂了。 这是我的代码:
主要activity:
@OnClick(R.id.shuffle)
public void shuffleList() {
Collections.shuffle(listOfImageFiles);
recyclerViewAdapter = new PictureRecycleViewAdapter(listOfImageFiles, this);
recyclerView.swapAdapter(recyclerViewAdapter, false);
recyclerViewAdapter.notifyDataSetChanged();
}
@OnClick(R.id.scan)
public void processImages() {
//progress bar
listOfPictures = new ArrayList<>();
//Gets data from default camera roll directory. Note that some of the phone companies have different file paths. So instead of hardcoding string paths, I used this instead.
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File filePath = new File(path);
listOfImageFiles = scanPhotos(filePath);
// async?
for (File file : listOfImageFiles
) {
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
//int is sufficient for most today's pixels. long would be overkill - 4 vs 8 bytes
int pixels = bitmap.getHeight() * bitmap.getWidth();
listOfPictures.add(new Picture(file.getPath(), pixels));
}
}
public List<File> scanPhotos(File directory) {
List<File> listOfPictures = new ArrayList<>();
try {
File[] files = directory.listFiles();
for (File file : files
) {
if (file.isDirectory() && !file.isHidden()) {
listOfPictures.addAll(scanPhotos(file));
} else {
if (file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") || file.getName().endsWith(".png")) {
listOfPictures.add(file);
}
}
}
} catch (Exception e) {
Log.e(e.getMessage(), e.getMessage());
}
return listOfPictures;
}
AsyncTask
没问题,你只需要注意它的实现。
然而,对于更长的 运行 任务,有更好的选择。 IntentService
是个不错的选择。
当谈到使用 IntentService
时的响应式 UI 时,您可以添加两件事。
通知
创建一个持续通知,表明您的应用正在处理某事。这让用户知道他们的 CPU 周期正在被后台的东西吃掉,他们不太可能(?)对他们的设备 运行 变慢感到困惑和胡思乱想。
此外,当 Android 正在寻找要杀死的后台应用程序以释放内存时,它为您的应用程序提供了更多的存活空间。
事件总线
您可以使用 EventBus 库使 UI 报告变得极其简单。我个人是greenbot/EventBus的粉丝,但还有其他人。
例子
在你的Activity
中:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onProgressEvent(ProgressEvent event) {
mProgressBar.setProgress(event.value);
}
在你的IntentService
中:
EventBus.getDefault().post(new ProgressEvent(5000));
意图服务
IntentService
绝对是一种有效的方法。您可以使用 Broadcasts 将您的结果 return 发送到应用程序的另一个组件,无论是 Activity 还是其他服务,例如:
- 启动
IntentService
- 如果您需要一些参数,将它们放在服务意图的Extras中。 - 您的
IntentService
在后台线程上运行,直到计算完成。 - 完成后,发送一个广播,计算结果放在 intent extras 中。
- 在你的 activity 中注册一个
BroadcastReceiver
来监听你的计算结果广播。 - 在您的
Activity
中获取广播后,从 intent extras 中检索计算结果。
您还可以实施您的服务接收的广播,用于取消计算或更新参数等。
IntentService
的优点之一是您可以轻松地将它与 JobScheduler API 集成以延迟执行,直到满足某些系统条件。
备选方案
您可以使用总线库,例如 https://github.com/greenrobot/EventBus 在
Activity
和Service
之间进行通信 - 唯一的问题是,EventBus 无法与远程服务(运行 在单独的进程中)。就像你提到的,将 RxJava 与 IO 和 computation 调度器一起使用也是一个好主意。
AsyncTask
很好,只要你不将它与 activity 的硬引用联系起来——不要将它实现为 class 的内部Activity 如果你想返回结果,通过WeakReference<T>