Parcelable creation from a bundle does not call constructor - android

I have a class that implements Parcelable.
I safe this object to the bundle in an activites onSaveInstanceState and I try to recreate the object in onCreate. This looks like following:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState != null)
{
// this line does NOT call my constructor, why not?
// AND I don't get the same object as before => I get an uninitialised object
mStackManager = savedInstanceState.getParcelable(BackstackManager.TAG);
// the list and map in the object are NULL after this...
}
else
{
mStackManager = new BackstackManager();
...
}
}
#Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putParcelable(BackstackManager.TAG, mStackManager);
}
When the Parcelable class is created, it should call the cosntructor of the parcelable class, shouldn't it? My parcelable class is retrieved from the bundle, but it does not look like the object that was saved, it even is not initialised...
My simplified parcelable class looks like following:
public class BackstackManager implements Parcelable
{
public static final String TAG = BackstackManager.class.getName();
private List<List<Pair<Class<?>, Pair<Integer, String>>>> mBackstack;
private Map<String, Fragment.SavedState> mSavedStateMap;
public BackstackManager()
{
init();
}
private void init()
{
mBackstack = new ArrayList<List<Pair<Class<?>, Pair<Integer, String>>>>();
mSavedStateMap = new HashMap<String, Fragment.SavedState>();
}
// ----------------------
// Parcelable
// ----------------------
public static final Parcelable.Creator<BackstackManager> CREATOR =
new Parcelable.Creator<BackstackManager>()
{
#Override
public BackstackManager createFromParcel(Parcel source)
{
return new BackstackManager(source);
}
#Override
public BackstackManager[] newArray(int size)
{
return new BackstackManager[size];
}
};
public BackstackManager(Parcel source)
{
init();
readFromParcel(source);
}
#Override
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags)
{
dest.writeInt(mBackstack.size());
for (int i = 0; i < mBackstack.size(); i++)
{
List<Pair<Class<?>, Pair<Integer, String>>> data = mBackstack.get(i);
dest.writeInt(data.size());
for (int j = 0; j < data.size(); j++)
{
Class<?> clazz = data.get(j).first;
Integer id = data.get(j).second.first;
String tag = data.get(j).second.second;
Fragment f = mFragmentManager.findFragmentByTag(tag);
// 1) save backstack data
ParcelBundleUtils.writeStringNullSafe(dest, clazz != null ? clazz.getName() : null);
dest.writeInt(id);
dest.writeString(tag);
// 2) save fragment state, if possible
ParcelBundleUtils.writeBoolean(dest, f != null);
if (f != null)
ParcelBundleUtils.writeParcelableNullSafe(dest, mFragmentManager.saveFragmentInstanceState(f), 0);
}
}
}
public void readFromParcel(Parcel source)
{
int backstackSize = source.readInt();
for (int i = 0; i < backstackSize; i++)
{
ArrayList<Pair<Class<?>, Pair<Integer, String>>> data = new ArrayList<Pair<Class<?>, Pair<Integer, String>>>();
int dataSize = source.readInt();
for (int j = 0; j < dataSize; j++)
{
// 1) read backstack data
String clazzName = ParcelBundleUtils.readStringNullSafe(source);
Class<?> clazz = null;
if (clazzName != null)
{
try
{
clazz = Class.forName(clazzName);
}
catch (ClassNotFoundException e)
{
L.e(this, e);
}
}
Integer id = source.readInt();
String tag = source.readString();
// 2) read fragment state if possible
boolean savedStateExists = ParcelBundleUtils.readBoolean(source);
if (savedStateExists)
{
SavedState state = ParcelBundleUtils.readParcelableNullSafe(source, SavedState.class.getClassLoader());
if (state != null)
mSavedStateMap.put(tag, state);
}
data.add(new Pair<Class<?>, Pair<Integer, String>>(clazz, new Pair<Integer, String>(id, tag)));
}
mBackstack.add(data);
}
}
}
My ParcelBundleUtils just write a bit if an object is null or not and dependent on that read/write the actual object...
EDIT
my current workround is to use a read/write function for a bundle that does EXACTLY (I copy and pasted my code and just replaced the read/write functions) the same as the parcel read/write function, but writes the data directly to a bundle... But I really would like to know, why the above is not working as a parcel
EDIT 2
I found following: Android: Parcelable.writeToParcel and Parcelable.Creator.createFromParcel are never called
But if that's the problem, I would not be the only one facing this problem...

You must have the following line as the first line in your onCreate(Bundle) override:
super.onCreate(savedInstanceState);
This is why your state is not working correctly.

Related

Unmarshalling unknown type code XXXXX at offset YYY when using getIntent().getExtras().getInt() [duplicate]

