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 还是其他服务,例如:

  1. 启动IntentService - 如果您需要一些参数,将它们放在服务意图的Extras中。
  2. 您的 IntentService 在后台线程上运行,直到计算完成。
  3. 完成后,发送一个广播,计算结果放在 intent extras 中。
  4. 在你的 activity 中注册一个 BroadcastReceiver 来监听你的计算结果广播。
  5. 在您的 Activity 中获取广播后,从 intent extras 中检索计算结果。

您还可以实施您的服务接收的广播,用于取消计算或更新参数等。

IntentService 的优点之一是您可以轻松地将它与 JobScheduler API 集成以延迟执行,直到满足某些系统条件。

备选方案

  • 您可以使用总线库,例如 https://github.com/greenrobot/EventBusActivityService 之间进行通信 - 唯一的问题是,EventBus 无法与远程服务(运行 在单独的进程中)。

  • 就像你提到的,将 RxJava 与 IOcomputation 调度器一起使用也是一个好主意。

  • AsyncTask 很好,只要你不将它与 activity 的硬引用联系起来——不要将它实现为 class 的内部Activity 如果你想返回结果,通过 WeakReference<T>