Android 从 firebase 下载多个文件时如何管理内存

How to manage memory when downloading multiple files from firebase in Android

我在 Firebase 存储中有一个图像目录,我正在尝试将该目录中的所有文件下载到我的应用程序中。每个图像在数据库中都有一个相应的字段,用于存储其名称 下载大部分是成功的,但当您尝试与 ui 交互时应用程序冻结并收到应用程序无响应提示,或者在您等待一段时间后崩溃。

下面是我用来将图像下载到我的应用程序存储目录的代码 '''

private void downloadAllTopicsAndFirstItems(Activity activity) {
    databaseReference.child(FIREBASE_TOPIC_NODE).addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            int count = 0;
            for (DataSnapshot snap : snapshot.getChildren()) {
                Topic topic = snap.getValue(Topic.class);
                if (topic == null) return;
                if (topic.getItems() != null) topic.setTotalItems(topic.getItems().size());
                File fileTopic = new File(activity.getFilesDir(), topic.getFirebaseImageNode() + ".jpg");
                Topic topic1 = new Topic();
                LiveData<Topic> topicWithId = viewModel.getTopicWithId(topic.getId());
                topicWithId.observe((LifecycleOwner) activity, topic2 -> {
                    if (topic2 != null) {
                        topic1.setId(topic2.getId());
                        topic1.setFirebaseImageNode(topic2.getFirebaseImageNode());
                        topic1.setImageUrl(topic2.getImageUrl());
                        topic1.setTitle(topic2.getTitle());
                        topic1.setDescription(topic2.getDescription());
                        topicWithId.removeObservers((LifecycleOwner) activity);
                    }
                });
                if (!fileTopic.exists()) {
                    int finalCount = count;
                    storageReference.child(FIREBASE_APP_IMAGES).child(topic.getFirebaseImageNode() + ".jpg")
                            .getFile(fileTopic)
                            .addOnSuccessListener(taskSnapshot -> {
                                topic.setLocalStorageUri(fileTopic.getAbsolutePath());
                                if (finalCount <6) getTopicItems(topic.getItems(), activity);
                                if (topic.equals(topic1)) viewModel.updateTopic(topic);
                                else viewModel.insertTopics(topic);
                                Log.d("Topic Image", "onSuccess: downloaded to " + fileTopic.getAbsolutePath());
                            })
                            .addOnFailureListener(e -> Log.d("Topic Image", "onFailure: " + topic.getFirebaseImageNode() + ".jpg" + e.toString()));
                } else {
                    topic.setLocalStorageUri(fileTopic.getAbsolutePath());
                    if (count<6) getTopicItems(topic.getItems(), activity);
                    if (topic.equals(topic1)) viewModel.updateTopic(topic);
                    else viewModel.insertTopics(topic);
                }

                topics.add(topic);
                viewModel.insertTopics(topic);
                Log.d("Topic ", topic.toString());
                count++;
                if (topics.size() == snapshot.getChildrenCount()) {
                    Log.d("Topics", "onDataChange: " + topics.toString());
                    topicRvAdapter.setItems(topics);
                    progressBar.setVisibility(View.GONE);
                }

            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {
            if (error.getCode() == DatabaseError.DISCONNECTED) {
                errorView.setText(R.string.no_internet);
            } else {
                errorView.setText(R.string.something_went_wrong);
            }
            progressBar.setVisibility(View.GONE);
        }
    });
}

'''

以下方法下载单个主题中包含的项目。 注意:有些主题有超过 200 个项目

