Fragment's Context gets "lost" while passing it as a parameter - android

I came across a very awkward behaviour in my fragment.
The output is:
this.userID: 0
and
RoomChatFragment userID: 14
But in this case, this.userID should also be 14. Is my context lost somewhere, while passing it as a parameter? I can't explain myself this behaviour. I don't think getActivity() returns null, otherwise there would be an exception.
// Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context = getActivity();
user = new UserHandler(context);
messageDatabase = MessageDatabase.getInstance(context);
Log.i("debug", "RoomChatFragment userID: " + user.getUserID());
}
// UserHandler
public class UserHandler {
private final SharedPreferences sharedPrefs;
private final SharedPreferences sharedPrefsPreferences;
private Context context;
public UserHandler(Context context) {
sharedPrefs = context.getSharedPreferences("USER", 0);
sharedPrefsPreferences = PreferenceManager.getDefaultSharedPreferences(context);
this.context = context;
}
public int getUserID() {
return sharedPrefs.getInt("userID", 0);
}
public void setUserID(int userID) {
SharedPreferences.Editor editor = sharedPrefs.edit();
editor.putInt("userID", userID);
editor.apply();
}
}
// Database
public class MessageDatabase extends AbstractDatabase {
private int userID;
protected static MessageDatabase instance;
public MessageDatabase(Context context) {
super(context);
UserHandler user = new UserHandler(context);
userID = user.getUserID();
}
public static MessageDatabase getInstance(Context context) {
if (MessageDatabase.instance == null) {
MessageDatabase.instance = new MessageDatabase(context);
}
return MessageDatabase.instance;
}
// ....
#Override
protected Message cursorToObject(Cursor cursor) {
Log.i("debug", "this.userID: " + this.userID);
}
}
// AbstractDatabase
public abstract class AbstractDatabase {
protected Context context;
protected AbstractDatabase(Context context) {
this.context = context;
}
}

I'm not absolutely sure what's going on here (your code is really messy). But it seems you're using a different key for the preference:
context.getSharedPreferences("USER", 0);
sharedPrefs.getInt("userID", 0);

Stupid me! The problem is the singleton design pattern of my database design. My MessageDatabase.instance is cached and holds an old Context object, where the userID of my SharedPreferences is 0.
I've updated my method like this and it seems to work:
public static MessageDatabase getInstance(Context context) {
if (MessageDatabase.instance == null || userID == 0) {
MessageDatabase.instance = new MessageDatabase(context);
}
return MessageDatabase.instance;
}

Related

Get the sharedpreference data in an adapter