This question already has answers here:
java.lang.RuntimeException: Parcel android.os.Parcel: Unmarshalling unknown type code
(2 answers)
Closed 5 years ago.
I'm trying to pass an int value from activity A to activity B.
Activity A:
Intent intent = new Intent(ActivityA.this, ActivityB.class);
intent.putExtra("list_size", list.size());
for (int i = 0; i < list.size(); i++) {
intent.putExtra("account" + i, accountList.get(i));
}
startActivityForResult(intent, 2);
Activity B:
private void init() {
Intent intent = getIntent();
int listSize = intent.getIntExtra("list_size", 0); // Error thrown here
for (int i = 0; i < listSize; i++) {
newList.add((Account) intent.getParcelableExtra("account" + i));
}
}
Account (this is the list item):
public class Account implements Parcelable {
private Long accountId;
private String accountName;
private Set<String> followedAccountsIds = new HashSet<>();
private Set<String> followersAccountsIds = new HashSet<>();
private Set<Integer> likedTagsIds = new HashSet<>();
private Set<Post> postsByAccount = new HashSet<>();
public Account() {
}
public Account(Long accountId, String accountName, Set<String> followedAccountsIds,
Set<String> followersAccountsIds, Set<Integer> likedTagsIds, Set<Post> postsByAccount) {
this.accountId = accountId;
this.accountName = accountName;
this.followedAccountsIds = followedAccountsIds;
this.followersAccountsIds = followersAccountsIds;
this.likedTagsIds = likedTagsIds;
this.postsByAccount = postsByAccount;
}
// Omitted getters and setters for brevity
protected Account(Parcel in) {
if (in.readByte() == 0) {
accountId = null;
} else {
accountId = in.readLong();
}
accountName = in.readString();
}
public static final Creator<Account> CREATOR = new Creator<Account>() {
#Override
public Account createFromParcel(Parcel in) {
return new Account(in);
}
#Override
public Account[] newArray(int size) {
return new Account[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeLong(accountId);
parcel.writeString(accountName);
}
}
Error that is thrown:
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel#8b4252e: Unmarshalling unknown type code 6357091 at offset 140
at android.os.Parcel.readValue(Parcel.java:2453)
at android.os.Parcel.readArrayMapInternal(Parcel.java:2727)
at android.os.BaseBundle.unparcel(BaseBundle.java:269)
at android.os.BaseBundle.getInt(BaseBundle.java:867)
at android.content.Intent.getIntExtra(Intent.java:6637)
at com.david.songshareandroid.activities.ActivityB.init(ActivityB.java:33)
at com.david.songshareandroid.activities.ActivityB.onCreate(ActivityB.java:28)
at android.app.Activity.performCreate(Activity.java:6912)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2877)
In ActivityB when intent.getIntExtra("list_size", 0) is invoked, the error above occurs. If I remove the for loop in ActivityA, that is the objects are not getting passed to ActivityB, then the error does not occur anymore.
You are putting something but getting something else.
You are putting an Int
intent.putExtra("list_size", list.size());
Now on Target Activity do this:
Intent intent = getIntent();
int number = intent.getIntExtra("list_size",0);
and your SomeObject class must implement Parceable interface
First you taking Bundle Extras using intent.getExtras() which returns you bundle object which contains key-value pairs, and then you trying to find pair with key list_size which is not in there. So you should use intent.getIntExtra() instead.
UPD
I don't think that your parcel implementation should work because of Sets. Use Set.toArray() method, then in Parcel.writeTypedArray write it to the parcel.

Recyclerview Inconsistency detected. Invalid view holder adapter positionViewHolder

In my app right now I am working with two lists. One is a List of an Object and the other the a List of Lists of the same object. I have a method in my Activity that transforms a list of Object into a List of Lists of object. And to my adapter I am passing the list of lists through this call:
mAdapter.swap(transformList(pipe.mList));
The method swap is responsible for passing the transformed list to my Adapter:
public void swap(List<List<Feed>> feedList) {
if (finallist == null) {
finallist = new ArrayList<>();
}
finallist.clear();
finallist.addAll(feedList);
}
The transformation checks if there are images in a row and puts all of them inside a single list (I am trying to implement something similar to WhatsApp image grouping). So I have a bunch of messages, they can be text messages, files, images, etc. In case of the last one, I group them in a single list.
Let me give a scenario as an example:
I have four images and 1 text message in my original array. The transformation puts all the four images into a single List of objects and de text message in another list of objects (both of them are inserted in my list of lists).
I thought about two ways to handle this transformation: 1 - do it inside the Adapter and 2 - do it in my Activity and pass the modified list to the Adapter. Each one of this solutions generated a different problem.
By following the steps in 1, I was able to display all of the content almost in the way I wanted. The grouping was working just fine! The problem is that if the original array had a length equals to 30, and the transformed array's length was decreased to 12. The RecyclerView would show all of the remaining 18 items as empty states (so after the transformation it wasn't handling the removing items properly).
By following the steps in 2, I was not able to display all of the content. Just the first element of my array. And I would get a IndexOutOfBoundsException in RecyclerView happens java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder error message. But I was not able to identify the problem. I checked many questions here and none of them helped me.
This is my Adapter class:
public class FeedAdapter extends BaseSkeletonAdapter<Feed> implements FeedHolder.FeedHolderListener{
private static final int HOLDER_COMMENT = 1;
private static final int HOLDER_IMAGE = 2;
private static final int HOLDER_FILE = 3;
private static final int HOLDER_AUDIO = 4;
private static final int HOLDER_MARKER = 5;
private static final int HOLDER_EMPTY = 6;
private static final int HOLDER_GROUP = 7;
private final FeedItemListener mListener;
private final int mAvatarSize;
private final String mUserId;
private final int mPictureSize;
private final int mSkeletonColor;
public static List<List<Feed>> finallist;
public FeedAdapter(FeedItemListener listener, String userId, int avatarSize, int pictureSize, int skeletonColor) {
super(2);
mListener = listener;
mUserId = userId;
mAvatarSize = avatarSize;
mPictureSize = pictureSize;
mSkeletonColor = skeletonColor;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType){
case HOLDER_COMMENT:
case HOLDER_IMAGE:
case HOLDER_FILE:
case HOLDER_MARKER:
case HOLDER_AUDIO:
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_feed, parent, false);
return new FeedHolder(view, this, mPictureSize);
case HOLDER_GROUP:
System.out.println("É um grupo!!!");
View viewGroup = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_feed_group,parent,false);
return new FeedHolder(viewGroup, this, mPictureSize);
case HOLDER_EMPTY:
default:
View empty = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_empty, parent, false);
return new EmptyPlaceholderViewHolder(empty, R.string.placeholder_feed_empty_title, R.string.placeholder_feed_empty_description, R.drawable.ic_feed_placeholder);
}
}
#Override
protected void onBind(RecyclerView.ViewHolder holder, int position) {
if(!(holder instanceof EmptyPlaceholderViewHolder)){
//Feed feed = finallist.get(position).get(0);
if (holder instanceof FeedHolder) {
if (position < finallist.size()) {
if (mUserId.equals(finallist.get(position).get(0).getCreatedById())) {
((FeedHolder) holder).onBind(finallist.get(position), mUserId, mAvatarSize);
} else {
((FeedHolder) holder).onBind(finallist.get(position), mUserId, mAvatarSize);
}
}
}
}
}
#Override
protected void onBind(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
if (payloads.isEmpty()) {
onBindViewHolder(holder, position);
}else {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).onBind(finallist.get(position), payloads, mUserId, mAvatarSize);
}
}
}
#Override
protected void setHolderSkeleton(RecyclerView.ViewHolder holder) {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).setHolderSkeleton(R.drawable.rounded_skeleton, mSkeletonColor);
}
}
#Override
protected void clearHolderSkeleton(RecyclerView.ViewHolder holder) {
if (holder instanceof FeedHolder) {
((FeedHolder) holder).clearHolderSkeleton();
}
}
#Override
public int getItemViewType(int position) {
if(mSkeletonMode){
return HOLDER_COMMENT;
} if (finallist != null && finallist.size() > 0 && position >= 0 && position < finallist.size()) {
System.out.println("Tamanho total: " + finallist.size());
if (finallist.get(position).size() > 1) {
System.out.println("Tamanho do grupo: " + finallist.get(position).size());
return HOLDER_GROUP;
} else {
Feed feed = finallist.get(position).get(0);
if (feed != null) {
String type = feed.getFeedType();
if (type != null) {
switch (type) {
case FEED_IMAGE:
return HOLDER_IMAGE;
case FEED_AUDIO:
return HOLDER_AUDIO;
case FEED_FILE:
return HOLDER_FILE;
case FEED_MARKER:
return HOLDER_MARKER;
case FEED_COMMENT:
default:
return HOLDER_COMMENT;
}
}
}
}
return HOLDER_COMMENT;
}else {
System.out.println("Tá vazia!");
return HOLDER_EMPTY;
}
}
public List<Feed> getItems() {
return returnList(finallist);
}
public List<List<Feed>> getListItems() {
return finallist;
}
public void swap(List<List<Feed>> feedList) {
if (finallist == null) {
finallist = new ArrayList<>();
}
finallist.clear();
finallist.addAll(feedList);
}
#Override
public void toggleLike(final int pos){
if(mListener != null && pos >= 0 && pos < finallist.size()){
mListener.toggleLike(finallist.get(pos).get(0));
}
}
#Override
public void onLongClick(final int pos, final View v) {
if (mListener != null && pos >= 0 && pos < finallist.size()) {
mListener.onLongClick(finallist.get(pos).get(0), v);
}
}
#Override
public int onAudioActionClicked(final int pos, final int progress) {
if (mListener != null) {
return mListener.onAudioActionClicked(pos, finallist.get(pos).get(0), progress);
}else {
return 0;
}
}
#Override
public void onClick(int pos) {
if (finallist!=null && pos >= 0 && pos<finallist.size()) {
Feed feed = finallist.get(pos).get(0);
if (feed != null && mListener != null) {
mListener.onClick(feed);
}
}
}
public interface FeedItemListener {
void toggleLike(#NonNull Feed feed);
void onLongClick(#NonNull Feed feed, #NonNull View v);
void onClick(#NonNull Feed feed);
int onAudioActionClicked(int pos, #NonNull Feed feed, final int progress);
}
private void transformList(List<Feed> mItems) {
finallist = new ArrayList<>();
for (int i = 0; i< mItems.size();i++) {
List<Feed> feed = new ArrayList<>();
feed.add(mItems.get(i));
finallist.add(feed);
}
int j = 0;
List<String> list = new ArrayList<>();
List<Integer> indexList = new ArrayList<>();
//System.out.println("Tamanho: " + mItems.size());
for (int i = 0; i < mItems.size(); i++) {
if (!mItems.get(i).getFeedType().equals("filePicture")) {
if (j >= 4) {
String temp = "";
for (int k = 0; k < j; k++) {
temp = temp + "->" + Integer.toString(i - (k+1));
if (k != 0) {
finallist.get(i - 1).add(finallist.get(i - (k + 1)).get(0));
indexList.add(i - (k+1));
}
}
list.add(temp);
}
j = 0;
} else {
j = j + 1;
}
if (i == mItems.size()-1) {
//System.out.println("Imagem por ultimo!");
if (j >= 4) {
//System.out.println("Grupo vem por ultimo!");
String temp = "";
for (int k = 0; k < j; k++) {
temp = temp + "->" + Integer.toString(i - (k));
if (k != 0) {
finallist.get(i).add(finallist.get(i - (k)).get(0));
indexList.add(i - (k));
}
}
list.add(temp);
}
}
}
Collections.sort(indexList);
int aux = 0;
for (int i = 0; i < indexList.size();i++) {
//System.out.println("Valor da posição: " + indexList.get(i)+ "\nTipo: "+ finallist.get((indexList.get(i).intValue())+aux).get(0).getFeedType()
// +"\nValor da posição + i: " + (indexList.get(i)+aux) + "\nAux: " + aux);
finallist.remove((indexList.get(i).intValue())+aux);
//notifyItemRangeRemoved(0, finallist.size());
//notifyDataSetChanged();
aux = aux - 1;
}
/*for (int i = 0; i< finallist.size(); i++){
if (finallist.get(i).size() > 1) {
System.out.println("groupImage: " + finallist.get(i).size());
} else {
System.out.println(finallist.get(i).get(0).getFeedType());
}
}*/
//returnList(finallist);
notifyItemRangeRemoved(0, returnList(finallist).size() - finallist.size() - 1);
//notifyItemRangeInserted(0, finallist.size());
}
public List<Feed> returnList(List<List<Feed>> lists) {
List<Feed> list = new ArrayList<>();
if (lists != null) {
for (int i = 0; i < lists.size(); i++) {
if (lists.get(i).size() > 1) {
for (int j = 0; j < lists.get(i).size(); j++) {
list.add(lists.get(i).get(j));
}
} else {
list.add(lists.get(i).get(0));
}
}
System.out.println("Tamanho de list: " + list.size());
}
return list;
}
}
And this is my Activity:
public abstract class FeedActivity extends UltraBaseActivity implements FeedAdapter.FeedItemListener, AudioManager.OnAudioFocusChangeListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
private static final String EXTRA_PROJECT_ID = "extra_project_id";
private static final String EXTRA_PROJECT_NAME = "extra_project_name";
private static final String EXTRA_RESOURCE_ID = "extra_resource_id";
private static final String EXTRA_RESOURCE_NAME = "extra_resource_name";
private static final String EXTRA_RESOURCE_KIND = "extra_resource_kind";
#BindView(R.id.swipeLayout) SwipeRefreshLayout mRefreshLayout;
#BindView(R.id.recyclerView) RecyclerView mRecyclerView;
#BindView(R.id.feedCreateFragment) View mFeedCreateLayout;
#BindView(R.id.mic) ImageView mMicView;
private WeakReference<FeedCreateFragment> mFeedCreateFragment;
protected FeedViewModel mViewModel;
protected FeedAdapter mAdapter;
private Feed mLongClick;
private Toolbar mToolbar;
protected int mScrollTo = -1;
protected WrapContentLinearLayoutManager mLayoutManager;
private String mPlayingFeedId;
private int mPlayingPos;
private int mActionResourcePause = R.drawable.ic_pause_black_24dp;
private int mActionResourcePlay = R.drawable.ic_play_black_24dp;
private MediaPlayer mPlayer;
private AudioManager mAudioManager;
protected Handler mAudioHandler;
protected Runnable mUpdateAudioHolderRunnable = new Runnable() {
#Override
public void run() {
try {
if (mPlayer != null && mPlayer.isPlaying()) {
notifyAdapterAudioUpdate(mPlayer.getCurrentPosition(), mActionResourcePause);
mAudioHandler.postDelayed(this, 100);
} else {
mAudioHandler.removeCallbacks(mUpdateAudioHolderRunnable);
}
} catch (IllegalStateException e){
MyLog.e(TAG, "Error while updating seed bar", e);
mAudioHandler.removeCallbacks(mUpdateAudioHolderRunnable);
}
}
};
public static void start(#NonNull Context context, #NonNull String projectId, #NonNull String projectName, #NonNull String resourceId, #NonNull String resourceName, #NonNull String resourceKind){
Intent intent = setIntent(context, projectId, projectName, resourceId, resourceName, resourceKind);
if (intent == null) return;
context.startActivity(intent);
}
public static void startForResult(#NonNull Fragment fragment, #NonNull String projectId, #NonNull String projectName, #NonNull String resourceId, #NonNull String resourceName, #NonNull String resourceKind){
Intent intent = setIntent(fragment.getContext(), projectId, projectName, resourceId, resourceName, resourceKind);
if (intent == null) return;
fragment.startActivityForResult(intent, Constants.Intents.INTENT_REQUEST_VIEW_FEED);
}
#Nullable
protected static Intent setIntent(#NonNull Context context, #NonNull String projectId, #NonNull String projectName, #NonNull String resourceId, #NonNull String resourceName, #NonNull String resourceKind) {
Intent intent;
if (resourceKind.equals(Task.ROUTE)) {
intent = new Intent(context, FeedTaskActivity.class);
}else if(resourceKind.equals(Chat.ROUTE)){
intent = new Intent(context, FeedChatActivity.class);
} else {
MyLog.e(TAG, "Error invalid resource Kind - " + resourceKind);
return null;
}
intent.putExtra(EXTRA_PROJECT_ID, projectId);
intent.putExtra(EXTRA_PROJECT_NAME, projectName);
intent.putExtra(EXTRA_RESOURCE_ID, resourceId);
intent.putExtra(EXTRA_RESOURCE_NAME, resourceName);
intent.putExtra(EXTRA_RESOURCE_KIND, resourceKind);
return intent;
}
public FeedActivity() {
super(R.layout.activity_feed);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
isLogged();
init(getIntent(), savedInstanceState);
super.onCreate(savedInstanceState);
initAdapter();
mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
mAudioHandler = new Handler();
mViewModel.subscribe()
.compose(this.<Resource<List<Feed>>>bindUntilEvent(ActivityEvent.DESTROY))
.flatMap(flatMapDiffUtils())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(onNext(), onError(), onCompleted());
}
#NonNull
private Func1<Resource<List<Feed>>, Observable<FeedViewModel.Pipe>> flatMapDiffUtils() {
return new Func1<Resource<List<Feed>>, Observable<FeedViewModel.Pipe>>() {
#Override
public Observable<FeedViewModel.Pipe> call(Resource<List<Feed>> r) {
if (mViewModel.hasResource()) {
List<Feed> old = mAdapter.getItems();
MyLog.i(TAG, "New length: " + (r.data != null ? r.data.size() : 0) + " - Old: " + old.size());
final FeedDiffCallback cb = new FeedDiffCallback(old, r.data, mUser.getId());
final DiffUtil.DiffResult result = DiffUtil.calculateDiff(cb);
return Observable.just(new FeedViewModel.Pipe(r.data, result, r.status == Status.LOADING && (r.data ==null || r.data.size() == 0)));
} else {
MyLog.i(TAG, "Loading resource from server");
return Observable.empty();
}
}
};
}
private Action1<? super FeedViewModel.Pipe> onNext() {
return new Action1<FeedViewModel.Pipe>() {
#Override
public void call(FeedViewModel.Pipe pipe) {
if (pipe != null) {
initFeedFragment();
mRefreshLayout.setRefreshing(false);
pipe.mDiffResult.dispatchUpdatesTo(mAdapter);
mAdapter.setSkeletonMode(pipe.mSkeletonMode);
//List<List<Feed>> list = new ArrayList<>();
//list = tranformList(pipe.mList);
mAdapter.swap(transformList(pipe.mList));
System.out.println("Tamanho desse troço: " + transformList(pipe.mList).size());
//mAdapter.notifyDataSetChanged();
//mAdapter.notifyItemRangeRemoved(0, pipe.mList.size());
if (mScrollTo == -1) {
mRecyclerView.scrollToPosition(mAdapter.getItemCount()-1);
}else {
mRecyclerView.scrollToPosition(mScrollTo);
}
updateMenuOptions();
showLoading(false);
}
}
};
}
protected Action0 onCompleted() {
return new Action0() {
#Override
public void call() {
MyLog.i(TAG, "Finishing feed activity");
finish();
}
};
}
protected Action1<Throwable> onError() {
return new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
MyLog.e(TAG, "Error", throwable);
}
};
}
#Override
protected void initToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if(mToolbar !=null) {
if (mViewModel != null) {
mToolbar.setTitle(mViewModel.getProjectName());
mToolbar.setSubtitle(mViewModel.getResourceName());
}
mToolbar.setSubtitleTextColor(ContextCompat.getColor(this, R.color.palette_black));
mToolbar.setNavigationIcon(R.drawable.ic_action_back_black);
}
setSupportActionBar(mToolbar);
if (mToolbar != null) {
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
}
}
protected void updateTitle(){
if(mToolbar !=null && mViewModel != null) {
mToolbar.setTitle(mViewModel.getProjectName());
mToolbar.setSubtitle(mViewModel.getResourceName());
}
}
#Override
protected void userUpdated(User user) { }
private void init(Intent i, Bundle b){
if (i != null) {
initViewModel(
i.getStringExtra(EXTRA_PROJECT_ID),
i.getStringExtra(EXTRA_PROJECT_NAME),
i.getStringExtra(EXTRA_RESOURCE_ID),
i.getStringExtra(EXTRA_RESOURCE_NAME),
i.getStringExtra(EXTRA_RESOURCE_KIND));
}else if(b != null){
initViewModel(
b.getString(EXTRA_PROJECT_ID),
b.getString(EXTRA_PROJECT_NAME),
b.getString(EXTRA_RESOURCE_ID),
b.getString(EXTRA_RESOURCE_NAME),
b.getString(EXTRA_RESOURCE_KIND));
}else {
MyLog.i(TAG, "Error while initializing view model");
finish();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_PROJECT_ID, mViewModel.getProjectId());
outState.putString(EXTRA_PROJECT_NAME, mViewModel.getProjectName());
outState.putString(EXTRA_RESOURCE_ID, mViewModel.getResourceId());
outState.putString(EXTRA_RESOURCE_NAME, mViewModel.getResourceName());
outState.putString(EXTRA_RESOURCE_KIND, mViewModel.getResourceKind());
}
private void initAdapter(){
mAdapter = new FeedAdapter(
this,
mUser.getId(),
getResources().getDimensionPixelSize(R.dimen.task_avatar_size),
(int) (AndroidUtils.getScreenWidth(this) * 0.6),
ContextCompat.getColor(this, R.color.bg_skeleton)
);
mRefreshLayout.setColorSchemeResources(R.color.yellow, android.R.color.darker_gray, R.color.yellow_edit_note, android.R.color.background_dark);
mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
mScrollTo = -1;
mViewModel.reload();
}
});
mLayoutManager = new WrapContentLinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
}
private void initFeedFragment(){
if(!(mFeedCreateFragment != null && mFeedCreateFragment.get() != null && getSupportFragmentManager().findFragmentById(R.id.feedCreateFragment) instanceof FeedCreateFragment)){
mFeedCreateFragment = new WeakReference<>(FeedCreateFragment.newInstance(mViewModel.getProjectId(), mViewModel.getResourceId(), mViewModel.getResourceKind(), mViewModel.getResourceUsers()));
getSupportFragmentManager()
.beginTransaction()
.add(R.id.feedCreateFragment, mFeedCreateFragment.get())
.commitAllowingStateLoss();
if (mViewModel.canCreateFeed()) {
mFeedCreateLayout.setVisibility(View.VISIBLE);
}else {
mFeedCreateLayout.setVisibility(View.GONE);
}
}
}
protected abstract void initViewModel(String projectId, String projectName, String resourceId, String resourceName, String resourceKind);
protected abstract void updateMenuOptions();
}
This is part of the Error (the text reached the maximum size):
10-18 17:29:14.702 23722-23722/com.construct.test E/WrapContentLinearLayoutManager: IndexOutOfBoundsException in RecyclerView happens
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{615b449 position=3 id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached no parent}
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5297)
at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5479)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5440)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5436)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2224)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1551)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1511)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:595)
at com.construct.v2.adapters.WrapContentLinearLayoutManager.onLayoutChildren(WrapContentLinearLayoutManager.java:20)
My problem was caused by the BaseSkeletonAdapter<Feed> that my FeedAdapter was extending. BaseSkeletonAdapter was using the original list and not the List of Lists I had to use. So I created a new class SkeletonAdapterFeed that is basically equal to the previous one, but the new one is receiving a List of Lists instead of just the List.
I know that it sounds a little confusing. I don't have full understanding of the project I am working right now so that's why I don't know everything about the classes, etc.

