我的应用程序依赖于从网络下载数据。数据被下载并存储在 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 不允许您这样做,但例如,如果您在单独的线程中发出请求,但告诉主线程等待其完成,这可能会阻塞主线程。