在 Android 9 (Pie) 上使用 Retrofit 服务时引用空对象?
Null object reference while using a Retrofit service on Android 9 (Pie)?
我正在使用改装从 "https://api.themoviedb.org/" 获取电影详细信息。该应用程序在 android 5 (Lolipop) 上运行良好,但在 Android 9 上崩溃。经过我的研究,我了解到 Android pie 默认仅使用 HTTPS。你可以清楚地看到上面的 URL 是加密的,所以,这应该不是问题。尽管如此,我还是创建了一个 网络安全配置 文件并将其包含在我的 AndroidManifest.xml
中
它仍然无法正常工作!!
我正在分享下面的代码 ->
MovieClient.java
public class MovieClient {
private static final String BASE_URL = "https://api.themoviedb.org/";
private Retrofit trendingMovieClient;
//Function for creating a client to receive movie data
public Retrofit getTrendingMovieClient(){
if(trendingMovieClient == null) {
trendingMovieClient = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return trendingMovieClient;
}
}
MovieService.java
public interface MovieService {
@GET("3/trending/all/day")
Call<MovieJsonData> getTrendingMovies(@Query("api_key") String apiKey);
@GET("3/discover/movie")
Call<MovieJsonData> getMostPopularMovies(@Query("api_key") String apiKey,
@Query("language") String language,
@Query("sort_by") String sortBy,
@Query("page") String page);
@GET("3/discover/movie")
Call<MovieJsonData> getTopRatedMovies(@Query("api_key") String apiKey,
@Query("language") String language,
@Query("sort_by") String sortBy,
@Query("page") String page,
@Query("vote_count.gte") String voteCountGte,
@Query("vote_count.lte") String voteCountLte,
@Query("vote_average.gte") String voteAverageGte);
@GET("3/movie/{movie_id}")
Call<MovieDetailsJson> getMovieDetails(@Path("movie_id") String movieId,
@Query("api_key") String apiKey,
@Query("language") String language);
}
MoviePresenter.java
public class MoviePresenter implements TrendingModule.Presenter{
private TrendingModule.View moduleView;
private MovieAdapter.ClickListener listener;
private MovieAdapter adapter;
private static final String API_KEY = "";
private String sort_key;
private static final String trending = "trending";
private static final String most_popular = "most_popular";
private static final String top_rated = "top_rated";
//POPULAR MOVIES
private static final String LANGUAGE_MOST_POPULAR = "en-US";
private static final String SORT_MOST_POPULAR = "popularity.desc";
private static final String PAGE_MOST_POPULAR = "1";
//TOP RATED MOVIES
private static final String LANGUAGE_TOP_RATED = "en-US";
private static final String SORT_TOP_RATED = "popularity.desc";
private static final String PAGE_TOP_RATED = "1";
private static final String VOTE_COUNT_GTE = "100";
private static final String VOTE_COUNT_LTE = "1500";
private static final String VOTE_AVERAGE_GTE = "8";
public MoviePresenter(TrendingModule.View view, MovieAdapter.ClickListener listener, String key){
this.moduleView = view;
this.listener = listener;
this.sort_key = key;
moduleView.initView();
initPresenter();
}
@Override
public void initPresenter() {
fetchData();
}
@Override
public void fetchData() {
moduleView.progressBarVisible();
MovieClient movieClient = new MovieClient();
Retrofit networkClient = movieClient.getTrendingMovieClient();
//Starting a service to fetch JSON data
MovieService trendingService = networkClient.create(MovieService.class);
Call<MovieJsonData> movieData = null;
//Fetching results through the service
if(sort_key == null || sort_key.equals(trending)) {
movieData = trendingService.getTrendingMovies(API_KEY);
}
else{
if(sort_key.equals(most_popular)){
movieData = trendingService.
getMostPopularMovies(
API_KEY,
LANGUAGE_MOST_POPULAR,
SORT_MOST_POPULAR,
PAGE_MOST_POPULAR);
}
else if(sort_key.equals(top_rated)){
movieData = trendingService.
getTopRatedMovies(
API_KEY,
LANGUAGE_TOP_RATED,
SORT_TOP_RATED,
PAGE_TOP_RATED,
VOTE_COUNT_GTE,
VOTE_COUNT_LTE,
VOTE_AVERAGE_GTE);
}
else{
moduleView.noResponse();
}
}
movieData.enqueue(new Callback<MovieJsonData>() {
@Override @EverythingIsNonNull
public void onResponse(Call<MovieJsonData> call, Response<MovieJsonData> response) {
moduleView.recyclerViewVisible();
MovieJsonData movieJsonData = response.body();
adapter = new MovieAdapter(movieJsonData, listener);
moduleView.displayMovieData(adapter);
}
@Override @EverythingIsNonNull
public void onFailure(Call<MovieJsonData> call, Throwable t) {
moduleView.noResponse();
}
});
}
}
network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:android="http://schemas.android.com/apk/res/android">
<domain-config
cleartextTrafficPermitted = "true">
<domain includeSubdomains = "true">themoviedb.org</domain>
</domain-config>
</network-security-config>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.moviesapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".view.Settings"
android:parentActivityName=".view.TrendingMovies" />
<activity android:name=".view.MovieDetail"
android:parentActivityName=".view.TrendingMovies"/>
<activity android:name=".view.TrendingMovies"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
错误 ->
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'void retrofit2.Call.enqueue(retrofit2.Callback)' on a null object reference
at com.example.moviesapp.presenter.MoviePresenter.fetchData(MoviePresenter.java:107)
当我使用默认的非空值初始化 movieData 时,问题已解决:
Call<MovieJsonData> movieData = null;
更改为 ->
Call<MovieJsonData> movieData = trendingService.getTrendingMovies(API_KEY);
我正在使用改装从 "https://api.themoviedb.org/" 获取电影详细信息。该应用程序在 android 5 (Lolipop) 上运行良好,但在 Android 9 上崩溃。经过我的研究,我了解到 Android pie 默认仅使用 HTTPS。你可以清楚地看到上面的 URL 是加密的,所以,这应该不是问题。尽管如此,我还是创建了一个 网络安全配置 文件并将其包含在我的 AndroidManifest.xml
中它仍然无法正常工作!!
我正在分享下面的代码 ->
MovieClient.java
public class MovieClient {
private static final String BASE_URL = "https://api.themoviedb.org/";
private Retrofit trendingMovieClient;
//Function for creating a client to receive movie data
public Retrofit getTrendingMovieClient(){
if(trendingMovieClient == null) {
trendingMovieClient = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return trendingMovieClient;
}
}
MovieService.java
public interface MovieService {
@GET("3/trending/all/day")
Call<MovieJsonData> getTrendingMovies(@Query("api_key") String apiKey);
@GET("3/discover/movie")
Call<MovieJsonData> getMostPopularMovies(@Query("api_key") String apiKey,
@Query("language") String language,
@Query("sort_by") String sortBy,
@Query("page") String page);
@GET("3/discover/movie")
Call<MovieJsonData> getTopRatedMovies(@Query("api_key") String apiKey,
@Query("language") String language,
@Query("sort_by") String sortBy,
@Query("page") String page,
@Query("vote_count.gte") String voteCountGte,
@Query("vote_count.lte") String voteCountLte,
@Query("vote_average.gte") String voteAverageGte);
@GET("3/movie/{movie_id}")
Call<MovieDetailsJson> getMovieDetails(@Path("movie_id") String movieId,
@Query("api_key") String apiKey,
@Query("language") String language);
}
MoviePresenter.java
public class MoviePresenter implements TrendingModule.Presenter{
private TrendingModule.View moduleView;
private MovieAdapter.ClickListener listener;
private MovieAdapter adapter;
private static final String API_KEY = "";
private String sort_key;
private static final String trending = "trending";
private static final String most_popular = "most_popular";
private static final String top_rated = "top_rated";
//POPULAR MOVIES
private static final String LANGUAGE_MOST_POPULAR = "en-US";
private static final String SORT_MOST_POPULAR = "popularity.desc";
private static final String PAGE_MOST_POPULAR = "1";
//TOP RATED MOVIES
private static final String LANGUAGE_TOP_RATED = "en-US";
private static final String SORT_TOP_RATED = "popularity.desc";
private static final String PAGE_TOP_RATED = "1";
private static final String VOTE_COUNT_GTE = "100";
private static final String VOTE_COUNT_LTE = "1500";
private static final String VOTE_AVERAGE_GTE = "8";
public MoviePresenter(TrendingModule.View view, MovieAdapter.ClickListener listener, String key){
this.moduleView = view;
this.listener = listener;
this.sort_key = key;
moduleView.initView();
initPresenter();
}
@Override
public void initPresenter() {
fetchData();
}
@Override
public void fetchData() {
moduleView.progressBarVisible();
MovieClient movieClient = new MovieClient();
Retrofit networkClient = movieClient.getTrendingMovieClient();
//Starting a service to fetch JSON data
MovieService trendingService = networkClient.create(MovieService.class);
Call<MovieJsonData> movieData = null;
//Fetching results through the service
if(sort_key == null || sort_key.equals(trending)) {
movieData = trendingService.getTrendingMovies(API_KEY);
}
else{
if(sort_key.equals(most_popular)){
movieData = trendingService.
getMostPopularMovies(
API_KEY,
LANGUAGE_MOST_POPULAR,
SORT_MOST_POPULAR,
PAGE_MOST_POPULAR);
}
else if(sort_key.equals(top_rated)){
movieData = trendingService.
getTopRatedMovies(
API_KEY,
LANGUAGE_TOP_RATED,
SORT_TOP_RATED,
PAGE_TOP_RATED,
VOTE_COUNT_GTE,
VOTE_COUNT_LTE,
VOTE_AVERAGE_GTE);
}
else{
moduleView.noResponse();
}
}
movieData.enqueue(new Callback<MovieJsonData>() {
@Override @EverythingIsNonNull
public void onResponse(Call<MovieJsonData> call, Response<MovieJsonData> response) {
moduleView.recyclerViewVisible();
MovieJsonData movieJsonData = response.body();
adapter = new MovieAdapter(movieJsonData, listener);
moduleView.displayMovieData(adapter);
}
@Override @EverythingIsNonNull
public void onFailure(Call<MovieJsonData> call, Throwable t) {
moduleView.noResponse();
}
});
}
}
network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config xmlns:android="http://schemas.android.com/apk/res/android">
<domain-config
cleartextTrafficPermitted = "true">
<domain includeSubdomains = "true">themoviedb.org</domain>
</domain-config>
</network-security-config>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.moviesapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".view.Settings"
android:parentActivityName=".view.TrendingMovies" />
<activity android:name=".view.MovieDetail"
android:parentActivityName=".view.TrendingMovies"/>
<activity android:name=".view.TrendingMovies"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
错误 ->
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'void retrofit2.Call.enqueue(retrofit2.Callback)' on a null object reference
at com.example.moviesapp.presenter.MoviePresenter.fetchData(MoviePresenter.java:107)
当我使用默认的非空值初始化 movieData 时,问题已解决:
Call<MovieJsonData> movieData = null;
更改为 ->
Call<MovieJsonData> movieData = trendingService.getTrendingMovies(API_KEY);