Android - 在后台下载并存储数据到 Room

问题描述 投票:0回答:1

我的应用程序依赖于从网络下载数据。数据被下载并存储在 Room 中,但它可以随时更改,因此必须在每次应用程序启动时下载数据(如果互联网连接可用)。此外,数据显示在适配器中,因此位于与下载(MainActivity)不同的位置。问题是,即使数据没有改变,应用程序也会在下载、存储和读取过程中冻结。

我创建了以下课程:

道:

@Dao
public interface AppDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertHolidayDay(HolidayDay day);

    @Query("DELETE FROM holiday_day")
    void deleteAllHolidayDays();

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertHoliday(Holiday holiday);

    @Query("DELETE FROM holiday")
    void deleteAllHolidays();

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertFloatingHoliday(FloatingHoliday floatingHoliday);

    @Query("DELETE FROM floating_holiday")
    void deleteAllFloatingHolidays();
}

数据库:

@Database(entities = {Holiday.class, FloatingHoliday.class, HolidayDay.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract AppDao appDao();
}

存储库:

public class AppRepository {
    private final AppDao holidayDao;
    private final MediatorLiveData<List<HolidayDay>> mergedHolidays = new MediatorLiveData<>();

    private List<HolidayDay> fixedHolidaysCache;
    private List<FloatingHoliday> floatingHolidaysCache;

    public AppRepository(final Application application) {
        final AppDatabase database = Room.databaseBuilder(application, AppDatabase.class, "uhc")
                .enableMultiInstanceInvalidation()
                .allowMainThreadQueries()
                .build();
        holidayDao = database.appDao();
        loadHolidays();
    }

    @Transaction
    public void updateCalendarData(@NonNull final UnusualCalendar calendar) {
        holidayDao.deleteAllHolidays();
        holidayDao.deleteAllHolidayDays();
        holidayDao.deleteAllFloatingHolidays();
        calendar.getFixed()
                .stream()
                .map(HolidayDay::getHolidays)
                .flatMap(Collection::stream)
                .forEach(holidayDao::insertHoliday);
        calendar.getFixed().forEach(holidayDao::insertHolidayDay);
        calendar.getFloating().forEach(holidayDao::insertFloatingHoliday);
    }

    private void loadHolidays() {
        final LiveData<List<HolidayDay>> fixedHolidays = holidayDao.getAllHolidayDays();
        final LiveData<List<FloatingHoliday>> floatingHolidays = holidayDao.getAllFloatingHolidays();

        mergedHolidays.addSource(fixedHolidays, fixed -> {
            fixedHolidaysCache = fixed;
            mergeHolidays();
        });

        mergedHolidays.addSource(floatingHolidays, floating -> {
            floatingHolidaysCache = floating;
            mergeHolidays();
        });
    }

    private void mergeHolidays() {
        // magic here
    }
}

共享视图模型:

public class SharedViewModel extends ViewModel {
    public static final ViewModelInitializer<SharedViewModel> INITIALIZER = new ViewModelInitializer<>(SharedViewModel.class, creationExtras -> {
        final UHCApplication uhcApplication = (UHCApplication) creationExtras.get(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY);
        assert uhcApplication != null;
        return new SharedViewModel(uhcApplication.getAppRepository());
    });
    private final AppRepository repository;

    public SharedViewModel(final AppRepository repository) {
        this.repository = repository;
    }   return repository.getHoliday(id);

    public void updateData(final UnusualCalendar calendar) {
        CompletableFuture.runAsync(() -> repository.updateCalendarData(calendar));
    }
}

在MainActivity中下载:

public class MainActivity extends AppCompatActivity {
    private AlertDialog alertDialog;
    private final List<HolidayDay> holidayDays = new ArrayList<>();
    private MutableLiveData<Boolean> internet;
    private volatile boolean downloading;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        configureObservers();

        if (Boolean.TRUE.equals(internet.getValue()) && !downloading) {
            initiateDownload();
        }

        // Observe the holiday data
        sharedViewModel.getAllHolidayDays().observe(this, days -> {
            if (!days.isEmpty()) {
                holidayDays.addAll(days);
            } else if (Boolean.FALSE.equals(internet.getValue())) {
                showNoInternetAlert();
            }
        });
    }

    private void initiateDownload() {
        downloading = true;
        CompletableFuture.supplyAsync(new Downloader()) // just a HttpsUrlConnection + gson
                .thenAcceptAsync(data -> {
                    sharedViewModel.updateData(data);
                    downloading = false;
                });
    }

    private void configureObservers() {
        internet = new MutableLiveData<>(Util.isNetworkAvailable(this));
        internet.observe(this, isConnected -> {
            if (Boolean.TRUE.equals(isConnected) && alertDialog != null && alertDialog.isShowing() && !downloading) {
                CompletableFuture.supplyAsync(new Downloader.UnusualCalendarDownloader())
                        .thenAccept(sharedViewModel::updateData);
                alertDialog.dismiss();
            }
        });
        final ConnectivityManager connectivityManager =
                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        Util.NETWORK_CAPABILITIES.stream()
                .map(new NetworkRequest.Builder()::addTransportType)
                .map(NetworkRequest.Builder::build)
                .forEach(x -> connectivityManager.registerNetworkCallback(x, new ConnectivityManager.NetworkCallback() {
                    @Override
                    public void onAvailable(@NonNull final Network network) {
                        super.onAvailable(network);
                        internet.postValue(true);
                    }

                    @Override
                    public void onLost(@NonNull final Network network) {
                        super.onLost(network);
                        internet.postValue(false);
                    }
                }));
    }

    private void showNoInternetAlert() {
        final AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setTitle(R.string.no_internet_connection);
        alert.setCancelable(false);
        alert.setMessage(R.string.no_internet);
        alertDialog = alert.show();
    }
}

CompletableFuture
是从
java9.util.concurrent
导入的。我不能依赖
OnConflictStrategy.REPLACE
,因为条目有时会消失,我也想从数据库中删除它们。

我更喜欢的解决方案是让应用程序始终响应,并允许数据在存储和读取时动态更新(用户可以看到它们更新)。

我想过更新SharedViewModel中的列表,但没有解决冻结问题。

android android-room persistence freeze android-viewmodel
1个回答
0
投票

似乎有什么东西只是阻塞了主线程,很可能是与数据库相关的操作。默认情况下,Android 不允许您这样做,但例如,如果您在单独的线程中发出请求,但告诉主线程等待其完成,这可能会阻塞主线程。

© www.soinside.com 2019 - 2024. All rights reserved.