Я использую шаблон репозитория в моем Android Архитектура приложения . Мой репозиторий с именем NewsRepository извлекает данные из API используя Retrofit , а затем сохраните данные в Database , используя Room liberary. Вот мой файл NewsRepository .
public class NewsRepository {
private final String TAG = NewsRepository.class.getSimpleName();
//Creating a NewsApiService instance
private NewsApiService newsApiService;
//Creating a NewsViewModel instance
private NewsViewModel newsViewModel;
//Creating an array
private String fragment[];
//Creating an Executor instance
private Executor DBExecutor;
//Creating a Retrofit instance
private Retrofit retrofit = null;
//Creating a NewsDatabase instance
private NewsDatabase mNewsDatabase;
//A reference to the (@link NewsDao} class
private NewsDao mNewsDao;
//Creating a MainActivity instance
private MainActivity mainActivity ;
//Creating an Observable instance
private Observable<News> DBObservable;
//Creating a Subscriber instance
private Subscriber<News> DBOSubscriber;
public NewsRepository(@NonNull Application application){
//Initializing MainActivity instance
mainActivity = new MainActivity();
//Getting the retrofit instance
retrofit = getRetrofitInstance();
//Configure the NewsApiService class with retrofit to receive the response
newsApiService = retrofit.create(NewsApiService.class);
//Retrieving the Database instance
mNewsDatabase = NewsDatabase.getDatabase(application);
//Retrieving the NewDao instance
mNewsDao = mNewsDatabase.newsDao();
//Creating a string array of fragment titles
fragment = new String[] {CATEGORY_LATEST, CATEGORY_BUSINESS, CATEGORY_SCIENCE,
CATEGORY_TECHNOLOGY,
CATEGORY_HEALTH, CATEGORY_SPORTS, CATEGORY_ENTERTAINMENT};
}
/**
* This method will setup the data for the view model
*/
public void setupData(int category){
Log.i(TAG, "Setting up the data");
if (category > 6) {
MainActivity.loadingState.setValue(MainActivity.LoadingState.LOADING_COMPLETE);;
}else {
connectGetAndPersistData(category);
}
}
/**
* Utility to setup Observable and Subscriber
*/
private List<News> getCategoricalNews(String category){
mNewsDao.getCategoricalNews(category).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<News>>(){
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(List<News> news) {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
}
}
/**
* This utility will establish the connection with the server and retrieve the data from the specified URL.
* @param category
*/
private void connectGetAndPersistData(int category){
Log.i(TAG, "Connecting with the URL......");
Call<NewsApiResponse> call;
//Call appropriate methods according to the category
if (fragment[category].equals(CATEGORY_LATEST))
call = newsApiService.getTopHeadlines(country, apiKey);
else
call = newsApiService.getTopHeadlines(fragment[category], country, apiKey);
Log.i(TAG, "Connecting with " + category + " headlines URL");
call.enqueue(new Callback<NewsApiResponse>() {
@Override
public void onResponse(Call<NewsApiResponse> call, Response<NewsApiResponse> response) {
//In-case if response body is null
if (response.body() == null){
try {
Log.d(TAG, "Error response body" + response.errorBody().string());
} catch (IOException e) {
e.printStackTrace();
}
}
//Retrieving the articles list from the response body
List<com.example.megaflip.Model.FetchModel.News> news = response.body().getArticles();
Log.i(TAG, "Successfully get the response consist of list of size " + news.size());
//Inserting the news list to the database
persistData(news, fragment[category]);
Log.i(TAG, "Data is persisted successfully for the "+ category + " URL");
MainActivity.loadingState.setValue(MainActivity.LoadingState.LOAD_NEWS);
}
@Override
public void onFailure(Call<NewsApiResponse> call, Throwable t) {
Log.e(TAG, t.toString());
}
});
}
/**
* This utility will persist the data into the local database
* @param news
* @param category
*/
private void persistData(List<com.example.megaflip.Model.FetchModel.News> news, String category){
Log.i(TAG, "Preparing to persist the data.....");
com.example.megaflip.Model.FetchModel.News newsItem;
for (int count=0; count<news.size(); count++){
newsItem = news.get(count);
mNewsDao.insert(new News(category, newsItem.getSource(), newsItem.getAuthor(),
newsItem.getTitle(), newsItem.getDescription(),
newsItem.getUrl(), newsItem.getUrlToImage(), newsItem.getPublishedAt(),
newsItem.getContent()));
}
Log.i(TAG, "Successfully inserted the " + category + "URL data of size " + news.size());
}
public void deleteData(){
mNewsDao.deleteAll();
}
/**
* This utility will create new retrofit instance
*/
public Retrofit getRetrofitInstance(){
//Creating a Retrofit instance from the base url
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Теперь проблема в том, что функция getCategoricalNews (String category) извлекает новости указанной категории из базы данных, используя Rx Java liberary и в метод обратного вызова onNext Мне нужно установить news в адаптер с именем NewsListAdapter , используя метод в классе NewsListAdapter , помеченный как setNews . Вот класс NewsListAdapter .
public class NewsListAdapter extends RecyclerView.Adapter<NewsListAdapter.NewsViewHolder> {
private List<com.example.megaflip.Model.FetchModel.News> newsList;
private LayoutInflater layoutInflater;
private Context context;
//This constructor initializes the list
public NewsListAdapter( Context context ){
//Inflate the layout for the specified context
layoutInflater = LayoutInflater.from( context );
this.context = context;
}
@NonNull
@Override
public NewsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Log.i(getClass().getName(), "inflated");
//Inflate the list item view for each recycler view item
View itemView = layoutInflater.inflate( R.layout.news_list_item, parent , false);
return new NewsViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull NewsViewHolder holder, int position) {
//In-case when data is not being ready yet
if ( newsList!= null ) {
//Getting the pet data at the specified place in the list
com.example.megaflip.Model.FetchModel.News news = newsList.get(position);
//Load the picture by the url and set it to the source picture image view
Log.v("MainActivity", "Before picasso");
Picasso.get().load(news.getUrlToImage()) .
into(holder.sourcePictureImageView);
Log.v("ImageUrl", news.getUrlToImage());
Log.v("MainActivity", "Before picasso");
holder.newsTitleTextView.setText(news.getTitle());
holder.sourceNameTextView.setText(news.getSource().getName());
}
}
@Override
public int getItemCount() {
if(newsList != null){
Log.v("ViewModel: ", "List size = "+Integer.toString(newsList.size()));
return newsList.size();
}
Log.v("ViewModel: ", "Returning empty list.");
return 0;
}
public class NewsViewHolder extends RecyclerView.ViewHolder{
//All the Text views holder
ImageView sourcePictureImageView;
AppCompatTextView sourceNameTextView;
AppCompatTextView newsTitleTextView;
public NewsViewHolder(@NonNull View itemView) {
super(itemView);
//Find the Text views by the corresponding id's
sourcePictureImageView = (ImageView) itemView.findViewById( R.id.source_picture_image_view );
sourceNameTextView = (AppCompatTextView) itemView.findViewById( R.id.source_name_text_view);
newsTitleTextView = (AppCompatTextView) itemView.findViewById( R.id.news_title_TextView_view );
}
}
//Update the pet list to the changed data
public void setNews( List<News> newsList )
{
this.newsList = newsList;
Log.v("NewsListAdapter: ", "Assigning new list of size = "+newsList.size());
notifyDataSetChanged();
}
}
Фрагмент с именем LatestFragment содержит экземпляр класса NewsListAdapter , и мне нужно настроить newsListAdapter с recyclerView . Вот файл LatestFragament .
public class LatestFragment extends Fragment {
private static final String TAG = LatestFragment.class.getSimpleName();
public enum LoadingState{
LOAD_NEWS,
LOAD_CONTINUE, //Indicates that news should be load
LOADING_COMPLETE //Indicates that news is loaded successfully
}
public static MutableLiveData<MainActivity.LoadingState> loadingState = new MutableLiveData<>(MainActivity.LoadingState.LOAD_NEWS);
public static final String NEWS_CATEGORY = "latest";
private NewsViewModel mNewsViewModel;
//Creating a recycler view instance
private RecyclerView recyclerView;
//Creating a NewsListAdapter instance
private NewsListAdapter mNewsListAdapter;
public static LatestFragment newInstance(){ return new LatestFragment(); }
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Retrieve the existing news view model
mNewsViewModel = new ViewModelProvider(this).get(NewsViewModel.class);
}
@Override
public View onCreateView(final LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.list_view, container, false);
return rootView;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
mNewsViewModel = new ViewModelProvider(requireActivity()).get(NewsViewModel.class);
RecyclerView recyclerView = view.findViewById(R.id.recycler_view);
NewsListAdapter newsListAdapter = new NewsListAdapter(getContext());
recyclerView.setAdapter(newsListAdapter);
}
/**
* Method to get the news view model instance
*/
public ViewModel getNewViewModel(){ return mNewsViewModel; } ;
}
Как передать список новостей из onNext метода обратного вызова newsListAdapter , потому что onNext находится в репозиторий и newsListAdapter находится в LatestFragment или как передать данные списка news в LatestFragment Вот мой код ViewModel с именем NewsViewModel .
public class NewsViewModel extends AndroidViewModel {
private static final String TAG = NewsViewModel.class.getSimpleName();
//Creating a NewsRepositoryInstance
private NewsRepository newsRepository;
public NewsViewModel(@NonNull Application application) {
super(application);
newsRepository = new NewsRepository(application);
}
/**
* This method will prepare data to be used to display into the views for the user
*/
public void prepareData(int category){
Log.i(TAG, "Preparing the data");
newsRepository.setupData(category);
}
public void deleteAllData(){
Log.i(TAG, "Deleting the data");
newsRepository.deleteData();
}
Какой должна быть схема для этого? Что нужно сделать и чего мне не хватает? Ваша помощь будет поощряться.