Googled and tried my whole day about getting the sharedpreference data in my adapter. I have an SharedPreferenceManager class
public class SharedPrefManager {
static SharedPreferences sharedPreferences;
static Context mContext;
static int PRIVATE_MODE = 0;
private static final String PREF_NAME = "sessionPref";
static SharedPreferences.Editor editor;
public SharedPrefManager (Context context) {
mContext = context;
sharedPreferences = mContext.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
editor = sharedPreferences.edit();
}
public void saveUID(Context context,String uid){
mContext = context;
sharedPreferences = mContext.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("UID", uid);
editor.commit();
}
public String getUID(){
sharedPreferences = mContext.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
return sharedPreferences.getString("UID", "");
}
public void clear(){
editor.clear();
editor.apply();
}
}
I call the SharedPrefManager in any activity like this
public SharedPrefManager sharedPrefManager;
public String uid
and in onCreate()
sharedPrefManager = new SharedPrefManager(mContext);
uid = sharedPrefManager.getUID();
now, how to get the same data in an adapter since the below code gives error in any adapter
SharedPrefManager sharedPrefManager = new SharedPrefManager(context);
public String uid = sharedPrefManager.getUID();
Here is my Adapter Class
public class MyCommentAdapter extends RecyclerView.Adapter<MyCommentAdapter.MyCommentHolder> {
Context context;
private List<MyComment> commentList;
//Here I get the null pointer error
SharedPrefManager sharedPrefManager = new SharedPrefManager(context);
public String uid = sharedPrefManager.getUID();
private DatabaseReference mDatabaseReference1=FirebaseDatabase.getInstance().getReference().child("User").child(uid).child("Comment");
int the Last line of code I want to access the database with the uid, that is why I need the uid value from shared preference
public MyCommentAdapter(Context context, List<MyComment> commentList) {
this.context = context;
this.commentList = commentList;
}
Your context is null. Try after assigning it
SharedPrefManager sharedPrefManager ;
public String uid ;
private DatabaseReference mDatabaseReference1;
public MyCommentAdapter(Context context, List<MyComment> commentList) {
this.context = context;
sharedPrefManager = new SharedPrefManager(context);
this.commentList = commentList;
uid = sharedPrefManager.getUID();
mDatabaseReference1=FirebaseDatabase.getInstance().getReference().child("User").child(uid).child("Comment");
}
First of all it's not a right thing to this kind of things inside of your RecyclerView adapter, it slows down you scrolling performance and it's violating SRP.
BUT
you can simply inject you SharedPrefManager into you adapter :
public class MyCommentAdapter extends RecyclerView.Adapter<MyCommentAdapter.MyCommentHolder> {
private Context context;
private List<MyComment> commentList;
private SharedPrefManager sharedPrefManager;
public String uid;
public MyCommentAdapter(Context context, List<MyComment> commentList, SharedPrefManager sharedPrefManager) {
this.context = context;
this.commentList = commentList;
this.sharedPrefManager = sharedPrefManager
this.uid = sharedPrefManager.getUID();
}
}
and then in your activity when you are creating the adapter you can inject sharedPrefManager in it's constructor.
you need to initialize your sharedPreferancne class in the constructor of the adpater. Something like this:
private CustomSharedPreferences customSharedPreferences;
and then in your constructor:
public MyCommentAdapter(Context context, List<MyComment> commentList)
{
this.context = context;
this.customSharedPrefernces = new CustomSharedPreferences(context);
this.commentList = commentList;
}
public class SP {
/**
* #param mContext
* #param key
* #param value
*/
public static void savePreferences(Context mContext, String key, String value) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(key, value).apply();
}
/**
* #param context
* #param keyValue
* #return
*/
public static String getPreferences(Context context, String keyValue) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
return sharedPreferences.getString(keyValue, "");
}
/**
* #param mContext
*/
public static void removeAllSharedPreferences(Context mContext) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.clear().apply();
}
}

Run a Slider just after the Splash Screen

