Suddenly I decided that need to gain android dev experience. I want to create a simple us which will get data from lib and push it to segment with the table view. I'm following the guide https://developer.android.com/guide/fragments/communicate
The problem is that I don't have exceptions but the data in the table(R.id.sdkhash) is not updated. There should be some stupid mistake on my side, but I can't find it.
MainActivity.java
I'm getting (InfoViewModel extends ViewModel) here and push the data via selectItem method.
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
infoViewModel = new ViewModelProvider(this).get(InfoViewModel.class);
RuntimeInfo runtimeInfo = new RuntimeInfo("A", "B", "C", "D");
infoViewModel.selectItem(runtimeInfo);
InfoViewModel.java
class RuntimeInfo {
public String sdkHash;
public String deviceUid;
public String URL;
public String userAgent;
public RuntimeInfo(String hash, String deviceid, String url, String agent) {
sdkHash = hash;
deviceUid = deviceid;
URL = url;
userAgent = agent;
}
}
InfoViewModel.java I'm defining my ViewModel here.
class RuntimeInfo {
// Dummy class to hold the data.
public String sdkHash;
public String deviceUid;
public String URL;
public String userAgent;
public RuntimeInfo(String hash, String deviceid, String url, String agent) {
sdkHash = hash;
deviceUid = deviceid;
URL = url;
userAgent = agent;
}
}
public class InfoViewModel extends ViewModel {
private final MutableLiveData<RuntimeInfo> selectedItem = new MutableLiveData<RuntimeInfo>();
public void selectItem(RuntimeInfo item) {
selectedItem.setValue(item);
}
public MutableLiveData<RuntimeInfo> getSelectedItem() {
return selectedItem;
}
}
InfoFragment.java - Fragment which is responsible for the UI part. I want to be able to update its UI based on the data from MainActivity.
public class InfoFragment extends Fragment {
...
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
hashRowview = (TextView) view.findViewById(R.id.sdkhash);
infoViewModel = new ViewModelProvider(this).get(InfoViewModel.class);
infoViewModel.getSelectedItem().observe(getViewLifecycleOwner(), item -> {
hashRowview.setText((CharSequence) item.sdkHash);
});
infoViewModel.getSelectedItem();
RuntimeInfo runtimeInfo = infoViewModel.getSelectedItem().getValue();
System.out.println("*************************************** runtimeInfo:" + runtimeInfo);
if (runtimeInfo != null) {
hashRowview.setText((CharSequence) runtimeInfo.sdkHash);
System.out.println("*************************************** runtimeInfo Not NULL");
}
Related
I have been trying to find the way to pass a ParcelableArrayList from one Fragment to Another and for some reason when I receive the ParcelableArrayList it's empty. I have checked many posts about this problem but I don't make it work.
This is my Activity that holds the Fragments in a FrameLayout:
package com.blumonpay.restaurant.customer.presentation.activities;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import android.os.Bundle;
import android.view.WindowManager;
import com.blumonpay.android.mifel.restaurante.R;
import com.blumonpay.restaurant.customer.presentation.fragments.ShowPayMethodsFragment;
public class PayMethodsActivity extends AppCompatActivity {
private Fragment showFragment;
private Fragment addFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
setContentView(R.layout.activity_pay_methods);
showFragment = ShowPayMethodsFragment.getInstance(this);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.frameLayout, showFragment)
.commit();
}
public void switchFragment(Fragment fragment) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frameLayout, fragment)
.commit();
}
}
This is how I'm trying to pass the ArrayList in my first Fragment:
Bundle dataBundle = new Bundle();
dataBundle.putParcelableArrayList("cardsList", (ArrayList<? extends Parcelable>) list);
dataBundle.putInt("position",position);
ShowDetailedPayMethodFragment showDetailPayMethod = ShowDetailedPayMethodFragment.getInstance(context);
showDetailPayMethod.setArguments(dataBundle);
((PayMethodsActivity)context).switchFragment(showDetailPayMethod);
As you can see I'm also passing an int value which I can receive with no problem at all.
Then, this is the second Fragment where I'm receiving the data (ParcelableArrayList and the int value):
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_view_detailed_pay_method, container, false);
if (this.getArguments()!=null) {
cardsList = this.getArguments().getParcelableArrayList("cardsList");
positionList = this.getArguments().getInt("position");
}
cardsBannerAdapter = new CustomCardsBannerAdapter(context, cardsList);
cardsViewPager.setAdapter(cardsBannerAdapter);
initSlider();
return v;
}
For some reason the List is empty when I get to this point, the only value I'm getting is this.getArguments().getInt("position"); (int value)
And finally this is my POJO class Cards:
package com.blumonpay.restaurant.customer.domain.model;
import android.os.Parcel;
import android.os.Parcelable;
public class Cards implements Parcelable {
private String cardNumber;
private String cardType;
private String cardBrand;
private String cardExpirationDate;
private String cardHolderName;
public Cards(String cardNumber, String cardType, String cardBrand, String cardExpirationDate, String cardHolderName) {
this.cardNumber = cardNumber;
this.cardType = cardType;
this.cardBrand = cardBrand;
this.cardExpirationDate = cardExpirationDate;
this.cardHolderName = cardHolderName;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getCardType() {
return cardType;
}
public void setCardType(String cardType) {
this.cardType = cardType;
}
public String getCardBrand() {
return cardBrand;
}
public void setCardBrand(String cardBrand) {
this.cardBrand = cardBrand;
}
public String getCardExpirationDate() {
return cardExpirationDate;
}
public void setCardExpirationDate(String cardExpirationDate) {
this.cardExpirationDate = cardExpirationDate;
}
public String getCardHolderName() {
return cardHolderName;
}
public void setCardHolderName(String cardHolderName) {
this.cardHolderName = cardHolderName;
}
protected Cards(Parcel in) {
cardNumber = in.readString();
cardType = in.readString();
cardBrand = in.readString();
cardExpirationDate = in.readString();
cardHolderName = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(cardNumber);
dest.writeString(cardType);
dest.writeString(cardBrand);
dest.writeString(cardExpirationDate);
dest.writeString(cardHolderName);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<Cards> CREATOR = new Parcelable.Creator<Cards>() {
#Override
public Cards createFromParcel(Parcel in) {
return new Cards(in);
}
#Override
public Cards[] newArray(int size) {
return new Cards[size];
}
};
}
Any clue of what am I missing?
Tried the same in kotlin, with the Cards.java intact. The list gets passed. Here is the snippet.
Could you try and compare?
In first fragment as an example:
val item = Cards("gfhfhfhfh", "", "", "", "")
val list = ArrayList<Cards>()
list.add(item
Passing it as: (no explicit cast)
bundle.putParcelableArrayList("list",list)
In the next fragment:
val l = arguments?.getParcelableArrayList<Cards>("list")
According to this post, How to marshall and unmarshall Parcelable, Parcelable object is not stable which might well be the reason that you cannot pass it from one activity to another. I faced similar problem recently and used the same post to solve my problem. Basically in steps:
1. You need to marshall the Parcelable object in the first Activity as :
byte[] bytes = marshall(parcelableObj);
intentOpenSecondActivity.putExtra(SOME_KEY, bytes);
2. You need to unmarshall it in the second Activity as :
byte[] bytes = getIntent().getExtras().getByteArray(SOME_KEY);
parcelableObj = unmarshall(bytes, ParcelableObject.CREATOR);
I have some adapter which use retrofit to get data right from web api and place it to recyclerview
public class NoticeAdapter extends RecyclerView.Adapter<NoticeAdapter.EmployeeViewHolder> {
private Wind wind;
private ArrayList<Notice> dataList;
private Main main;
private Date currentTime = Calendar.getInstance().getTime();
private RecyclerItemClickListener recyclerItemClickListener;
public NoticeAdapter(ArrayList<Notice> dataList, Main main, Wind wind, RecyclerItemClickListener recyclerItemClickListener) {
this.dataList = dataList;
this.main = main;
this.wind = wind;
this.recyclerItemClickListener = recyclerItemClickListener;
}
#Override
public EmployeeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.single_view_row, parent, false);
return new EmployeeViewHolder(view);
}
#Override
public void onBindViewHolder(EmployeeViewHolder holder, #SuppressLint("RecyclerView") final int position) {
if(getAddressMap()!=null){holder.txtNoticeAddress.setText("Loc: "+getAddressMap());}else{holder.txtNoticeAddress.setText("Loc: Unknown location");}
holder.imageIcon.setImageURI(Uri.parse("android.resource://com.locweather/drawable/i"+dataList.get(position).getIcon()));
holder.txtNoticeWind.setText("Wind: "+roundUp(+wind.getSpeed(),1)+"m/s, "+arrow());
holder.txtNoticeTempMain.setText(roundUp(+main.getTemp(),1)+"°C");
holder.txtNoticeWeather.setText(dataList.get(position).getWeather()+" : "+dataList.get(position).getInfo());
holder.txtNoticeTemp.setText("Feels: "+roundUp(+main.getFeelsLike(),1)+"°C ");
holder.txtNoticeTime.setText(currentTime.toString());
holder.txtNoticeHumidity.setText("Humidity: "+main.getHumidity()+"%");
holder.txtNoticePressure.setText("Pressure: "+main.getPressure()+"hPa");
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
recyclerItemClickListener.onItemClick(dataList.get(position));
}
});
}
#Override
public int getItemCount() {
return dataList.size();
}
class EmployeeViewHolder extends RecyclerView.ViewHolder {
ImageView imageIcon;
TextView txtNoticeWeather, txtNoticeTempMain,txtNoticeTemp, txtNoticeHumidity,txtNoticeAddress,txtNoticePressure,txtNoticeWind,txtNoticeTime;
EmployeeViewHolder(View itemView) {
super(itemView);
imageIcon=itemView.findViewById(R.id.image_icon);
txtNoticeTime= itemView.findViewById(R.id.txt_time);
txtNoticeWind= itemView.findViewById(R.id.txt_notice_wind);
txtNoticeAddress= itemView.findViewById(R.id.txt_notice_title);
txtNoticeWeather = itemView.findViewById(R.id.txt_notice_weather);
txtNoticeTemp = itemView.findViewById(R.id.txt_notice_temp);
txtNoticeHumidity = itemView.findViewById(R.id.txt_notice_humidity);
txtNoticePressure = itemView.findViewById(R.id.txt_notice_pressure);
txtNoticeTempMain = itemView.findViewById(R.id.txt_notice_temp_main);
}
}
This is my recyclerview
This works only when network is enabled
The question is how to set this data right from RecyclerView (or other way) to my Room DataBase when network is enabled by Onclick SaveButton to create other recyclerview and set data there, to get it offline later.
I'm trying to create Entity
#Entity
public class WeatherData {
#PrimaryKey(autoGenerate = true)
private long id;
private String address;
private Double windSpeed;
private Integer windDegree;
private String datalistIcon;
private String datalistInfo;
private String datalistWeather;
private Double mainTemp;
private Double mainFeel;
private Integer mainHumidity;
private Integer mainPressure;
private String time;
private Double locLat;
private Double locLon;
public WeatherData(){}
#Ignore
public WeatherData(String address, Double windSpeed, Integer windDegree, String datalistIcon,String datalistInfo,String datalistWeather, Double mainTemp,Double mainFeel,Integer mainHumidity,Integer mainPressure,String time,LatLng currentLocation,Double locLat,Double locLon) {
this.address = address;
this.windSpeed = windSpeed;
this.windDegree = windDegree;
this.datalistIcon=datalistIcon;
this.datalistInfo=datalistInfo;
this.datalistWeather=datalistWeather;
this.mainTemp=mainTemp;
this.mainFeel=mainFeel;
this.mainHumidity=mainHumidity;
this.mainPressure=mainPressure;
this.time=time;
this.locLat=locLat;
this.locLon=locLon;
}
Dao
#Dao
public interface WeatherDataDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void saveAll(List<WeatherData> weathers);
#Insert(onConflict = OnConflictStrategy.REPLACE)
void save(WeatherData weather);
#Update
void update(WeatherData weather);
#Delete
void delete(WeatherData weather);
#Query("SELECT * FROM WeatherData")
LiveData<List<WeatherData>> findAll();
}
and DataBase
#Database(entities = {WeatherData.class}, version = 1)
public abstract class WeatherDatabase extends RoomDatabase {
public static WeatherDatabase INSTANCE;
public abstract WeatherDataDao weatherDao();
private static final Object sLock = new Object();
public static WeatherDatabase getInstance(Context context) {
synchronized (sLock) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
WeatherDatabase.class, "Weathers.db")
.allowMainThreadQueries()
.build();
}
return INSTANCE;
}
}
Which way do I need to create it?
Create an #Entity Notice which is your data type to be stored in your Room DB.
Create a View Model which is attached to your Activity/Fragment where you need to show this list.
Use your ViewModel to store the list from API into your Room DB.
Create a LiveData which observes on the DB and sends the updated list to the other view.
Code for Saving Data in DB. This needs to be run on Background Thread.
public static void saveNoticeList(Context context, List<Notice> noticeList) {
if (context != null && noticeList != null) {
RoomDatabaseCreator.getInstance(context)
.getDatabase()
.noticeDao()
.saveNotices(noticeList);
}
}
// For Saving in background you can use RxJava, I am using a new thread for simplification
backgroundHandler.post(() -> {
saveNoticeList(getActivity(), dataList);
});
ViewModel
public class NoticeViewModel extends AndroidViewModel {
public MutableLiveData<List<Notice>> mNoticesLiveData = new MutableLiveData<>();
private Context mContext;
public NoticeViewModel(final Application application) {
super(application);
mContext = application.getApplicationContext();
mNoticesLiveData = Transformations.switchMap(databaseCreated,
(Function<Boolean, LiveData<List<Notice>>) isDbCreated -> {
if (!Boolean.TRUE.equals(isDbCreated)) { // Not needed here, but watch out for null
//noinspection unchecked
return ABSENT;
} else {
return databaseCreator.getDatabase()
.noticedao()
.getSavedNotices();
}
}
});
public LiveData<List<Notice> getNoticeLiveData() {
return mNoticesLiveData;
}
}
Activity Code where you need to show the saved data
//1. Initialize the viewModel
NoticeViewModel viewModel = ViewModelProviders.of(this).get(NoticeViewModel.class);
//2. Subscribe to the saved notices live-data to get updates in your view
viewModel.getNoticeLiveData().observe(this
list -> {
if (list.isEmpty()) {
return;
}
// Add the list in your adapter
});
I have 5 fragments inside my activity where one fragment stays active at one time. Clicking on a recyclerview item opens another fragment and puts current fragment in the backstack.
The same code was working fine some days ago, but now the app is throwing NotSerializableException whenever I click the home button to put the app in background. I have tried putting the initializing the variables inside onStart and then giving the null value in onStop but that didn't work.
Fragment Code:
public class PaperListFragment extends Fragment implements Serializable {
private static final String TAG = "PaperListFragment";
private static final String QUESTIONS_FRAGMENT_TAG = "questions_fragment";
private static final String ADD_PAPER_FRAGMENT_TAG = "add_paper_fragment";
private OnFragmentActiveListener mOnFragmentActiveListener;
private TextView mHeadingText;
private Bundle mOutState;
private FirebaseAuth mAuth;
private DatabaseReference mDatabaseReference;
private ProgressBar mProgressBar;
private OnItemClickListener mOnItemClickListener;
private FloatingActionButton mFab;
private RecyclerView mRecyclerViewPaper;
private ArrayList<Paper> mPaperList = new ArrayList<>();
private Subject mSubject = new Subject();
private Exam mExam = new Exam();
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_recycler_list, container, false);
mProgressBar = (ProgressBar) rootView.findViewById(R.id.progressbar_news);
mFab = (FloatingActionButton) rootView.findViewById(R.id.floatingActionButton);
mProgressBar.setVisibility(View.VISIBLE);
Log.d(TAG, "onCreateView: Fragment created");
mAuth = FirebaseAuth.getInstance();
mDatabaseReference = FirebaseDatabase.getInstance().getReference();
if (mAuth.getCurrentUser() == null) {
startActivity(new Intent(getActivity(), LoginActivity.class));
getActivity().finish();
return null;
}
if (getArguments() != null) {
mOnFragmentActiveListener = (OnFragmentActiveListener) getArguments().getSerializable(Keys.FRAGMENT_ACTIVE_LISTENER);
mSubject = (Subject) getArguments().getSerializable(Keys.SUBJECT_KEY);
mExam = (Exam) getArguments().getSerializable(Keys.EXAMS_KEY);
}
mRecyclerViewPaper = (RecyclerView) rootView.findViewById(R.id.recycler_list);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()) {
#Override
public boolean canScrollVertically() {
return false;
}
};
mRecyclerViewPaper.setLayoutManager(layoutManager);
Log.d(TAG, "onCreateView: Layout Manager Set.");
mFab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startAddPaperFragment();
}
});
mOnItemClickListener = new OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView.ViewHolder holder, int position) {
Log.d(TAG, "onItemClicked: Clicked item position is: "+ position);
QuestionListFragment questionFragment = new QuestionListFragment();
questionFragment.setRetainInstance(true);
startFragment(position, questionFragment, QUESTIONS_FRAGMENT_TAG);
}
#Override
public void OnItemLongClicked(RecyclerView.ViewHolder holder, int position) {
}
};
mHeadingText = (TextView) rootView.findViewById(R.id.heading_textview);
mHeadingText.setText(mExam.getExam_name()+" > "+ mSubject.getSubject_name());
if (mOutState != null) {
mPaperList = (ArrayList<Paper>) mOutState.getSerializable(Keys.PAPER_LIST_KEY);
updateUI();
} else {
updateUIFromDatabase();
}
return rootView;
}
private void startFragment(int position, Fragment fragment, String fragmentTag) {
Paper paper = new Paper();
if (mPaperList.size() > 0) {
paper = mPaperList.get(position);
}
Bundle args = new Bundle();
args.putSerializable(Keys.EXAMS_KEY, mExam);
args.putSerializable(Keys.SUBJECT_KEY, mSubject);
args.putSerializable(Keys.PAPER, paper);
args.putSerializable(Keys.FRAGMENT_ACTIVE_LISTENER, mOnFragmentActiveListener);
fragment.setArguments(args);
FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
fragmentTransaction.replace(R.id.questions_fragment_container, fragment, fragmentTag);
fragmentTransaction.addToBackStack(fragmentTag);
fragmentTransaction.commit();
}
private void startAddPaperFragment() {
AddPaperFragment addPaperFragment = new AddPaperFragment();
addPaperFragment.setRetainInstance(true);
startFragment(0, addPaperFragment, ADD_PAPER_FRAGMENT_TAG);
}
private void updateUIFromDatabase() {
if (getArguments() != null){
Exam exam = (Exam) getArguments().getSerializable(Keys.EXAMS_KEY);
Subject subject = (Subject) getArguments().getSerializable(Keys.SUBJECT_KEY);
DatabaseReference paperReference =
mDatabaseReference
.child(Keys.APP_DATA_KEY)
.child(Keys.EXAM_PAPERS)
.child(exam.getExam_name())
.child(subject.getSubject_name());
Query query = paperReference.orderByChild(Keys.TIME_ADDED);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
mPaperList.clear();
for (DataSnapshot paperChild : dataSnapshot.getChildren()) {
mPaperList.add(paperChild.getValue(Paper.class));
}
updateUI();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
private void updateUI() {
PaperRecyclerAdapter adapter = new PaperRecyclerAdapter(
getActivity(),
mRecyclerViewPaper,
mPaperList,
mOnItemClickListener
);
mRecyclerViewPaper.setAdapter(adapter);
mProgressBar.setVisibility(View.GONE);
}
#Override
public void onResume() {
super.onResume();
if (getArguments()!=null){
mOnFragmentActiveListener.onFragmentActive(
this,
"Topics"
);
}
}
#Override
public void onPause() {
super.onPause();
mOutState = new Bundle();
mOutState.putSerializable(Keys.PAPER_LIST_KEY, mPaperList);
}
}
Exception:
2018-12-26 17:49:38.344 14834-14834/in.crazybytes.bankmaniaadmin E/AndroidRuntime: FATAL EXCEPTION: main
Process: in.crazybytes.bankmaniaadmin, PID: 14834
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = in.crazybytes.bankmaniaadmin.activities.QuestionsActivity)
at android.os.Parcel.writeSerializable(Parcel.java:1526)
at android.os.Parcel.writeValue(Parcel.java:1474)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:723)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408)
at android.os.Bundle.writeToParcel(Bundle.java:1133)
at android.os.Parcel.writeBundle(Parcel.java:763)
at android.support.v4.app.FragmentState.writeToParcel(FragmentState.java:124)
at android.os.Parcel.writeTypedArray(Parcel.java:1306)
at android.support.v4.app.FragmentManagerState.writeToParcel(FragmentManager.java:639)
at android.os.Parcel.writeParcelable(Parcel.java:1495)
at android.os.Parcel.writeValue(Parcel.java:1401)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:723)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408)
at android.os.Bundle.writeToParcel(Bundle.java:1133)
at android.os.Parcel.writeBundle(Parcel.java:763)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3697)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3768)
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:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Caused by: java.io.NotSerializableException: com.google.firebase.auth.internal.zzj
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1224)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1584)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1549)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1472)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1218)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1584)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1549)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1472)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1218)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:346)
at android.os.Parcel.writeSerializable(Parcel.java:1521)
at android.os.Parcel.writeValue(Parcel.java:1474)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:723)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408)
at android.os.Bundle.writeToParcel(Bundle.java:1133)
at android.os.Parcel.writeBundle(Parcel.java:763)
at android.support.v4.app.FragmentState.writeToParcel(FragmentState.java:124)
at android.os.Parcel.writeTypedArray(Parcel.java:1306)
at android.support.v4.app.FragmentManagerState.writeToParcel(FragmentManager.java:639)
at android.os.Parcel.writeParcelable(Parcel.java:1495)
at android.os.Parcel.writeValue(Parcel.java:1401)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:723)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1408)
at android.os.Bundle.writeToParcel(Bundle.java:1133)
at android.os.Parcel.writeBundle(Parcel.java:763)
at android.app.ActivityManagerProxy.activityStopped(ActivityManagerNative.java:3697)
at android.app.ActivityThread$StopInfo.run(ActivityThread.java:3768)
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:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
Note: The weird thing is that one of fragment has the exact same code and is hosted inside the same activity, but when that fragment is active and app goes to background, interestingly the app is not crashing.
**Exam Model Class:
package in.crazybytes.bankmaniaadmin.models;
import java.io.Serializable;
public class Exam implements Serializable {
private String mExam_name;
private String mExam_key;
private Long mTime_added;
private int mNum_subjects;
private int mNum_questions;
public Exam(String exam_name, String exam_key, Long time_added, int num_subjects, int num_questions) {
mExam_name = exam_name;
mExam_key = exam_key;
mTime_added = time_added;
mNum_subjects = num_subjects;
mNum_questions = num_questions;
}
public Exam() {
}
public String getExam_name() {
return mExam_name;
}
public void setExam_name(String exam_name) {
mExam_name = exam_name;
}
public String getExam_key() {
return mExam_key;
}
public void setExam_key(String exam_key) {
mExam_key = exam_key;
}
public Long getTime_added() {
return mTime_added;
}
public void setTime_added(Long time_added) {
mTime_added = time_added;
}
public int getNum_subjects() {
return mNum_subjects;
}
public void setNum_subjects(int num_subjects) {
mNum_subjects = num_subjects;
}
public int getNum_questions() {
return mNum_questions;
}
public void setNum_questions(int num_questions) {
mNum_questions = num_questions;
}
}
Paper Model Class
package in.crazybytes.bankmaniaadmin.models;
import java.io.Serializable;
public class Paper implements Serializable {
private String mPaper_name;
private String mPaper_key;
private Long mTime_added;
private int mNum_questions;
public Paper(String paper_name, String paper_key, Long time_added, int num_questions) {
mPaper_name = paper_name;
mPaper_key = paper_key;
mTime_added = time_added;
mNum_questions = num_questions;
}
public Paper() {
}
public String getPaper_key() {
return mPaper_key;
}
public void setPaper_key(String paper_key) {
mPaper_key = paper_key;
}
public Long getTime_added() {
return mTime_added;
}
public void setTime_added(Long time_added) {
mTime_added = time_added;
}
public int getNum_questions() {
return mNum_questions;
}
public void setNum_questions(int num_questions) {
mNum_questions = num_questions;
}
public String getPaper_name() {
return mPaper_name;
}
public void setPaper_name(String paper_name) {
mPaper_name = paper_name;
}
}
Subject Model Class:
package in.crazybytes.bankmaniaadmin.models;
import java.io.Serializable;
public class Subject implements Serializable {
private String mSubject_name;
private String mSubject_key;
private Long mTime_added;
private int mNum_papers;
private int mNum_questions;
public Subject(String subject_name, String subject_key, Long time_added, int num_papers, int num_questions) {
mSubject_name = subject_name;
mSubject_key = subject_key;
mTime_added = time_added;
mNum_papers = num_papers;
mNum_questions = num_questions;
}
public Subject() {
}
public String getSubject_name() {
return mSubject_name;
}
public void setSubject_name(String subject_name) {
mSubject_name = subject_name;
}
public String getSubject_key() {
return mSubject_key;
}
public void setSubject_key(String subject_key) {
mSubject_key = subject_key;
}
public Long getTime_added() {
return mTime_added;
}
public void setTime_added(Long time_added) {
mTime_added = time_added;
}
public int getNum_papers() {
return mNum_papers;
}
public void setNum_papers(int num_papers) {
mNum_papers = num_papers;
}
public int getNum_questions() {
return mNum_questions;
}
public void setNum_questions(int num_questions) {
mNum_questions = num_questions;
}
}
Somehow QuestionsActivity is getting into the fragment save state, even if you don't intend for that to happen. While QuestionsActivity is being serialized, another object that is not serializable is being encountered. That's why you see TextViews and other things attempting to get serialized because all the instance variables of QuestionsActivity get serialized by default.
My best guess for why this is happening is due to this line:
args.putSerializable(Keys.FRAGMENT_ACTIVE_LISTENER, mOnFragmentActiveListener);
But it's difficult to know for sure without seeing where OnFragmentActiveListener is defined. I'm assuming either QuestionsActivity implements OnFragmentActiveListener, or QuestionsActivity defines OnFragmentActiveListener as an inner class. Either way, if you put an OnFragmentActiveListener into fragment arguments, you will encounter an exception because you indirectly are storing the entire QuestionsActivity as a fragment arg too. When a fragment stops, all fragment args become part of the fragment save state. And that's the cause of the error.
I would suggest not passing the OnFragmentActiveListener around as a fragment arg. If the OnFragmentActiveListener comes from the activity, just use getActivity() to get a reference to the activity and then get a reference to the listener.
I also noticed PaperListFragment implements Serializable and I'm assuming you did the same thing for QuestionsActivity. You probably did this to get around compile errors. But this has led to runtime errors because the instance variables on both of these classes are not all serializable. So to avoid more runtime issues, I would suggest never having activities or fragments implement serializable because these classes are inherently not serializable due to their members.
Lets take the example below.
Here i can see the Thumbnail of the youTubeThumbnailView1 in the imageView.
I have included some questions as part of comments in the code.
Early on i thought i could just create an ArrayList of ImageView and get all the Thumbnails inside that but that wouldn't work as i can't even access the imageView outside of the method.
Any help is appreciated.
public class MainActivity extends YouTubeBaseActivity implements YouTubeThumbnailView.OnInitializedListener {
private YouTubeThumbnailView youTubeThumbnailView1;
//private YouTubeThumbnailLoader youTubeThumbnailLoader;
private ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
youTubeThumbnailView1 = (YouTubeThumbnailView) findViewById(R.id.youtubeThumbnailView1);
imageView = (ImageView) findViewById(R.id.imageView);
youTubeThumbnailView1.initialize(DeveloperKey.DEVELOPER_KEY, this);
//Accessing imageView here would give NullPointerException
}
#Override
public void onInitializationSuccess(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader youTubeThumbnailLoader) {
//this.youTubeThumbnailLoader = youTubeThumbnailLoader; for calling next() etc. in onThumbnailLoader(,)
youTubeThumbnailLoader.setOnThumbnailLoadedListener(new ThumbnailListener());
youTubeThumbnailLoader.setPlaylist("ECAE6B03CA849AD332",1);
//How to call release() on the youTubeThumbnailLoader parameter as it causes ServiceConnectionLeaked after exiting the app. But if you call it here nothing works.
}
#Override
public void onInitializationFailure(YouTubeThumbnailView youTubeThumbnailView, YouTubeInitializationResult youTubeInitializationResult) {
}
private class ThumbnailListener implements YouTubeThumbnailLoader.OnThumbnailLoadedListener{
#Override
public void onThumbnailLoaded(YouTubeThumbnailView youTubeThumbnailView, String s) {
imageView.setImageDrawable(youTubeThumbnailView.getDrawable());
/*How can i access this imageView outside of this method?
Using something like below will show all the thumbnails BUT how do you access them outside of this method?
if (youTubeThumbnailLoader.hasNext()) {
youTubeThumbnailLoader.next();
} else {
youTubeThumbnailLoader.first();
}
*/
}
#Override
public void onThumbnailError(YouTubeThumbnailView youTubeThumbnailView, YouTubeThumbnailLoader.ErrorReason errorReason) {
}
}
}
i recently managed to do this with the help of a library called picasso to load the images into the imageview and android async http to retrieve the list of thumbnails.
Add this to you app-level build.gradle file under dependencies.
dependencies {
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.loopj.android:android-async-http:1.4.9'
}
For retrieving the images, i made a http get request to the Youtube data API and retrieved all the image URLs and added it as the thumbnail property of the Video class.
More information here on retrieving all the thumbnail: https://developers.google.com/youtube/v3/docs/playlistItems/list
I did it like this.
private void loadVideos(String id) {
//just a method to check if i am connected to the internet
checkConnected();
String requestUrl = "https://www.googleapis.com/youtube/v3/playlistItems";
String key = API_KEY;
String param = "?part=id, snippet&playlistId=" + id + "&key=" + key + "&maxResults=50";
String request = requestUrl + param;
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
client.get(request, params, new TextHttpResponseHandler() {
#Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
}
#Override
public void onSuccess(int statusCode, Header[] headers, String responseString) {
try {
videoArr = new ArrayList<Video>();
JSONObject obj = new JSONObject(responseString);
JSONArray playlistItems = obj.getJSONArray("items");
for (int i = 0; i < playlistItems.length(); i++) {
JSONObject snippet = playlistItems.getJSONObject(i).getJSONObject("snippet");
String title = snippet.getString("title");
String description = snippet.getString("description");
JSONObject thumbnailObj = snippet.getJSONObject("thumbnails");
//Here i am extracting the URL of the thumbnail
String thumbnail = thumbnailObj.getJSONObject("high").getString("url");
JSONObject resourceId = snippet.getJSONObject("resourceId");
String id = resourceId.getString("videoId");
Video video = new Video(id, title, description, thumbnail);
videoArr.add(video);
}
populateListView(videoArr);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
my Video class is as follows:
public class Video implements Serializable {
private String id;
private String title;
private String description;
private String thumbnail;
public Video(String id, String title, String description, String thumbnail) {
this.id = id;
this.title = title;
this.description = description;
this.thumbnail = thumbnail;
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getThumbnail() {
return thumbnail;
}
}
And finally, to load the images, i did it like this using picasso.
ImageView yourImageView = (ImageView)findViewById(R.id.yourImageView);
//video is my Video object.
Picasso.with(yourActivity.this).load(video.getThumbnail()).fit().into(yourImageView);
And don't forget you do have to get an API key from the developers console.Hope i helped. Good luck with your app!
I'm new to Android programming, and I'd like to create a central database service class which will take care of user data exchange with an external database. For this, I created a service which is started after successful login. I created another class that extends AsyncTask to do the data retrieval.
Now, I wanted the methods for the data retrieval to be stored in the service. I would fire intents to the service from different activities, and with .setAction() I would determine which method to call, or which data to retrieve.
I also created an interface class for handling the AsyncTask results.
Now, from this question I thought that it would be possible to have multiple listeners to one and the same AsyncTask result. But now this seems impossible to achieve: I'd like to retrieve the AsyncTask results in the MainMenuActivity, but I can't create an instance of AsyncUserData there as a delegate for the UserData class. In my example below, the missing piece is a valid instance of AsyncUserData for the UserData class to work with. How could I do it?
Here's the example:
MainMenuActivity
public class MainMenuActivity extends ActionBarActivity implements AsyncUserData {
TextView tvUsername;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_menu);
tvUsername =
(TextView) findViewById(R.id.tvUsername);
TelephonyManager tManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
String uid = tManager.getDeviceId();
getDataFromUserSessionService(this, uid);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void retrieveResult(String result) throws JSONException {
JSONObject jsonObject = new JSONObject(result);
String joName;
joName = jsonObject.getJSONObject("name").toString();
user.setName(joName);
tvUsername.setText(joName);
}
public void getDataFromUserSessionService(Context context, String uid) {
Intent intent = new Intent(context, UserSession.class);
intent.setAction(UserSession.ACTION_FETCH_USER_DATA);
intent.putExtra(UserSession.UID, uid);
context.startService(intent);
}
UserSession Service
public class UserSession extends IntentService {
public static final String ACTION_FETCH_USER_DATA = "com.example.blahblah.services.action.read_user_data";
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
utils = new Utils(this);
final String action = intent.getAction();
uid = intent.getStringExtra(UID);
if (ACTION_FETCH_USER_DATA.equals(action)) {
handleUserDataFetch(uid);
}
}
}
private void handleUserDataFetch(String uid) {
String[] parameters = new String[2];
parameters[0] = uid;
parameters[1] = Constants.USER_DATA_FETCH;
UserData userData = new UserData(this);
userData.execute(parameters);
}
UserData AsyncTask Class (the Utils class just has another post method):
public class UserData extends AsyncTask < String, Void, String > {
public AsyncUserData delegate = null;
private Context myContext;
public UserData(Context context) {
myContext = context;
}
#Override
protected String doInBackground(String...params) {
String serverResponse = "";
String uid = params[0];
Utils utils = new Utils(myContext);
String phpName = params[1];
List < NameValuePair > nameValuePairs = new ArrayList < NameValuePair > ();
nameValuePairs.add(new BasicNameValuePair("uid", uid));
try {
serverResponse = utils.passDataToServer(phpName, nameValuePairs);
} catch (IOException e) {
e.printStackTrace();
}
return serverResponse;
}
protected void onPostExecute(String result) {
try {
delegate.retrieveResult(result);
} catch (JSONException e) {
e.printStackTrace();
}
}
};
And the AsyncUserData interface:
public interface AsyncUserData {
void retrieveResult(String result) throws JSONException;
}
You can use a Singleton that stores a reference to the activity
public class ServiceToActivity
{
public ActionBarActivity mainactivity = null;
private static ServiceToActivity singleton = null;
public Class<?> cl = null;
private ServiceToActivity()
{
}
public static ActionBarActivity getSingleton()
{
if(singleton==null)
return null;
return singleton.mainactivity;
}
public static Class<?> getSingletonClass()
{
if(singleton==null)
return null;
return singleton.cl;
}
public static void setSingleton(ActionBarActivity mainactivity, Class<?> cl)
{
if(singleton==null)
singleton = new ServiceToActivity();
singleton.mainactivity = mainactivity;
singleton.cl = cl;
}
}
Then create the singleton before the service is started
public void getDataFromUserSessionService(Context context, String uid) {
Intent intent = new Intent(context, UserSession.class);
intent.setAction(UserSession.ACTION_FETCH_USER_DATA);
intent.putExtra(UserSession.UID, uid);
ServiceToActivity.setSingleton(this,this.getClass()); //create Singleton to store a reference to the activity
context.startService(intent);
}
In UserData retrieve data to the main activity by:
protected void onPostExecute(String result) {
try {
Class<?> cl = ServiceToActivity.getSingletonClass();
Method met = cl.getMethod("retrieveResult", String); //String because result is of type String: you can use result.getClass() instead
met.invoke(cl.cast(ServiceToActivity.getSingleton()), result); // compare it to this ServiceToActivity.getSingleton().retrieveResult(result);
} catch (JSONException e) {
e.printStackTrace();
}
}
It sounds like you might want to use an event bus such as otto