如何在 MVVM 中进行新的网络调用?

How to make NEW Networking Calls in MVVM?

我正在尝试使用 MVVM(和 recyclerview)实现下拉刷新,但我不明白我应该如何获取新数据。应用程序的初始加载很好,因为我只是在创建视图模型时观察它的实时数据,但是我如何查询更多数据?

MainActivity.java

package com.example.simplenews;

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.example.simplenews.adapters.NewsArticleAdapter;
import com.example.simplenews.adapters.RecyclerItemClickListener;
import com.example.simplenews.models.Article;
import com.example.simplenews.models.NewsResponse;
import com.example.simplenews.repositories.NewsAPI;
import com.example.simplenews.repositories.NewsRepository;
import com.example.simplenews.viewmodels.NewsViewModel;
import com.victor.loading.rotate.RotateLoading;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;

import timber.log.Timber;

public class MainActivity extends AppCompatActivity {
    private RecyclerView newsRecyclerView;
    private NewsArticleAdapter newsAdapter;
    private NewsAPI NewsAPI;
    private ArrayList<Article> newsArticles = new ArrayList<>();
    private RotateLoading rotateLoadingIndicator;
    private SwipeRefreshLayout swipeRefreshLayout;
    private NewsViewModel newsViewModel;



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//        Planting timber debug tree here because this joint refuses to work when planted in the application class
        Timber.plant(new Timber.DebugTree());


        swipeRefreshLayout = findViewById(R.id.swipe_refresh_layout);
        newsRecyclerView = findViewById(R.id.newsRecyclerView);
        rotateLoadingIndicator = findViewById(R.id.rotate_loading_indicator);

//        Getting and setting up the viewmodel
        newsViewModel = new ViewModelProvider(this).get(NewsViewModel.class);
        newsViewModel.initNewsViewModel();

//        Setting up the observer
        newsViewModel.getNewsRepositoryQuery().observe(this, newsResponse -> {
            ArrayList<Article> freshNewsArticles = (ArrayList<Article>) newsResponse.getArticles();
            newsArticles.addAll(freshNewsArticles);
            newsAdapter.notifyDataSetChanged();
        });


        initReyclerView();


//        This is not the way to do recyclerview click listeners but this will suffice for now
        newsRecyclerView.addOnItemTouchListener(
                new RecyclerItemClickListener(this, newsRecyclerView, new RecyclerItemClickListener.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        Article article = newsArticles.get(position);
                        Uri uri = Uri.parse(article.getUrl());
                        Intent webIntent = new Intent(Intent.ACTION_VIEW, uri);
                        startActivity(webIntent);
                    }

                    @Override
                    public void onLongItemClick(View view, int position) {
                    }
                })
        );

        // Configure the refreshing colors
        swipeRefreshLayout.setColorSchemeColors(getResources().getColor(R.color.colorPrimary));

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                newsViewModel.getNewHeadlines().observe(MainActivity.this, new Observer<NewsResponse>() {
                    @Override
                    public void onChanged(NewsResponse newsResponse) {
                        if (newsResponse.getArticles() != null) {
                            refreshNewsRecyclerView(newsResponse.getArticles());
                            swipeRefreshLayout.setRefreshing(false);
                        }


                        swipeRefreshLayout.setRefreshing(false);
                        Timber.d("the articles in the refresh callback were null");
                    }
                });

            }
        });
    }


    /*
     * Helper method that refreshes topHeadlinesRecyclerView with new articles
     * @param: list of new article objects from a network request
     * */
    private void refreshNewsRecyclerView(List<Article> freshArticles) {
        newsRecyclerView.setVisibility(View.INVISIBLE);
        showLoadingIndicator();
        newsAdapter.clearNewsArticles();
        newsAdapter.addAll(freshArticles);
        newsRecyclerView.setVisibility(View.VISIBLE);
        hideLoadingIndicator();
        newsAdapter.notifyDataSetChanged();
    }

    /*
     * Helper method to show the loading indicator
     * */
    private void showLoadingIndicator() {
        rotateLoadingIndicator.setVisibility(View.VISIBLE);
        rotateLoadingIndicator.start();
    }

    /*
     * Helper method to hide loading indicator
     * */
    private void hideLoadingIndicator() {
        rotateLoadingIndicator.stop();
        rotateLoadingIndicator.setVisibility(View.GONE);
    }

    /*
     * Helper method to setup the recyclerView
     * */
    private void initReyclerView() {
        if (newsAdapter == null) {
            showLoadingIndicator();
            newsAdapter = new NewsArticleAdapter(newsArticles, this);
            RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
            newsRecyclerView.setLayoutManager(layoutManager);
            newsRecyclerView.setAdapter(newsAdapter);
            hideLoadingIndicator();
        } else {
            newsAdapter.notifyDataSetChanged();
        }

    }

}

NewsViewModel

public class NewsViewModel extends ViewModel {

    private MutableLiveData<NewsResponse> mutableLiveData;

    
    private NewsRepository newsRepository;

    //    When a viewmodel object is created fetch the data needed for the activitiy
    public void initNewsViewModel() {
        if (mutableLiveData != null) {
            return;
        }
        newsRepository = NewsRepository.getInstance();
        mutableLiveData = newsRepository.getTopHeadlines();

    }


    public MutableLiveData<NewsResponse> getNewsRepositoryQuery() {
        return mutableLiveData;
    }

    public MutableLiveData<NewsResponse> getNewHeadlines() {
        MutableLiveData<NewsResponse> response = newsRepository.getTopHeadlines();
        return response;
    }


}

新闻库

public class NewsRepository {
    private static NewsRepository newsRepository;
    private NewsAPI newsAPI;




    private List<Article> freshArticles;


    public static NewsRepository getInstance() {
        if (newsRepository == null) {
            newsRepository = new NewsRepository();
        }
        return newsRepository;
    }

    /*
     * Private constructor because nobody should be creating this object direcly
     * */
    private NewsRepository() {
        newsAPI = RetrofitClient.getRetrofitInstance().create(NewsAPI.class);
    }

    public MutableLiveData<NewsResponse> getTopHeadlines() {
        MutableLiveData<NewsResponse> topHeadlines = new MutableLiveData<>();
        newsAPI.getRootJSONObject().enqueue(new Callback<NewsResponse>() {
            @Override
            public void onResponse(Call<NewsResponse> call, Response<NewsResponse> response) {
                if (response.isSuccessful()) {
                    topHeadlines.setValue(response.body());
                    Timber.d("Network call was succesful here is the response code " + response.code());
                } else {
                    Timber.d("Network call was unsuccesful " + response.code());
                }
            }

            @Override
            public void onFailure(Call<NewsResponse> call, Throwable t) {
                Timber.d("Network call completely failed lol");
                topHeadlines.setValue(null);
            }
        });
        return topHeadlines;

    }



}

您可以简单地创建一个函数来重置 MutableLiveData 的值 例如,在滑动调用 viewmodel.resetNewsHeadlines() 和 resetNewsHeadlines() 方法中,简单地将值设置为 null 并调用 mutableLiveData = newsRepository.getTopHeadlines();再次