My app crashes every time the Splash Screen has slept for 5 seconds and it just won't start my slider.I'd like to add by saying that I've tried using Shared Preferences but the error tends to persist.Any help would be appreciated.The method launchmain2() is basically nothing but calling a blank activity named Main2Activity.I haven't created as many layouts for the slider as I would need but rather just one which gets all its resources accordingly from the Slider class.Here's the full code
MainActivity
public class MainActivity extends AppCompatActivity {
ImageView iv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = findViewById(R.id.welcome_image);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.transition);
iv.setAnimation(animation);
Thread loading = new Thread() {
public void run() {
try {
sleep(5000);
Intent main = new Intent(getApplicationContext(),Slide_Adapter.class);
startActivity(main);
finish();
}
catch (Exception e) {
e.printStackTrace();
}
}
};
loading.start();
}
}
Slide_Adapter
public class Slide_Adapter extends AppCompatActivity {
ViewPager pager;
Slider adapter;
Preferences preferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slide__adapter);
pager = findViewById(R.id.viewpager);
adapter = new Slider(this);
pager.setAdapter(adapter);
preferences = new Preferences(this);
if(!preferences.First()){
launchmain2();
finish();
}
}
private void launchmain2() {
preferences.FirstTime(false);
Intent intent = new Intent(Slide_Adapter.this, Main2Activity.class);
startActivity(intent);
finish();
}
}
Slider
public class Slider extends PagerAdapter {
private Context context;
public Slider(Slide_Adapter slide_adapter) {
this.context = context;
}
public int images[] = {R.drawable.add, R.drawable.call, R.drawable.message};
public String title[] = {"ADD A CONTACT", "MAKE CALLS", "TEXT"};
public int background[] = {
Color.rgb(255,0,0),
Color.rgb(128,255,0),
Color.rgb(255,0,255)};
#Override
public int getCount() {
return title.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return (view == (RelativeLayout)object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.slides, container, false);
RelativeLayout relativeLayout = view.findViewById(R.id.relative_layout);
ImageView imageView = view.findViewById(R.id.image);
TextView textView = view.findViewById(R.id.description);
relativeLayout.setBackgroundColor(background[position]);
imageView.setImageResource(images[position]);
textView.setText(title[position]);
container.addView(view);
return view;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((RelativeLayout)object);
}
}
Preference Class
public class Preferences {
SharedPreferences sharedPreferences;
SharedPreferences.Editor editor;
Context context;
private static final String FIRST_LAUNCH = "A";
int MODE = 0;
private static final String PREFERENCE = "B";
public Preferences(Context context) {
this.context = context;
sharedPreferences = context.getSharedPreferences(PREFERENCE, MODE);
editor = sharedPreferences.edit();
}
public void FirstTime(boolean first){
editor.putBoolean(FIRST_LAUNCH, first);
editor.commit();
}
public boolean First(){
return sharedPreferences.getBoolean(FIRST_LAUNCH, true);
}
}
This issue arises because of the null context. Update context on Slider Adapter page.
Context update
private Context mContext;
public Slider(Context context) {
this.mContext = context;
}
And then use the mContext for instantiating the item.
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Update: For opening another activity for second time opening, change your MainActivity like this.
public class MainActivity extends AppCompatActivity {
ImageView iv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Preferences.init(getApplicationContext());// Also add this
iv = findViewById(R.id.welcome_image);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.transition);
iv.setAnimation(animation);
Thread loading = new Thread() {
public void run() {
try {
sleep(5000);
if(Preferences.getIsFirst() == false){
Preferences.writeFirstTimeOpen(true);
Intent main = new Intent(getApplicationContext(),Slide_Adapter.class);
startActivity(main);
finish();
}else{
Intent main = new Intent(getApplicationContext(), Main2Activity.class);
startActivity(main);
finish();
}
}
catch (Exception e) {
e.printStackTrace();
}
}
};
loading.start();
}
}
And Preference Class:
public class Preferences {
private static SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
Context context;
private static final String FIRST_LAUNCH = "A";
int MODE = 0;
private static final String PREFERENCE = "B";
public static void init(Context context) {
if (sharedPreferences == null) {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
}
}
public static boolean writeFirstTimeOpen(boolean value) {
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(FIRST_LAUNCH, value);
return editor.commit();
}
public boolean getIsFirst(){
return sharedPreferences.getBoolean(FIRST_LAUNCH, true);
}
}
Please check this, as this will also fix another first time opening issue.
You need to modify like this your constructor
public Slider(Context context) {//Add Context in parameter
this.context = context;
}
And please go though this link how to set and get value from shared-preferences
Hope this helps you
I noticed that parameter in constructor of Slider class not define as needed
What you do
public Slider(Slide_Adapter slide_adapter) {//There is not Context in parameter
this.context = context;//context will be still null
}
What need to do
public Slider(Context context) {//Add Context in parameter
this.context = context;
}

Getting Null Pointer Exception - Using an Interface to Pass Data from an Adapter to a Sub-fragment

