安卓后台线程处理

3
我正在制作图像处理应用程序。我需要扫描手机上的图片,并列出它们的像素数。因此,这将对性能产生很大影响,据我所知,我需要在后台线程上工作。
那么我的问题是,什么是最好的方法?我知道IntentService可能是最佳解决方案,但我不确定如何使用它来实现进度条,并且我需要返回Picture对象并稍后在洗牌按钮上更新UI。我使用Glide库进行更新,所以会很顺利。
阅读有关Asynctasks的文章时,我偶然发现了一些评论,称其为不好的做法,并且会导致内存泄漏,应该避免使用。rXJava目前也过于复杂。
这是我的代码:
主要活动:
@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;
}
2个回答

2

IntentService

IntentService是一种有效的方法。您可以使用Broadcasts将结果返回到应用程序的另一个组件中,例如Activity或另一个Service:

  1. 启动IntentService - 如果需要某些参数,请将它们放在服务意图的Extras中。
  2. 您的IntentService在后台线程上运行,直到计算完成。
  3. 完成后,发送一个广播,并将计算结果放置在意图附加信息中。
  4. 在Activity中注册一个BroadcastReceiver,以便监听计算结果广播。
  5. Activity中接收广播后,从意图附加信息中检索计算结果。

您还可以实现由您的Service接收的广播,例如取消计算或更新参数。

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

替代方案

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

  • 如您所提到的,使用RxJava和IOcomputation调度程序也是一个不错的选择。

  • AsyncTask很好,只要您不将其与Activity的硬引用绑定在一起 - 不要将其实现为Activity的内部类,如果要将结果返回,请通过WeakReference<T>进行。


2

AsyncTask是可以的,但是你需要小心它的实现。

然而,对于长时间运行的任务,有更好的选择。 IntentService是一个不错的选择。

在使用IntentService时,如果涉及到响应式UI,你可以添加两个东西。

通知

创建一个持续的通知,指示您的应用正在处理某些事情。这让用户知道他们的CPU周期被后台进程中的某些东西所消耗,他们不太可能感到困惑和恼怒,因为设备运行缓慢。

此外,当Android正在查找要杀死以释放内存的后台应用程序时,它允许您的应用程序保持活动状态。

EventBus

通过使用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));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接