'''

private void getTopicItems(ArrayList<Item> items, Activity activity){
    if (items != null) {
        for (Item item : items) {
            Item item1 = new Item();
            LiveData<Item> itemWithId = viewModel.getItemWithId(item.getId());
            itemWithId.observe((LifecycleOwner) activity, item2 -> {
                if (item2 != null) {
                    item1.setId(item2.getId());
                    item1.setImageUrl(item2.getImageUrl());
                    item1.setEnglishWord(item2.getEnglishWord());
                    item1.setCategory(item2.getCategory());
                    item1.setTopic(item2.getTopic());
                    item1.setRutooroWord(item2.getRutooroWord());
                    item1.setFirebaseImageNode(item2.getFirebaseImageNode());
                    itemWithId.removeObservers((LifecycleOwner) activity);

                }
            });
            // download the item image to storage;
            String itemNode = item.getFirebaseImageNode();
            File file = new File(activity.getFilesDir(), itemNode + ".jpg");
            if (itemNode != null) {
                if (!file.exists()) {
                    storageReference.child(FIREBASE_APP_IMAGES).child(itemNode + ".jpg")
                            .getFile(file)
                            .addOnSuccessListener(taskSnapshot -> {
                                item.setLocalStorageUri(file.getAbsolutePath());
                                if (item.equals(item1)) viewModel.updateItem(item);
                                else viewModel.insertItems(item);
                                Log.d("Topic Item Image", "onSuccess: downloaded to " + file.getAbsolutePath());
                            })
                            .addOnFailureListener(e -> Log.d("Topic Item Image", "onFailure: " + itemNode + ".jpg" + e.toString()));
                } else {
                    item.setLocalStorageUri(file.getAbsolutePath());
                    if (item.equals(item1)) viewModel.updateItem(item);
                    else viewModel.insertItems(item);
                }
            } else {
                if (item.equals(item1)) viewModel.updateItem(item);
                else viewModel.insertItems(item);
            }
        }
    }

}

'''

一两分钟后,应用程序崩溃并出现以下错误

''' E/AndroidRuntime:致命异常:main 进程:com.allez.san.myapplication,PID:18718 java.lang.OutOfMemoryError: 无法分配 24 字节分配,有 2000472 个空闲字节和 1953KB,直到 OOM,目标占用空间 201326592,增长限制 201326592;由于碎片而失败(最大可能的连续分配 150470656 字节) 在 java.lang.reflect.Executable.getMethodOrConstructorGenericInfoInternal(Executable.java:708) 在 java.lang.reflect.Executable.getGenericParameterTypes(Executable.java:270) 在 java.lang.reflect.Method.getGenericParameterTypes(Method.java:212) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper$BeanMapper.deserialize(CustomClassMapper.java:588) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper$BeanMapper.deserialize(CustomClassMapper.java:563) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertBean(CustomClassMapper.java:433) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.deserializeToClass(CustomClassMapper.java:232) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.deserializeToType(CustomClassMapper.java:179) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.deserializeToParameterizedType(CustomClassMapper.java:246) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.deserializeToType(CustomClassMapper.java:177) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.access$100(CustomClassMapper.java:48) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper$BeanMapper.deserialize(CustomClassMapper.java:593) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper$BeanMapper.deserialize(CustomClassMapper.java:563) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertBean(CustomClassMapper.java:433) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.deserializeToClass(CustomClassMapper.java:232) 在 com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertToCustomClass(CustomClassMapper.java:80) 在 com.google.firebase.database.DataSnapshot.getValue(DataSnapshot.java:203) 在 com.allez.san.learnrutooro.utils.DownloadUtil$3.onDataChange(DownloadUtil.java:296) 在 com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:75) 在 com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:63) 在 com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:55) 在 android.os.Handler.handleCallback(Handler.java:938) 在 android.os.Handler.dispatchMessage(Handler.java:99) 在 android.os.Looper.loop(Looper.java:246) 在 android.app.ActivityThread.main(ActivityThread.java:8512) 在 java.lang.reflect.Method.invoke(本机方法) 在 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1139) I/Process:发送信号。 PID:18718 SIG:9

'''

您正在并行下载所有文件,这意味着内存消耗将随着文件数量的增加而增加。要限制内存消耗,请一个一个地下载文件,或者使用合理的最大并行下载数。