I have an Adapter and a Sub-Fragment. I am using an interface to pass data from the Adapter to the Sub-Fragment. The problem I am having is that I keep getting a Null Pointer Exception. I have read through the various posts here relating to the problem and can't figure out where I went wrong. I get the NPE at the line 'passAdapterVariable.passAdapterVariable(mname)'. Based on what I have read I suspect it may be because I am not properly initializing passAdapterVariable. I have tried initializing it several different ways based on other examples but I keep getting the NPE.
Here is the Adapter
public class MatchAdapter extends RecyclerView.Adapter<MatchAdapter.MatchViewHolder> {
//declaration of variables
private Fragment fragment;
private FragmentManager fragmentManager;
private DiscoverPage discoverPage;
private Context context;
private int size;
private int mposition;
private TextView txt_matchname;
private ImageView img_matchpic;
List<String> maImg = new ArrayList<>();
private String mname;
PassAdapterVariable passAdapterVariable;
public interface PassAdapterVariable {
void passAdapterVariable(String mname);
}
//the constructor
public MatchAdapter(List<String> maImg, int size, Context context, DiscoverPage discoverPage){//, PassAdapterVariable passAdapterVariable) {
this.maImg = maImg;
this.context = context;
this.discoverPage = discoverPage;
this.size = size;
//this.passAdapterVariable = (PassAdapterVariable)context;
}
public MatchAdapter(String mname, Context context) {
this.context = context;
this.passAdapterVariable = (PassAdapterVariable)context;
}
//PassAdapterVariable passAdapterVariable = (PassAdapterVariable) context;
#Override
public MatchAdapter.MatchViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.match_items, parent, false);
MatchViewHolder matchViewHolder = new MatchViewHolder(view, maImg, discoverPage);
return matchViewHolder;
}
#Override
public void onBindViewHolder(MatchViewHolder holder, int position) {
Picasso.with(context).load(maImg.get(position)).into(holder.img_match);
holder.setIsRecyclable(false);
}
#Override
public int getItemCount() {
return maImg.size();
}
//viewholder class
public class MatchViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private static final String TAG = "error";
//declare variables
private DiscoverPage discoverPage;
private ImageView img_match;
//ViewHolder constructor
public MatchViewHolder(View itemView, final List<String> maImg, final DiscoverPage discoverPage) {
super(itemView);
//initialize variables inside the viewholder constructor
this.discoverPage = discoverPage;
img_match = (ImageView) itemView.findViewById(R.id.img_match);
txt_matchname = (TextView) itemView.findViewById(R.id.txt_matchname);
img_matchpic = (ImageView) itemView.findViewById(R.id.img_matchpic);
//set click listener for the img_match
img_match.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (view == img_match) {
//discoverPage.isHidden();
Fragment currentFragment;
fragment = new ClickedMatch();
fragmentManager = ((AppCompatActivity) context).getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(android.R.id.content, fragment);
transaction.addToBackStack("DiscoverPage");
if ((currentFragment = ((AppCompatActivity) context).getSupportFragmentManager().findFragmentById(R.id.main_container)) != null) {
transaction.hide(currentFragment);
}
else {
transaction.commit();
}
mname = maImg.get(getAdapterPosition());
mposition = getAdapterPosition();
mname = maImg.get(mposition);
passAdapterVariable.passAdapterVariable(mname);
}
}
}
}
Here is the Sub-Fragment
public class ClickedMatch extends Fragment implements MatchAdapter.PassAdapterVariable{
//declare variables
private Toolbar toolbar;
private TextView txt_matchname;
private TextView txt_matchprice;
private ImageView img_matchpic;
private String mname;
private String imgmatch;
List<String> maImg = new ArrayList<>();
int size;
Context context;
DiscoverPage discoverPage;
private String pname;
private int i;
MatchAdapter.PassAdapterVariable passAdapterVariable;
public ClickedMatch() {
// 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_clicked_match, container, false);
//initialize variables
toolbar = (Toolbar) view.findViewById(R.id.toolbar);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);//set toolbar as action bar
txt_matchname = (TextView)view.findViewById(R.id.txt_matchname);
img_matchpic = (ImageView)view.findViewById(R.id.img_matchpic);
//setHasOptionsMenu(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getActivity().onBackPressed();
}
});
if(((AppCompatActivity)getActivity()).getSupportActionBar()!= null){
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayShowHomeEnabled(true);
((AppCompatActivity)getActivity()).getSupportActionBar().setHomeButtonEnabled(true);
((AppCompatActivity)getActivity()).getSupportActionBar().setTitle("");
}
// MatchAdapter matAdapter = new MatchAdapter(maImg, size, context, discoverPage,
//passAdapterVariable);
// matAdapter.passAdapterVariable = this;
//passAdapterVariable.passAdapterVariable(mname);
//txt_matchname.setText(pname);
MatchAdapter matAdapter = new MatchAdapter(pname, getContext());
matAdapter.passAdapterVariable = this;
passAdapterVariable(pname);
txt_matchname.setText(pname);
return view;
}
#Override
public void passAdapterVariable(String mname) {
this.pname = mname;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
this.getActivity().finish();
}
return super.onOptionsItemSelected(item);
}
}
Here is the error log
08-13 21:53:49.341 12852-12852/com.test.jack E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.test.jack, PID: 12852
java.lang.NullPointerException: Attempt to invoke interface method 'void com.test.jack.MatchAdapter$PassAdapterVariable.passAdapterVariable(java.lang.String)' on a null object reference
at com.test.jack.MatchAdapter$MatchViewHolder.onClick(MatchAdapter.java:138)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
To add some more context, I have a main activity(UserMainPage). A bottom navigation menu selector replaces the UserMainPage with a fragment(DiscoverPage).
DiscoverPage calls the adapter(MatchAdapter). A button click on DiscoverPage replaces DiscoverPage with a sub-fragment(ClickedMatch).
I am trying to pass a variable from MatchAdapter to ClickedMatch.
I was able to pass data from an adapter to a sub-fragment using a Bundle.
I modified my code as follows:
In the adapter I added:
fragment = new ClickedMatch();
Bundle bundle = new Bundle();
bundle.putString("matchname",mname);
fragment.setArguments(bundle);
...
transaction.commit();
In my sub-fragment I added:
Bundle bundle = this.getArguments();
if(bundle != null){
pname = bundle.getString("matchname");
}
There was no need to use an interface. Based on what I have researched I think (and I may be wrong) that creating an interface and then implementing it is better used when communicating between fragments as opposed to communicating between an adapter and a sub-fragment.
Looks like the String "mname" is null when you call passAdapterVariable.passAdapterVariable(mname);
Either you're List maImg is empty || null or the provided adapter position is wrong.
try logging
Log.d(TAG, "pos: " + getAdapterPosition());
mname = maImg.get(getAdapterPosition());
Log.d(TAG, "name: " + mname);
You can add Context parameter in constructor of Adapter with String as
public MatchAdapter(String mname, Context context) {
this.context = context;
}
Now get the context from Fragment as
MatchAdapter matAdapter = new MatchAdapter(pname, getActivity());
matAdapter.passAdapterVariable = this;
passAdapterVariable(pname);
txt_matchname.setText(tname);
return view;
PassAdapterVariable passAdapterVariable = (PassAdapterVariable) context;
Is just a declaration+Assignment statement in your adapter class. At this point in time, context is null.
The variable context is getting assigned only in your constructor. Assign your passAdapterVariable after the context has been assigned
public MatchAdapter(List<String> maImg, int size, Context context, DiscoverPage discoverPage) {
this.maImg = maImg;
this.context = context;
this.discoverPage = discoverPage;
this.size = size;
passAdapterVariable = (PassAdapterVariable) context;
}
Inside MatchAdapter------
Add instance to your interface in constructor that will work as
{
DataInterface passInterface;
Context context;
public MatchAdapter(String mname, Context context1, DataPass pass) {
this.passAdapterVariable = mname;
this.context = context1;
this.passInterface = pass;
}
}
Now get the instance of that interface while calling adapter as
{
MatchAdapter matAdapter = new MatchAdapter(pname, getActivity(),this);
}

