I want to fetch BASE_URL from SharedPreferences inside my RetrofitClient Class.
My Code Is:
RetrofitClient.java:
public class RetrofitClient {
private static final String BASE_URL = getBaseUrl();
private String getBaseUrl() {
SharedPreferences sp1 = getSharedPreferences("Login", MODE_PRIVATE);
String apiUrl = sp1.getString("apiUrl", null);
return apiUrl;
}
private RetrofitClient() {
//MyRetrofitClient...
}
}
How can i get get it work?
MainActivity.java:
Call<LoginResponse> call = RetrofitClient
.getInstance()
.getApi()
.loginUser(username, password, action);
call.enqueue(new Callback<LoginResponse>() {
}
For passing data , you need to make a constructor for that class . something like this
public class ApiClient {
Context my_conext;
public ApiClient (Context context) {
my_conext= context;
}
SharedPreferences sp1 = my_context.getSharedPreferences("Login", MODE_PRIVATE);
}
EDIT: from your updated code .
you are doing it wrong .
first of all don't call function at the time of initialisation. use like this
retrofit = new Retrofit.Builder()
.baseUrl(getBaseUrl(my_context))
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
Second thing . you need **Constructor** constructor has the same name like class . carefully see my above answer . both has same name ApiClient. so in your case
public class RetrofitClient {
Context my_conext;
public RetrofitClient (Context context) {
my_conext= context;
}
}
from activity , you are calling this RetrofitClient , call like this
RetrofitClient(MainActivity.this).getApi();
Related
I am trying to achieve MVVM design pattern in my application.I have created viewmodel and repository class but when I am trying to instantiate viewmodel in my MainActivity its showing error red line below MainActivity at the time of instantiation in below line.
pdfViewModel = new ViewModelProvider(MainActivity.this).get(PdfViewModel.class);
Below is my code:
MainActivity.java
public class MainActivity extends AppCompatActivity {
PdfViewModel pdfViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pdfViewModel = new ViewModelProvider(MainActivity.this).get(PdfViewModel.class);
}
}
PdfViewModel.java
public class PdfViewModel extends AndroidViewModel {
private PdfRepository pdfRepository;
public PdfViewModel(#NonNull Application application) {
super(application);
pdfRepository = new PdfRepository(application);
}
public LiveData<List<Pdfs>> getAllPdfs(){
return pdfRepository.getMutableLiveData();
}
}
PdfRepository.java
public class PdfRepository {
private ArrayList<Pdfs> list = new ArrayList<>();
private MutableLiveData<List<Pdfs>> mutableLiveData = new MutableLiveData<>();
private Application application;
public PdfRepository(Application application){
this.application = application;
}
public MutableLiveData<List<Pdfs>> getMutableLiveData(){
SharedPreferences preferences = application.getSharedPreferences("Credentials", Context.MODE_PRIVATE);
String email = preferences.getString("email",null);
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<Pdfs>> call = apiService.getFiles(email);
call.enqueue(new Callback<List<Pdfs>>() {
#Override
public void onResponse(Call<List<Pdfs>> call, Response<List<Pdfs>> response) {
if(response.body() != null){
list = (ArrayList<Pdfs>) response.body();
mutableLiveData.setValue(list);
}
}
#Override
public void onFailure(Call<List<Pdfs>> call, Throwable t) {
TastyToast.makeText(application,t.getMessage(),TastyToast.LENGTH_SHORT,TastyToast.ERROR).show();
}
});
return mutableLiveData;
}
}
What needs to be corrected in the above code?
Your code is trying to create a new instance of the class ViewModelProvider (with the new keyword) and that's not the right way to instantiate a ViewModel.
On MainActivity, instead of:
pdfViewModel = new ViewModelProvider(MainActivity.this).get(PdfViewModel.class);
try:
pdfViewModel = ViewModelProviders.of(this).get(PdfViewModel.class);
Notice the right class is ViewModelProviders (with an "s" at the end) and you need to call the static method of instead of creating a new instance of it with new. If you can't import that class, make sure you have the dependency 'androidx.lifecycle:lifecycle-extensions:2.2.0' added to app/build.gradle.
To make your code even clearer, I'd suggest learning about the Kotlin KTX method viewModels, as described here. You'd need to use Kotlin for that though.
I am able to call Retrofit and SharedPreferences and Intent fine from an Activity and all works. But it gets messy real fast and lots of repetition thus I want to call them from a helper class instead. It makes it complicated cos I end up having to extend the Helper class with the Application class, followed by IDE complaining I have missing XML files which are actually not needed for a helper class.
Feel like I am over complicating things. What's the solution here? Write directly on Activity classes and endure the code repetitions or is there an elegant solution to this?
This is how I tried to extract the logic for Retrofit and Intent away from an Activity class Using a helper.
public class RetrofitHelper extends Application {
private final String ERROR_MSG = "username is invalid";
private static final String TAG = "RetrofitHelper";
private static final String API_KEY = "";
private VerificationHelper verificationHelper;
public RetrofitHelper(VerificationHelper verificationHelper) {
this.verificationHelper = verificationHelper;
}
public Retrofit getRetrofit(String baseUrl){
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(setTimeout())
.addConverterFactory(GsonConverterFactory.create());
return builder.build();
}
public void performCallBack(Call<User> call){
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
String usernameToken = Objects.requireNonNull(response.body()).getUsername();
String username = null;
if(usernameToken != null){
username = verificationHelper.parseToken(usernameToken, API_KEY);
}
if (username != null && !username.equals(ERROR_MSG)){
Log.i(TAG, "onResponse: Success " + username);
// verificationHelper.setPreferences(username);
verificationHelper.goToMainActivity();
}
else {
Log.i(TAG, "onResponse: Incorrect response.");
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
Log.i(TAG, "onFailure: server error");
}
});
}
private OkHttpClient setTimeout(){
return new OkHttpClient().newBuilder()
.connectTimeout(60, TimeUnit.MINUTES)
.readTimeout(60, TimeUnit.MINUTES)
.writeTimeout(60, TimeUnit.MINUTES)
.build();
}
}
For reference, VerificationHelper called from the above Helper.
public class VerificationHelper extends Application {
private static boolean isValidCredential = false;
public static boolean isValidCredential() {
return isValidCredential;
}
static void setValidCredential(boolean validCredential) {
isValidCredential = validCredential;
}
private <T> String convertObjToJson(T object){
// some logic
return "";
}
public String createToken(String apiKey, User user, long ttlMillis) {
//some logic
return "";
}
String parseToken(String token, String apiKey) {
//some logic
return "";
}
//trying to keep this here so I call at many places.
void setPreferences(String username){
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("username", username);
editor.putBoolean("isLoggedIn", true);
editor.apply();
}
//trying to keep this here so I call at many places.
void goToMainActivity(){
Intent mainIntent = new Intent(this, MainActivity.class);
startActivity(mainIntent);
}
}
Make a Helper Class (no Activity extended) and pas the context to the functino that you want to call. An Exmaple with shared preferences:
public class SharedPreferenceHelper {
private final static String PREF_FILE = "<Shared_PReferences_Name>";
/**
* Set a string shared preference
* #param context - Context (Activity that is calling the function)
* #param key - Key to set shared preference
* #param value - Value for the key
*/
public static void setSharedPreferenceString(Context context, String key, String value){
SharedPreferences settings = context.getSharedPreferences(PREF_FILE, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putString(key, value);
editor.apply();
}
}
The you can call the fucntino like this:
SharedPreferenceHelper.setSharedPreferenceString(MainActivity.this, <key>, <value>);
Source: https://github.com/nickescobedo/Android-Shared-Preferences-Helper/blob/master/SharedPreferenceHelper.java
From the documentation you can see getDefaultSharedPreferences method takes a context as a parameter. This context can be an Activity, a Fragment or a Service. The simplest solution would be to pass the context to your helper class methods via dependency injection. Something like:
void setUsernamePref(String username, Context context){
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("username", username);
editor.putBoolean("isLoggedIn", true);
editor.apply();
}
Remember to avoid storing context statically in the helper class, otherwise you'll risk to fall into unwanted memory leaks
I'm working on an Android Project right now and I'm trying to parse from an URL. In my "ApiClient" I have no problem to parse. Here is my "ApiClient" class:
public class ApiClient implements Callback<Map<String, Channel>> {
static final String BASE_URL = "someURL";
public void start() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
RestInterface restInterface = retrofit.create(RestInterface.class);
Call<Map<String, Channel>> call = restInterface.getChannels();
call.enqueue(this);
}
#Override
public void onResponse(retrofit2.Call<Map<String, Channel>> call, Response<Map<String, Channel>> response) {
System.out.println(response.code());
if(response.isSuccessful()) {
Map<String, Channel> body = response.body();
List<Channel> channels = new ArrayList<>(body.values());
}
...
}
I'm trying to get the response into a List from using callback in my "Radio" class. This the place where I'm having the problem. I tried this three too but it didn't solved my problem:
private List<Channel> listChannels = new ArrayList<Channel>();
private List<Channel> listChannels = new ArrayList<>();
private List<Channel> listChannels = new List<>();
Here is my "Radio" class:
public class Radio {
private static final String STORAGE = "radio";
private List<Channel> listChannels;
public static Radio get() {
return new Radio();
}
private SharedPreferences storage;
private Radio() {
storage = App.getInstance().getSharedPreferences(STORAGE, Context.MODE_PRIVATE);
}
public List<Channel> getData() {
RestInterface restInterface = SingletonClass.getService();
restInterface.getChannels().enqueue(new Callback<Map<String, Channel>>() {
#Override
public void onResponse(Call<Map<String, Channel>> call, Response<Map<String, Channel>> response) {
if(response.isSuccessful()){
Map<String, Channel> body = response.body();
List<Channel> channels = new ArrayList<>(body.values());
loadChannels(channels);
}
}
#Override
public void onFailure(Call<Map<String, Channel>> call, Throwable t) {
}
});
System.out.println(listChannels.get(1).getArtist());
return listChannels;
}
public boolean isRated(int itemId) {
return storage.getBoolean(String.valueOf(itemId), false);
}
public void setRated(int itemId, boolean isRated) {
storage.edit().putBoolean(String.valueOf(itemId), isRated).apply();
}
private void loadChannels(List<Channel> channels){
listChannels.clear();
listChannels.addAll(channels);
}
}
Here is my interface class:
public interface RestInterface {
#GET("someURL")
retrofit2.Call<Map<String, Channel>> getChannels();
}
and my SingletonClass:
public class SingletonClass{
private static final Retrofit RETROFIT = new Retrofit.Builder()
.baseUrl(someURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
private static final RestInterface SERVICE = RETROFIT.create(RestInterface.class);
public static RestInterface getService(){
return SERVICE;
}
}
I don't know what should I do to fill the List in my Radio class now. I'm totally open to suggestions. Thanks for the help.
Are you getting an empty list? You're asynchronously setting in the channel data in getData(), so if you're trying to get the data by reading it in the next line, it may not be loaded yet.
This means that when you call System.out.println(listChannels.get(1).getArtist()), you won't see the result of loadChannels, because that call happens right after you call enqueue() while loadChannels() is running on a separate thread. If you moved that into onResponse() you might have more luck.
In Android, a fairly easy way to do things like this and interact with the UI is by using AsyncTask, which for you would look something like this:
private class loadChannelTask extends AsyncTask<Void, Void, List<Channel>> {
protected List<Channel> doInBackground() {
//get response
//pass to load channels
}
protected void onPostExecute() {
System.out.println(listChannels.get(1).getArtist()); //presumably the artist name
}
}
I'm trying to update this RetroFit + Otto tutorial, so my code updated is:
IWeather.java
RetroFit 2.+ doesn't allow to return void, so instead of void getWeather(...) I added Call<Weather> getWeather(...).
public interface IWeather {
#GET("/{latitude},{longitude}")
Call<Weather> getWeather(#Path("latitude") String latitude,
#Path("longitude") String longitude,
Callback<Weather> callback);
}
ForecastClient.java
RetroFit 2.+ has changed his constructor, so the new ForecastClient has the next form. The IllegalArgumentException points to the weather.getWeather(...) method.
public class ForecastClient {
private static final String BASE_URL = "https://api.darksky.net/forecast/";
private static final String API_KEY = "******************";
public static final String API_URL = BASE_URL + API_KEY + "/";
private static ForecastClient mForecastClient;
private static Retrofit mRetroAdapter;
public static ForecastClient getClient() {
if (mForecastClient == null) {
mForecastClient = new ForecastClient();
}
return mForecastClient;
}
private ForecastClient() {
mRetroAdapter = new Retrofit.Builder()
.baseUrl(API_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(new OkHttpClient())
.build();
}
public void getWeather(String latitude, String longitude, Callback<Weather> callback) {
IWeather weather = mRetroAdapter.create(IWeather.class);
weather.getWeather(latitude, longitude, callback);
}
}
ForecastManager.java
public class ForecastManager {
private Context mContext;
private Bus mBus;
private ForecastClient sForecastClient;
public ForecastManager(Context context, Bus bus) {
this.mContext = context;
this.mBus = bus;
sForecastClient = ForecastClient.getClient();
}
#Subscribe
public void onGetWeatherEvent(GetWeatherEvent getWeatherEvent) {
String latitude = Double.toString(getWeatherEvent.getLatitude()).trim();
String longitude = Double.toString(getWeatherEvent.getLongitude()).trim();
Callback<Weather> callback = new Callback<Weather>() {
#Override
public void onResponse(Call<Weather> call, Response<Weather> response) {
Log.d(ForecastManager.class.getSimpleName(), response.body().toString());
mBus.post(new SendWeatherEvent(response.body()));
}
#Override
public void onFailure(Call<Weather> call, Throwable t) {
Log.e(ForecastManager.class.getSimpleName(), t.getMessage());
}
};
sForecastClient.getWeather(latitude, longitude, callback);
}
}
I'm receiving a No Retrofit annotation found and I guess the reason is something related with my callback, but it is a RetroFit callback, so I don't understand why the error.
The stacktrace points to the MainActivity, in particular to the Otto's method post() with a java.lang.RuntimeException: Could not dispatch event, caused by the error previously mentioned java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #3):
MainActivity.java
public class MainActivity extends AppCompatActivity {
#BindView(R.id.activity_main_textView)
TextView textView;
#BindView(R.id.activity_main_button)
Button button;
private static final double LATITUDE = **.******;
private static final double LONGITUDE = **.******;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
#OnClick(R.id.activity_main_button)
public void onClick(View view) {
BusProvider.getInstace().post(new GetWeatherEvent(LATITUDE, LONGITUDE));
}
#Subscribe
public void onSendWeatherEvent(SendWeatherEvent sendWeatherEvent) {
Weather weather = sendWeatherEvent.getWeather();
Currently currently = weather.getCurrently();
textView.setText(currently.getSummary());
}
Any clue about where is the error or how should I handle the bus subscriptions, the callback, etcetera?
java.lang.IllegalArgumentException: No Retrofit annotation found. (parameter #3)
As the error says, the problem is that the third parameter of the getWeather method does not have an annotation. The Callback class is used for the Call#enqueue(Callback) method.
Simply change
sForecastClient.getWeather(latitude, longitude, callback)
to
sForecastClient.getWeather(latitude, longitude).enqueue(callback)
and remove the third parameter.
Try it and modify your retrofit version number to 2.6.0. The use of coroutines in Retrofit 2.6.0 can return a Response object directly. If await() is not needed, Retrofit will automatically call it. This is not applicable when the version is below 2.6.0.
I'm trying to run a simple HTTP query through Volley and this code works completely fine in the default Android MainActivity under onCreate():
JsonObjectRequest jsonRequest = new JsonObjectRequest(Request.Method.GET, url, (String)null, new Response.Listener<JSONObject>()
{
#Override
public void onResponse(JSONObject response) {
// the response is already constructed as a JSONObject!
try {
// response = response.getJSONObject("results");
String success = response.getString("success"),
status = response.getString("status");
if(status.equals("1"))
{
GlobalVar.armedStatus = 1;
}
else if(status.equals("0"))
{
GlobalVar.armedStatus = 0;
}
System.out.println("Success: "+success+"\nStatus: "+status);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
});
// RequestQueue queue = Volley.newRequestQueue(jsonRequest);
Volley.newRequestQueue(this).add(jsonRequest);
When I run this code in a separate class though, the final line gives an error.
Volley.newRequestQueue(this).add(jsonRequest);
The error on newRequestQueue is "Cannot resolve symbol 'newRequestQueue' and the error on add(jsonRequest); is "Missing method body, or declare abstract."
Any help would be appreciated, not sure why it works in the Activity, but not in the class file... I assume the reason is onCreate(), but I'm sure there is some way to keep it in another class?
It's because in the line:
Volley.newRequestQueue(this).add(jsonRequest);
The this you pass into newRequestQueue() needs to be an Android Context. When you're calling it from your Activity then this refers to the Activity which is a type of Context. When you call if from your own class it will refer to that class which won't be a Context.
You have a few different options:
Add Context as a Constructor Parameter for your Object
You can have it so that when you make an instance of your class you have to pass in a context which you hold onto. This would then be used when you call Volley:
public class MyClass {
private Context context;
public MyClass(Context context) {
this.context = context;
}
public void doVolleyRequest() {
//...
Volley.newRequestQueue(context).add(jsonRequest);
}
}
Then when you make your class from your Activity you would have to instantiate it like:
MyClass myClass = new MyClass(this);
Have a static Reference to the Application Context
You can have a static reference to the Application Context which can be accessed from your Application class. To do this in your Application Class have something like:
public class App extends Application {
private static Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
public static Context getContext() {
return context;
}
}
Then from your class where you are using Volley:
Volley.newRequestQueue(App.getContext()).add(jsonRequest);
Have an App Wide RequestQueue
Instead you could set up a request queue in your Application class and add all requests to that:
public class App extends Application {
private static RequestQueue requestQueue;
#Override
public void onCreate() {
super.onCreate();
requestQueue = Volley.newRequestQueue(getApplicationContext());
}
public static RequestQueue getRequestQueue() {
return requestQueue;
}
}
Then in your class:
App.getRequestQueue().add(jsonRequest);