Make ArrayList<ArrayList<Integer>> parcelable

I am trying to make the following Class parcelable. Usually, I do this by using the parcelabler website. But the website seems to have problems with the ArrayList> part. Does anybody know how to do it properly?
Thanks in advance!
package com.example.stoos.data;
import java.util.ArrayList;
import android.os.Parcel;
import android.os.Parcelable;
public class DataModelDiagrammDaten implements Parcelable {
ArrayList<String> Antworten = new ArrayList<String>();
ArrayList<ArrayList<Integer>> AntwortIDs = new ArrayList<ArrayList<Integer>>();
public ArrayList<String> getAntworten() {
return Antworten;
}
public ArrayList<ArrayList<Integer>> getAntwortIDs() {
return AntwortIDs;
}
public void setAntworten(ArrayList<String> antworten) {
Antworten = antworten;
}
public void setAntwortIDs(ArrayList<ArrayList<Integer>> antwortIDs) {
AntwortIDs = antwortIDs;
}
public DataModelDiagrammDaten(ArrayList<String> antworten,
ArrayList<ArrayList<Integer>> antwortIDs) {
super();
Antworten = antworten;
AntwortIDs = antwortIDs;
}
protected DataModelDiagrammDaten(Parcel in) {
if (in.readByte() == 0x01) {
Antworten = new ArrayList<String>();
in.readList(Antworten, String.class.getClassLoader());
} else {
Antworten = null;
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (Antworten == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(Antworten);
}
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<DataModelDiagrammDaten> CREATOR = new Parcelable.Creator<DataModelDiagrammDaten>() {
#Override
public DataModelDiagrammDaten createFromParcel(Parcel in) {
return new DataModelDiagrammDaten(in);
}
#Override
public DataModelDiagrammDaten[] newArray(int size) {
return new DataModelDiagrammDaten[size];
}
};
}
I think it is a little late, but hope it's still useful for others.
I tried to write a two dimensional Array to Parcel like this:
public class Avatar implements Parcelable {
ArrayList<Integer> arrayList;
ArrayList<ArrayList<Integer>> arrayLists;
public Avatar() {
arrayLists = new ArrayList<>();
for (int i = 0; i < 10; i++) {
ArrayList<Integer> temp = new ArrayList<>();
for (int j = 0; j < 10; j++) {
temp.add(j);
}
arrayLists.add(temp);
}
arrayList = new ArrayList<>();
for (int i = 100; i < 104; i++) {
arrayList.add(i);
}
}
protected Avatar(Parcel in) {
arrayList = new ArrayList<>();
in.readList(arrayList, Integer.class.getClassLoader());
arrayLists = new ArrayList<>();
in.readList(arrayLists, ArrayList.class.getClassLoader());
}
public static final Creator<Avatar> CREATOR = new Creator<Avatar>() {
#Override
public Avatar createFromParcel(Parcel in) {
return new Avatar(in);
}
#Override
public Avatar[] newArray(int size) {
return new Avatar[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(arrayList);
dest.writeList(arrayLists);
}
#Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < arrayList.size(); i++) {
stringBuilder.append(arrayList.get(i) + " ");
}
for (int i = 0; i < arrayLists.size(); i++) {
for (int j = 0; j < arrayLists.get(i).size(); j++) {
stringBuilder.append(arrayLists.get(i).get(j) + " ");
}
}
return stringBuilder.toString();
}
}
And send the class to another Activity:
avatar = new Avatar();
Intent intent = new Intent();
intent.setClass(ParcelActivity.this, Parcel2Activity.class);
intent.putExtra("avatar", avatar);
startActivity(intent);
Receive in another Activity:
textView.setText(getIntent().getParcelableExtra("avatar") + "");
It works correctly:
test
What should be careful is that writing and reading for the field objects in Parcel must be in the same sequence.
EDIT:
Try to put the second ArrayList in another class like this:
public class IdsObject implements Parcelable {
ArrayList<Integer> ids;
public IdsObject(ArrayList<Integer> ids) {
this.ids = ids;
}
protected IdsObject(Parcel in) {
if (in.readByte() == 0x01) {
ids = new ArrayList<Integer>();
in.readList(ids, Integer.class.getClassLoader());
} else {
ids = null;
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (ids == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(ids);
}
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<IdsObject> CREATOR = new Parcelable.Creator<IdsObject>() {
#Override
public IdsObject createFromParcel(Parcel in) {
return new IdsObject(in);
}
#Override
public IdsObject[] newArray(int size) {
return new IdsObject[size];
}
};
}
Then, use this class on your class like this:
public class DataModelDiagrammDaten implements Parcelable, Parcelable {
ArrayList<String> Antworten = new ArrayList<String>();
ArrayList<IdsObject> AntwortIDs = new ArrayList<IdsObject>();
protected DataModelDiagrammDaten(Parcel in) {
if (in.readByte() == 0x01) {
Antworten = new ArrayList<String>();
in.readList(Antworten, String.class.getClassLoader());
} else {
Antworten = null;
}
if (in.readByte() == 0x01) {
AntwortIDs = new ArrayList<IdsObject>();
in.readList(AntwortIDs, IdsObject.class.getClassLoader());
} else {
AntwortIDs = null;
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
if (Antworten == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(Antworten);
}
if (AntwortIDs == null) {
dest.writeByte((byte) (0x00));
} else {
dest.writeByte((byte) (0x01));
dest.writeList(AntwortIDs);
}
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<DataModelDiagrammDaten> CREATOR = new Parcelable.Creator<DataModelDiagrammDaten>() {
#Override
public DataModelDiagrammDaten createFromParcel(Parcel in) {
return new DataModelDiagrammDaten(in);
}
#Override
public DataModelDiagrammDaten[] newArray(int size) {
return new DataModelDiagrammDaten[size];
}
};
}
Ok, I solved it. Here is the solution in case anybody else faces the same problem:
You can first make your class with an normal ArrayList (instead of ArrayList> ) parcelable with the website. Then take the following snippet an write it into the Constructor with the Parcel argument at the place, were the ArrayList was. Now change your ArrayList back to your ArrayList> and it should work.
if (in.readByte() == 0x01) {
IDs = new ArrayList<ArrayList<Integer>>();
Type collectionType = new TypeToken<ArrayList<Integer>>(){}.getType();
in.readList(IDs, collectionType.getClass().getClassLoader());
} else {
IDs = null;
}

When passing 2 identical Parcable Classes, 1 is null

i have a weird issue.
I'm trying to pass 2 parcable classesfrom one activity to another.
I define both of them the exact same way, but of them is null.
The parcable class :
class Friends implements Parcelable {
private ArrayList<Integer> ids = new ArrayList<Integer>();
private ArrayList<String> names = new ArrayList<String>();
private ArrayList<Bitmap> images = new ArrayList<Bitmap>();
public void addId(Integer id)
{
ids.add(id);
}
public void addName(String name){
names.add(name);
}
public void addImage(Bitmap img){
images.add(img);
}
public ArrayList<Integer> getIds() {
return ids;
}
public ArrayList<String> getNames() {
return names;
}
public ArrayList<Bitmap> getImages() {
return images;
}
#Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(ids);
dest.writeList(names);
dest.writeList(images);
}
public static final Parcelable.Creator<Friends> CREATOR = new Parcelable.Creator<Friends>() {
public Friends createFromParcel(Parcel in) {
return new Friends(in);
}
public Friends[] newArray(int size) {
return new Friends[size];
}
};
public Friends(Parcel in){
in.readList(ids, null);
in.readList(names, null);
in.readList(images, null);
}
public Friends(Integer id, String name, Bitmap img) {
ids.add(id);
names.add(name);
images.add(img);
}
public Friends(){
}
The Sending part :
for(Integer position : selectedIds)
{
String name = a.getItem(position).getFriendName();
int id = a.getItem(position).getFriendId();
Bitmap img = a.getItem(position).getFriendImage();
Log.e("ID",String.valueOf(id));
selectedFriends.addId(new Integer(id));
selectedFriends.addName(name);
selectedFriends.addImage(img);
}
for(int position=0;position<list.getCount(); position++)
{
String name = a.getItem(position).getFriendName();
int id = a.getItem(position).getFriendId();
Bitmap img = a.getItem(position).getFriendImage();
Log.e("All IDs",String.valueOf(id));
allFriends.addId(new Integer(id));
allFriends.addName(name);
allFriends.addImage(img);
}
b.putParcelable("selecet_friends", selectedFriends);
b.putParcelable("all_friends", allFriends);
data.putExtras(b);
Both of the loops are being runned ( i can see the logs), all variables you don't see are being initialized correctly, everything is fine.
The Reciving part :
i define both as null :
private Friends selectedFriends = null;
private Friends allFriends = null;
And handle the onResult like this :
Log.e("Result","yessss");
Friends all_friends = (Friends)data.getParcelableExtra("all_friends");
Friends selected_friends = (Friends)data.getParcelableExtra("selected_friends");
allFriends = all_friends;
selectedFriends = selected_friends;
if(selectedFriends != null){
Log.e("is null","No");
}
if(allFriends != null){
Log.e("is all null","No");
}
Does anyone know how come the selectedFriends is null when allFriends is not?
EDIT:
Just a thought, but maybe it's because i put 2 parcables on a Bundle?
just i just add 2 bundles?
In the sending method you have a typo in this line:
b.putParcelable("selecet_friends", selectedFriends);
try this instead:
b.putParcelable("selected_friends", selectedFriends);
Also, you should use more specific names for the keys. The documentation for putExtras() says:
Add a set of extended data to the intent. The keys must include a
package prefix, for example the app com.android.contacts would use
names like "com.android.contacts.ShowAll

How to reuse the Parcelable object in Android?

I have the class that implements Parcelable interface. That contain HashMap, this HashMap contains bitmap images. I need this HashMap for all my activities. So I used Parcelable. Look on my Parcelable code.
private HashMap<String, Bitmap> map;
public ParcelFullData() {
map = new HashMap();
}
public ParcelFullData(Parcel in) {
map = new HashMap();
readFromParcel(in);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public ParcelFullData createFromParcel(Parcel in) {
return new ParcelFullData(in);
}
public ParcelFullData[] newArray(int size) {
return new ParcelFullData[size];
}
};
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(map.size());
for (String s: map.keySet()) {
dest.writeString(s);
dest.writeValue(map.get(s));
}
}
public void readFromParcel(Parcel in) {
int count = in.readInt();
for (int i = 0; i < count; i++) {
map.put(in.readString(), (Bitmap)in.readValue(Bitmap.class.getClassLoader()));
}
}
public Bitmap get(String key) {
return map.get(key);
}
public String[] getKeys() {
Set<String> mapset = map.keySet();
String[] photoName = new String[mapset.size()];
Iterator<String> itr = mapset.iterator();
int index = 0;
while (itr.hasNext()) {
String name = itr.next();
photoName[index] = name;
index++;
}
return photoName;
}
public void put(String key, Bitmap value) {
map.put(key, value);
}
public HashMap<String, Bitmap> getHashMap() {
return map;
}
This will working fine when i pass the one Activity to another Activity.( For example Activity A to Activity B.) But If I pass this to another Activity( For example Activity B to Activity C.) means that HashMap contains no elements. That size is 0.
In Activity_A,
Intent setIntent = new Intent(activity_a, Activity_B.class);
ParcelFullData parcelData = new ParcelFullData();
Bundle b = new Bundle();
b.putParcelable("parcelData", parcelData);
startActivity(setIntent);
In Activity_B
ParcelFullData parcelData = (ParcelFullData)bundle.getParcelable("parcelData");
Parcel parcel = Parcel.obtain();
parcel.writeParcelable(parcelData , 0);
parcelData.writeToParcel(parcel, 0);
Intent setIntent = new Intent(activity_a, Activity_B.class);
Bundle b = new Bundle();
b.putParcelable("parcelData", parcelData);
startActivity(setIntent);
I'm doing the same in Activity C.
How to use resolve this?
Why do you use bundle? You don't add this bundle to intent. Use Intent.putExtra(String, Parcelable) and everything will be OK. I pass one Parcelable object through all activities in my app and it works fine.
In first activity:
Intent i = new Intent(activity_a, Activity_B.class);
i.putExtra("parcelData", parcelData);
In second activity:
parcelData = getIntent().getParcelableExtra("parcelData");
// ... do anything with parcelData here
Intent i = new Intent(activity_b, Activity_C.class);
i.putExtra("parcelData", parcelData);

Categories

Resources