set SharedPreferences name dynamically

I'm using this sequence for designing an app:
(This classes will not change and I'm gonna use them for multiple activities)
Custom adapter
Model Class
Shared Preferences
And Activity with tab Layouts(with two Fragments) wich contains:
I'm gonna name this: (Package #1)
MainActivity
Fragment One
Fragment Two
Now I want to duplicate Package #1 and change some contents then name it as Package #2. But I have a problem here.
I'm using one shared preferences for Package #1, Package #2, Package #3..., right?
please have a look into my shared preferences class:
public class SharedPreference_light {
private SharedPreferences settings;
private SharedPreferences.Editor editor;
private Gson gson = new Gson();
private static final String PREFS_NAME = "Light_Products";
private static final String FAVORITES = "Favorite_Tones_Light";
public SharedPreference_light(Context context) {
settings = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
editor = settings.edit();
}
private void saveFavorites(List<ProductLocal> favorites) {
String jsonFavorites = gson.toJson(favorites);
editor.putString(FAVORITES, jsonFavorites);
editor.apply();
}
public void addFavorite(ProductLocal product) {
List <ProductLocal> favorites = getFavorites();
if (favorites == null)
favorites = new ArrayList<>();
favorites.add(product);
saveFavorites(favorites);
}
public void removeFavorite(ProductLocal product) {
ArrayList <ProductLocal> favorites = getFavorites();
if (favorites != null) {
favorites.remove(product);
saveFavorites(favorites);
}
}
public ArrayList <ProductLocal> getFavorites() {
List<ProductLocal> favorites;
if (settings.contains(FAVORITES)) {
String jsonFavorites = settings.getString(FAVORITES, null);
ProductLocal[] favoriteItems = gson.fromJson(jsonFavorites, ProductLocal[].class);
favorites = Arrays.asList(favoriteItems);
favorites = new ArrayList <> (favorites);
} else
return null;
return (ArrayList <ProductLocal> ) favorites;
}
}
The problem is if I use this two variables:
private static final String PREFS_NAME = "Light_Products";
private static final String FAVORITES = "Favorite_Tones_Light";
There will be a conflict between those packages. because I'm going to add some list items into shared preferences and use getSharedPreferences. then all those items from multiple packages will be added into one shared preferences, and I don't want that.
Now my real question would be:
How can I set shared preferences names(variables) dynamically?
Note:
I have one usage of shared preferences in custom adapter:
private boolean checkFavoriteItem(ProductLocal checkProduct) {
boolean check = false;
List<ProductLocal> favorites = sharedPreference.getFavorites();
if (favorites != null) {
for (ProductLocal product : favorites) {
if (product.equals(checkProduct)) {
check = true;
break;
}
}
}
return check;
}
Adapter:
public class LocalAdapter extends RecyclerView.Adapter<LocalAdapter.MyViewHolder>{
private SharedPreference_light sharedPreference;
public LocalAdapter(Activity activity, List<ProductLocal> dataList, RelativeLayout snackLayout) {
this.snackLayout=snackLayout;
this.activity = activity;
this.dataList = dataList ;
this.dataListFilter = dataList ;
sharedPreference = new SharedPreference_light(activity);
methods = new Methods(activity);
}
first you would like to use an interface providing the package name:
public interface LightPrefs {
String getPackageName();
}
Secondly, you can reuse your class and make it implementing the previous interface but making it abstract:
public abstract class SharedPreference_light implements LightPrefs {
private SharedPreferences settings;
private SharedPreferences.Editor editor;
protected final String PREFS_NAME = "Light_Products_" + getPackageName();
protected final String FAVORITES = "Favorite_Tones_Light_" + getPackageName();
public SharedPreference_light(Context context) {
settings = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
editor = settings.edit();
}
private void saveFavorites(List<ProductLocal> favorites) {
String jsonFavorites = gson.toJson(favorites);
editor.putString(FAVORITES, jsonFavorites);
editor.apply();
}
public void addFavorite(ProductLocal product) {
List <ProductLocal> favorites = getFavorites();
if (favorites == null)
favorites = new ArrayList<>();
favorites.add(product);
saveFavorites(favorites);
}
public void removeFavorite(ProductLocal product) {
ArrayList <ProductLocal> favorites = getFavorites();
if (favorites != null) {
favorites.remove(product);
saveFavorites(favorites);
}
}
}
Especially pay attention to some visibility modifiers that have changed.
And finally extend this abstract class in your packages:
public class SharedPreference_package1 extends SharedPreference_light {
private static final String TAG = "SharedPref_package1";
public SharedPreference_package1(Context context) {
super(context);
Log.d(TAG, PREFS_NAME);
}
#Override
public String getPackageName() {
return "package#1";
}
}
and:
public class SharedPreference_package2 extends SharedPreference_light {
private static final String TAG = "SharedPref_package2";
public SharedPreference_package2(Context context) {
super(context);
Log.d(TAG, PREFS_NAME);
}
#Override
public String getPackageName() {
return "package#2";
}
}
Instantiating both of these classes gives you this log:
D/SharedPref_package1: Light_Products_package#1
D/SharedPref_package2: Light_Products_package#2
About the adapter, I think you should specify the shared preference object upon construction:
public class LocalAdapter extends RecyclerView.Adapter<LocalAdapter.MyViewHolder>{
private SharedPreference_light sharedPrefs;
public LocalAdapter(Activity activity, List<ProductLocal> dataList, RelativeLayout snackLayout, SharedPreference_light sharedPrefs) {
this.snackLayout=snackLayout;
this.activity = activity;
this.dataList = dataList ;
this.dataListFilter = dataList ;
this.sharedPrefs = sharedPrefs;
methods = new Methods(activity);
}
So you can initialise your adapter like this in package #1:
SharedPreference_package1 sharedPrefs = new SharedPreference_package1();
LocalAdapter adapter = new LocalAdapter(activity, dataList, snackLayout, sharedPrefs);
And you can adapt with SharedPreference_package2 in the second package.
Hope this will help you.
Make your all methods takes shared preferences key as parameter like:
public SharedPreference_light(Context context, String name);
private void saveFavorites(List<ProductLocal> favorites, String name);
public void addFavorite(ProductLocal product, String name);
public void removeFavorite(ProductLocal product, String name);
public ArrayList <ProductLocal> getFavorites(String name);

Singleton constructor crashes my app

I'm testing out the model layer of my application and I want to add an element to a list. But whenever I try to add some data into my data model the application crashes. I cannot find the reason for this.
My code for the data model.
public class DataModel {
private List<Log> logs;
private static DataModel instance;
private Context ctx;
//Singleton constructor
private DataModel()
{
//This makes it crash
logs.add(new Log("1234","sms", 123545, 1, 0));
//Load logs from database - Not done yet.
}
public static DataModel getInstance()
{
if (instance == null)
{
//Creates the instance
instance = new DataModel();
}
return instance;
}
My code for log
public class Log {
private String phonenumber;
private String type;
private long date;
private int incoming;
private int outgoing;
private long id;
//Constructor for incoming sms or call
public Log( String Phonenumber, String Type, long Date, int Incoming, int Outgoing)
{
this.phonenumber = Phonenumber;
this.type = Type;
this.date = Date;
this.incoming = Incoming;
this.outgoing = Outgoing;
}
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getPhonenumber()
{
return phonenumber;
}
public void setPhonenumer(String phonenumber)
{
this.phonenumber = phonenumber;
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
public long getDate()
{
return date;
}
public void setDate(long date)
{
this.date = date;
}
public int getIncoming()
{
return incoming;
}
public void setIncoming(int incoming)
{
this.incoming = incoming;
}
public int getOutgoing()
{
return outgoing;
}
public void setOutgoing (int outgoing)
{
this.outgoing = outgoing;
}
You are not initializing logs. Its null when you execute this statement:
logs.add(new Log("1234","sms", 123545, 1, 0));
Change:
private List<Log> logs;
to:
private List<Log> logs = new ArrayList<Log>();
I see a context in your code, but you don't set it or use it anywhere so maybe you stripped part of your code. In relation to that, if you use it to UI related stuff (and some other cases) I can guarantee you that it will crash your app if you don't reset it every time the screen orientation changes or you change activities.
You have not Instantiated list object
private List<Log> logs;
Update your constructor to this
//Singleton constructor
private DataModel()
{
//This makes it crash
logs = new ArrayList<Log>();
logs.add(new Log("1234","sms", 123545, 1, 0));
//Load logs from database - Not done yet.
}
Now every time you constructor gets called you will get a fresh copy of list object.
Initialize the List before use
you can initialize the List in Constructor as well
public class DataModel {
private List<Log> logs= new ArrayList<Log>();
private static DataModel instance;
private Context ctx;
//Singleton constructor
private DataModel()
{
//This makes it crash
logs.add(new Log("1234","sms", 123545, 1, 0));
int i=0;
//Load logs from database - Not done yet.
}
public static DataModel getInstance()
{
if (instance == null)
{
//Creates the instance
instance = new DataModel();
}
return instance;
}
}
Don't initialize globally logs and also use synchronized getInstance method so that only one instance should get created if two threads are trying to access at the same time.
Use this code:
public class DataModel {
private List<Log> logs;
private static DataModel instance;
private Context ctx;
//Singleton constructor
private DataModel()
{
if(logs == null){
logs = new ArrayList<Log>();
}
logs.add(new Log("1234","sms", 123545, 1, 0));
//Load logs from database - Not done yet.
}
public synchronized static DataModel getInstance()
{
if (instance == null)
{
//Creates the instance
instance = new DataModel();
}
return instance;
}

Categories

Resources