I am trying to write unit tests for Android app that uses dagger for ViewModel injection. I don't seem to get injection working for the JUnitTest. Retrofit is returning null call request when I inject the gapiService using #Mock here:
final MutableLiveData<GissuesResponse> lData = new MutableLiveData<>();
Call<List<Issue>> callRequest = gapiService.getIssues(owner, repo);
My whole project is at http://github.com/CodingWords/Gissues
Not sure how to properly setup the ViewModel for injection in JUnitTest. Thanks!
Here is my JUnitTest
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import com.codingwords.sobrien.gissues.api.GAPIService;
import com.codingwords.sobrien.gissues.model.SearchIssuesModel;
import com.codingwords.sobrien.gissues.repo.IssueRepository;
import com.codingwords.sobrien.gissues.vm.GissuesViewModel;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import javax.inject.Inject;
public class GissuesUnitTest {
#Mock //Mock annotation tells Mockito to give a mocked object
GissuesScreen gissuesScreen;
#Mock
GAPIService gapiService;
//class that is being tested
#Inject
ViewModelProvider.Factory viewModelFactory;
IssueRepository gissuesRepo;
GissuesViewModel gissuesViewModel;
final String dummyOwner = "ethereum";
final String dummyRepo = "solidity";
#Before
public void setupGissuesViewModel(){
//this function will be called before all tests are run
// call this function to init all objects annotated with #mock
MockitoAnnotations.initMocks(this);
gissuesRepo = new IssueRepository();
gissuesRepo.setGapiService(gapiService);
gissuesViewModel = new GissuesViewModel(gissuesRepo);
gissuesViewModel.setGissuesScreen(gissuesScreen);
SearchIssuesModel sim = new SearchIssuesModel();
gissuesViewModel.setSearchModel(sim);
//we create an instance of the class to be tested by passing the mocked objec
}
#Test
public void requestIssuesWithEmptyOwner_showsOwnerError(){
gissuesViewModel.getSearchModel().setOwner("");
gissuesViewModel.pullIssues();
//use mockito to verify that the showOwnerError() method is called in the screen object
Mockito.verify(gissuesScreen).showOwnerError();
}
#Test
public void requestIssuesWithEmptyRepo_showsRepoError(){
gissuesViewModel.getSearchModel().setOwner("ethereum");
gissuesViewModel.getSearchModel().setRepo("");
gissuesViewModel.pullIssues();
Mockito.verify(gissuesScreen).showRepoError();
}
#Test
public void requestIssuesWithEmptyList_showsIssuesNotFound(){
gissuesViewModel.getSearchModel().setOwner("ethereum");
gissuesViewModel.getSearchModel().setRepo("ethereum");
gissuesViewModel.pullIssues();
Mockito.verify(gissuesScreen).showIssuesNotFound();
}
}
Here is IssueRepo
public class IssueRepository implements IssueRepo {
#Inject
public IssueRepository() {
}
#Inject
GAPIService gapiService;
#Override
public LiveData<GissuesResponse> receiveIssues(String owner, String repo) {
final MutableLiveData<GissuesResponse> lData = new MutableLiveData<>();
// callRequest is returned as null because gapiService was mock object?
Call<List<Issue>> callRequest = gapiService.getIssues(owner, repo);
callRequest.enqueue(new Callback<List<Issue>>() {
#Override
public void onResponse(Call<List<Issue>> call, Response<List<Issue>> response) {
lData.setValue(new GissuesResponse(response.body()));
}
#Override
public void onFailure(Call<List<Issue>> call, Throwable t) {
lData.setValue(new GissuesResponse(t));
}
});
return lData;
}
public GAPIService getGapiService() {
return gapiService;
}
public void setGapiService(GAPIService gapiService) {
this.gapiService = gapiService;
}
}
Here is View Model
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MediatorLiveData;
import android.arch.lifecycle.ViewModel;
import android.support.annotation.NonNull;
import com.codingwords.sobrien.gissues.GissuesScreen;
import com.codingwords.sobrien.gissues.entity.GissuesResponse;
import com.codingwords.sobrien.gissues.model.SearchIssuesModel;
import com.codingwords.sobrien.gissues.repo.IssueRepo;
import javax.inject.Inject;
/**
* Created by Administrator on 2/19/2018.
*/
public class GissuesViewModel extends ViewModel {
private IssueRepo issueRepository;
private MediatorLiveData<GissuesResponse> gapiResponse;
private SearchIssuesModel searchModel;
private GissuesScreen gissuesScreen;
#Inject
public GissuesViewModel(IssueRepo repository) {
this.issueRepository = repository;
this.gapiResponse = new MediatorLiveData<GissuesResponse>();
}
public MediatorLiveData<GissuesResponse> getGapiResponse() {
return gapiResponse;
}
public void pullIssues(){
if (getSearchModel() != null){
if ((getSearchModel().getOwner() == null) || (getSearchModel().getOwner().length() < 2)){
gissuesScreen.showOwnerError();
} else if ((getSearchModel().getRepo() == null) || (getSearchModel().getRepo().length() < 2)) {
gissuesScreen.showRepoError();
} else {
pullIssues(getSearchModel().getOwner(), getSearchModel().getRepo());
}
}
}
public void pullIssues(#NonNull String user, String repo) {
LiveData<GissuesResponse> issuesSource = issueRepository.receiveIssues(user, repo);
gapiResponse.addSource(
issuesSource,
gapiResponse -> {
if (this.gapiResponse.hasActiveObservers()) {
this.gapiResponse.removeSource(issuesSource);
}
this.gapiResponse.setValue(gapiResponse);
}
);
}
public SearchIssuesModel getSearchModel() {
return searchModel;
}
public void setSearchModel(SearchIssuesModel searchModel) {
this.searchModel = searchModel;
}
public GissuesScreen getGissuesScreen() {
return gissuesScreen;
}
public void setGissuesScreen(GissuesScreen gissuesScreen) {
this.gissuesScreen = gissuesScreen;
}
}
Related
I was creating a simple reminder app with room database based on a youtube tutorial.
but it ends with java.lang.NullPointerException: Attempt to invoke virtual method 'com.dev.alarmreminder.Database.EventDao com.dev.alarmreminder.Database.DatabaseClass.EventDao()' on a null object reference.
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'com.dev.alarmreminder.Database.EventDao com.dev.alarmreminder.Database.DatabaseClass.EventDao()' on a null object reference
at com.dev.alarmreminder.MainActivity.setAdapter(MainActivity.java:49)
at com.dev.alarmreminder.MainActivity.onResume(MainActivity.java:43)
I don't get it why it's called nullpointerexception. tried but it didn't get it.
DatabaseClass.java
#Database(entities = {EntityClass.class}, version = 1)
public abstract class DatabaseClass extends RoomDatabase
{
public abstract EventDao EventDao();
private static DatabaseClass INSTANCE;
static DatabaseClass getDatabase(final Context context)
{
if(INSTANCE == null)
{
synchronized (DatabaseClass.class)
{
if(INSTANCE == null)
{
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), DatabaseClass.class, "product_database").build();
}
}
}
return INSTANCE;
}
}
EventDao.java
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
#Dao
public interface EventDao
{
#Insert
void insertAll(EntityClass entityClass);
#Query("SELECT * FROM myTable")
List<EntityClass> getAllData();
}
EntityClass.java
import androidx.room.Entity;
import androidx.room.PrimaryKey;
#Entity(tableName = "myTable")
public class EntityClass
{
#PrimaryKey(autoGenerate = true)
int id;
String eventname;
String eventdate;
String eventtime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEventname() {
return eventname;
}
public void setEventname(String eventname) {
this.eventname = eventname;
}
public String getEventdate() {
return eventdate;
}
public void setEventdate(String eventdate) {
this.eventdate = eventdate;
}
public String getEventtime() {
return eventtime;
}
public void setEventtime(String eventtime) {
this.eventtime = eventtime;
}
}
MainACtivity
package com.mahidev.alarmreminder;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.mahidev.alarmreminder.Adapter.EventAdapter;
import com.mahidev.alarmreminder.Database.DatabaseClass;
import com.mahidev.alarmreminder.Database.EntityClass;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
Button createEvent;
EventAdapter eventAdapter;
RecyclerView recyclerView;
DatabaseClass databaseClass;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createEvent = findViewById(R.id.btn_createEvent);
recyclerView = findViewById(R.id.recyclerview);
createEvent.setOnClickListener(this);
}
#Override
protected void onResume() {
super.onResume();
setAdapter();
}
private void setAdapter()
{
List<EntityClass> classList = databaseClass.EventDao().getAllData();
eventAdapter = new EventAdapter(getApplicationContext(), classList);
recyclerView.setAdapter(eventAdapter);
}
#Override
public void onClick(View view)
{
if(view == createEvent)
{
goToCreateEventActivity();
}
}
private void goToCreateEventActivity()
{
Intent intent = new Intent(getApplicationContext(), CreateEvent.class);
startActivity(intent);
}
}
issue here is the first line in setAdapter method.
You haven't instantiated databaseClass as such it is null and hence the exception.
You need to add a line (in the onCreate method, immediately after the setContentView call would be a suitable place)
databaseClass = Database.getDatabase(this);
I'm trying to implement pull to refresh with MVVM (and a recyclerview) yet I don't understand how I'm supposed to fetch new data. Inital load up of the app is fine as I'm just observing the livedata from the view model when it's created, but how do I query for more data?
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;
}
}
News Repository
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;
}
}
You can simply make a function which reset value of MutableLiveData
For example on swipe call viewmodel.resetNewsHeadlines() and in resetNewsHeadlines() method simple set value to null and recall mutableLiveData = newsRepository.getTopHeadlines(); again
I have some JSON below.
{
"Table": [
{
"CMBL009001": "010001",
"NMBL009002": 0,
"CMBL009003": "",
"CMBL009004": "",
"CMBL009005": "",
"CMBL009006": "",
"NMBL009007": 0,
"BMBL009008": 0,
"NMBL009009": 0,
"CMBL009010": "ADMIN",
"CMBL009011": "",
"NMBL009012": 2,
"NMBL009013": 1
}
]
}
this is my model:
package com.example.showmyjsonapplicationuser;
import com.google.gson.annotations.SerializedName;
public class User {
#SerializedName("BMBL009008")
private Long mBMBL009008;
#SerializedName("CMBL009001")
private String mCMBL009001;
#SerializedName("CMBL009003")
private String mCMBL009003;
#SerializedName("CMBL009004")
private String mCMBL009004;
#SerializedName("CMBL009005")
private String mCMBL009005;
#SerializedName("CMBL009006")
private String mCMBL009006;
#SerializedName("CMBL009010")
private String mCMBL009010;
#SerializedName("CMBL009011")
private String mCMBL009011;
#SerializedName("NMBL009002")
private Long mNMBL009002;
#SerializedName("NMBL009007")
private Long mNMBL009007;
#SerializedName("NMBL009009")
private Long mNMBL009009;
#SerializedName("NMBL009012")
private Long mNMBL009012;
#SerializedName("NMBL009013")
private Long mNMBL009013;
public Long getBMBL009008() {
return mBMBL009008;
}
public void setBMBL009008(Long bMBL009008) {
mBMBL009008 = bMBL009008;
}
public String getCMBL009001() {
return mCMBL009001;
}
public void setCMBL009001(String cMBL009001) {
mCMBL009001 = cMBL009001;
}
public String getCMBL009003() {
return mCMBL009003;
}
public void setCMBL009003(String cMBL009003) {
mCMBL009003 = cMBL009003;
}
public String getCMBL009004() {
return mCMBL009004;
}
public void setCMBL009004(String cMBL009004) {
mCMBL009004 = cMBL009004;
}
public String getCMBL009005() {
return mCMBL009005;
}
public void setCMBL009005(String cMBL009005) {
mCMBL009005 = cMBL009005;
}
public String getCMBL009006() {
return mCMBL009006;
}
public void setCMBL009006(String cMBL009006) {
mCMBL009006 = cMBL009006;
}
public String getCMBL009010() {
return mCMBL009010;
}
public void setCMBL009010(String cMBL009010) {
mCMBL009010 = cMBL009010;
}
public String getCMBL009011() {
return mCMBL009011;
}
public void setCMBL009011(String cMBL009011) {
mCMBL009011 = cMBL009011;
}
public Long getNMBL009002() {
return mNMBL009002;
}
public void setNMBL009002(Long nMBL009002) {
mNMBL009002 = nMBL009002;
}
public Long getNMBL009007() {
return mNMBL009007;
}
public void setNMBL009007(Long nMBL009007) {
mNMBL009007 = nMBL009007;
}
public Long getNMBL009009() {
return mNMBL009009;
}
public void setNMBL009009(Long nMBL009009) {
mNMBL009009 = nMBL009009;
}
public Long getNMBL009012() {
return mNMBL009012;
}
public void setNMBL009012(Long nMBL009012) {
mNMBL009012 = nMBL009012;
}
public Long getNMBL009013() {
return mNMBL009013;
}
public void setNMBL009013(Long nMBL009013) {
mNMBL009013 = nMBL009013;
}
}
this is my retrofit class:
package com.example.showmyjsonapplicationuser;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitSingleton {
private static Retrofit retrofit;
public static Retrofit getInstance() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.200.10:6139/api/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
private RetrofitSingleton() {
}
}
this is provider class:
package com.example.showmyjsonapplicationuser.providers;
import com.example.showmyjsonapplicationuser.ApiService;
import com.example.showmyjsonapplicationuser.RetrofitSingleton;
public class ApiServiceProvider {
private static ApiService apiService;
public static ApiService provideApiService() {
if (apiService == null) {
apiService = RetrofitSingleton.getInstance().create(ApiService.class);
}
return apiService;
}
}
this is jsonUserResponse:
package com.example.showmyjsonapplicationuser;
public class JSONUserResponse {
private User[] Table;
public User[] getTable(){
return Table;
}
}
this is apiService:
package com.example.showmyjsonapplicationuser;
import retrofit2.Call;
import retrofit2.http.GET;
public interface ApiService {
#GET("values?url=<NewDataSet><Table><ver>1_02.01.06</ver><proc>003TOTALSELECT</proc><P1>ADMIN</P1><P2>123456</P2><P3>MBLTYPEVISIT1</P3></Table></NewDataSet>")
Call<JSONUserResponse> getUsersJSON();
}
this is mainViewModel:
`package com.example.showmyjsonapplicationuser;
import com.example.showmyjsonapplicationuser.providers.ApiServiceProvider;
import retrofit2.Call;
public class MainViewModel {
private ApiService apiService = ApiServiceProvider.provideApiService();
Call<JSONUserResponse> callUser = apiService.getUsersJSON();
}
this in main activity:
package com.example.showmyjsonapplicationuser;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity {
private MainViewModel viewModel = new MainViewModel();
private ArrayList<User> data = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewModel.callUser.enqueue(new Callback<JSONUserResponse>() {
#Override
public void onResponse(Call<JSONUserResponse> call, Response<JSONUserResponse> response) {
JSONUserResponse jsonUserResponse = response.body();
assert jsonUserResponse != null;
data = new ArrayList<>(Arrays.asList(jsonUserResponse.getTable()));
Toast.makeText(MainActivity.this, "اطلاعات با موفقیت دریافت شد", Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(Call<JSONUserResponse> call, Throwable t) {
Log.d("Error", t.getMessage());
}
});
}
}
the problem is that the response is null.
You need to create two model classes , one will be like below
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Table {
#SerializedName("CMBL009001")
#Expose
private String cMBL009001;
#SerializedName("NMBL009002")
#Expose
private Integer nMBL009002;
#SerializedName("CMBL009003")
#Expose
private String cMBL009003;
#SerializedName("CMBL009004")
#Expose
private String cMBL009004;
#SerializedName("CMBL009005")
#Expose
private String cMBL009005;
#SerializedName("CMBL009006")
#Expose
private String cMBL009006;
#SerializedName("NMBL009007")
//getters and setters
}
Another will be
import java.util.List;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class TestModel {
#SerializedName("Table")
#Expose
private List<Table> table = null;
public List<Table> getTable() {
return table;
}
public void setTable(List<Table> table) {
this.table = table;
}
}
And then you can use the TestModel in your call.
Even though every thing is working properly and registration is happening. Retrofit on Response don't get called but onFailure gets called. Call.isexecutted returns true.
I am showing the model class and registrationFragment where the error occured. This is taking a lot time. So thanks in advance for help
RegistrationFragment.java
package com.example.milan.hospital;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* A simple {#link Fragment} subclass.
*/
public class RegistrationFragment extends Fragment {
private EditText Name,UserName, UserPassword;
private Button BnRegister;
public RegistrationFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_registration, container, false);
Name = view.findViewById(R.id.txt_name);
UserName = view.findViewById(R.id.txt_user_name);
UserPassword = view.findViewById(R.id.txt_password);
BnRegister = view.findViewById(R.id.bn_register);
BnRegister.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
performRegistration();
}
});
return view;
}
public void performRegistration()
{
String name = Name.getText().toString();
String username = UserName.getText().toString();
String password = UserPassword.getText().toString();
Call<User> call = MainActivity.apiInterface.performRegistration(name,username,password);
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if(response.body().getResponse().equals("ok"))
{
MainActivity.prefConfig.displayToast("Registration success...");
}
else if(response.body().getResponse().equals("exist"))
{
MainActivity.prefConfig.displayToast("User already exist....");
}
else if(response.body().getResponse().equals("error"))
{
MainActivity.prefConfig.displayToast("Something went wrong...");
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
}
});
Name.setText("");
UserPassword.setText("");
UserName.setText("");
}
}
User.java
package com.example.milan.hospital;
import com.google.gson.annotations.SerializedName;
public class User {
#SerializedName("response")
private String Response;
#SerializedName("name")
private String Name;
public String getResponse() {
return Response;
}
public String getName() {
return Name;
}
}
MainActivity.java
package com.example.milan.hospital;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity implements LoginFragment.OnLoginFormActivityListener{
public static PrefConfig prefConfig;
public static ApiInterface apiInterface;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
prefConfig = new PrefConfig(this);
apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
if(findViewById(R.id.fragment_container) != null)
{
if(savedInstanceState != null)
{
return;
}
if(prefConfig.readLoginStatus())
{
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,new WelcomeFragment()).commit();
}
else
{
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,new LoginFragment()).commit();
}
}
}
#Override
public void performRegister() {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
new RegistrationFragment()).addToBackStack(null).commit();
}
#Override
public void performLogin(String name) {
}
}
ApiInterface.java
package com.example.milan.hospital;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface ApiInterface
{
#GET("register.php")
Call<User> performRegistration(#Query("name") String Name,#Query("user_name") String UserName,#Query("user_password") String UserPassword);
#GET("login.php")
Call<User> performUserLogin(#Query("user_name") String UserName,#Query("user_password") String UserPassword);
}
I think performRegistration() in ApiInterface.java should have an annotation of #POST rather than #GET since registration is the act of posting data rather than getting. Give it a try once.
change the ApiClient to this. As there was a well known problem of retrofit while reading json. to resolve we have initialize gson object ourself
public class ApiClient
{
public static final String BASE_URL = "http://10.0.3.2/loginapp/";
public static Retrofit retrofit = null;
public static Retrofit getApiClient()
{
Gson gson = new GsonBuilder()
.setLenient()
.create();
if(retrofit == null)
{
retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create(gson)).build();
}
return retrofit;
}
}
in this link i read that to using RxJava on phone rotation and save state, but i have simple problem on import Transformer on this class:
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import org.reactivestreams.Subscription;
import io.reactivex.Observable;
import io.reactivex.subjects.BehaviorSubject;
public class RxLoader<T> extends Loader<T> {
private final Observable<T> observable;
private final BehaviorSubject<T> cache = BehaviorSubject.create();
private Subscription subscription;
private RxLoader(Context context, Observable<T> observable) {
super(context);
this.observable = observable;
}
public static <T> Observable.Transformer<T, T> compose(AppCompatActivity activity, int id) {
return observable -> create(activity, id, observable);
}
public static <T> Observable<T> create(AppCompatActivity activity, int id,
Observable<T> observable) {
LoaderManager loaderManager = activity.getSupportLoaderManager();
CreateLoaderCallback<T> createLoaderCallback =
new CreateLoaderCallback<>(activity, observable);
loaderManager.initLoader(id, null, createLoaderCallback);
RxLoader<T> rxLoader = (RxLoader<T>) loaderManager.getLoader(id);
return rxLoader.cache.asObservable();
}
#Override
protected void onStartLoading() {
super.onStartLoading();
subscription = observable.subscribe(cache::onNext);
}
#Override
protected void onReset() {
super.onReset();
subscription.unsubscribe();
}
private static class CreateLoaderCallback<T>
implements LoaderManager.LoaderCallbacks<T> {
private final Context context;
private final Observable<T> observable;
public CreateLoaderCallback(Context context, Observable<T> observable) {
this.context = context;
this.observable = observable;
}
#Override
public Loader<T> onCreateLoader(int id, Bundle args) {
return new RxLoader<>(context, observable);
}
#Override
public void onLoadFinished(Loader<T> loader, T data) { }
#Override
public void onLoaderReset(Loader<T> loader) { }
}
}
problem is in this part of class:
public static <T> Observable.Transformer<T, T> compose( ...
The code you have imported most possibly is implemented with RxJava 1.
You have two options:
Import RxJava 1 instead of RxJava 2
Convert existing code to RxJava 2's ObserverTransformer