Not getting stacktrace when android app crashes

I am making an android app that communicates with a json rest api to provide the user with movie and tv show details.

Currently, I am testing the app (manually) to fix bugs when the app is offline.

I am getting a strange bug when I click on an item in the main recyclerview and the details activity is launched. The Android monitor only shows the following:

09-13 13:39:43.860 30608-30608/<MY-PACKAGE-NAME> E/AndroidRuntime: FATAL EXCEPTION: main
                                                                                     Process: <MY-PACKAGE-NAME>, PID: 30608

It doesn't print a stacktrace, so I don't have a starting point from which I can start to debug.

I know that the crash happens AFTER the details activity is launched, as I set several breakpoints to try and identify when it happens.

The crash happens AFTER the onstart() callback of my details fragment finishes.

How can I get a complete stacktrace?

Also, if there is a glaring error that I don't see, could you point it out for me please?

This is my details activity:

public class DetailActivity extends SearchBaseActivity
    implements NestedScrollView.OnScrollChangeListener, FragmentHandler {

private static final String RESOURCE = "resource";

@BindView(R.id.detail_view_container)
NestedScrollView mContainer;
@BindView(R.id.toolbar)
Toolbar mToolbar;
@BindView(R.id.reviews_view_container)
FrameLayout mReviewsContainer;

private Drawable mToolbarBackgroundDrawable;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private int mScrollFadeLimit;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_detail);
    ButterKnife.bind(this);

    Media media = getIntent().getParcelableExtra(RESOURCE);

    mToolbarBackgroundDrawable = mToolbar.getBackground();

    setSupportActionBar(mToolbar);
    ActionBar actionBar = getSupportActionBar();
    if (actionBar != null) {
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setTitle(null);
    }

    getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);
    mScrollFadeLimit = ViewUtil.setHeightForAspectRatio(mDisplayMetrics.widthPixels, ViewUtil.STANDARD);

    mContainer.setOnScrollChangeListener(this);
    MediaDetailFragment.insertOnTarget(this, R.id.detail_view_container, media);
}

@Override
protected void onStart() {
    super.onStart();
    int newAlpha = getTransparencyRatio(mContainer.getScrollY());
    updateActionBarTransparency(newAlpha);
}

@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
    int newAlpha = getTransparencyRatio(scrollY);
    updateActionBarTransparency(newAlpha);

}

public static Intent getStartIntent(Context context, Media media) {
    return new Intent(context, DetailActivity.class).putExtra(RESOURCE, media);
}

private int getTransparencyRatio(int scrollY) {
    int headerHeight = mScrollFadeLimit - mToolbar.getHeight();
    float ratio = 0;
    if (scrollY > 0 && headerHeight > 0)
        ratio = (float) Math.min(Math.max(scrollY, 0), headerHeight) / headerHeight;

    return (int) (ratio * 255);
}

private void updateActionBarTransparency(int scrollRatio) {
    mToolbarBackgroundDrawable.mutate().setAlpha(scrollRatio);
    mToolbar.setBackground(mToolbarBackgroundDrawable);
}

@Override
public void onReviewsRequested(Media media) {
    if (mReviewsContainer != null) {
        mReviewsContainer.setVisibility(View.VISIBLE);
    }
    ReviewFragment.insertOnTarget(this, R.id.reviews_view_container, media);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == android.R.id.home) {
        if (findViewById(android.R.id.list) != null) {
            onBackPressed();
            return true;
        }
    }
    return super.onOptionsItemSelected(item);
}
}

This is my details fragment:

public class MediaDetailFragment extends Fragment implements DetailsMvpView {

    public static final String DETAILS_FRAGMENT_NAME = MediaDetailFragment.class.getName();
    private static final String RESOURCE = "resource";
    private static final String POSTER_SIZE = "w342/";
    private static ViewTreeObserver.OnGlobalLayoutListener mListener;

    @Inject
    DetailPresenter mDetailPresenter;
    @Inject
    CreditPresenter mCreditPresenter;
    @BindView(R.id.media_image_flipper)
    ViewFlipper mImageFlipper;
    @BindView(R.id.title_textview)
    TextView mTitleTextView;
    @BindView(R.id.button_share)
    ImageButton mShare;
    @BindView(R.id.button_trailers)
    ImageButton mTrailers;
    @BindView(R.id.button_reviews)
    ImageButton mReviews;
    @BindView(R.id.keyword_recyclerview)
    RecyclerView mKeywordRecyclerView;
    @BindView(R.id.overview_content_textview)
    TextView mOverviewTextView;
    @BindView(R.id.main_image_holder)
    ViewGroup mImageHolder;
    @BindView(R.id.parent_cardview)
    CardView mParentCardView;
    @BindView(R.id.cast_recyclerview)
    CreditRecyclerView mCastRecyclerView;
    @BindView(R.id.crew_recyclerview)
    CreditRecyclerView mCrewRecyclerView;
    @BindView(R.id.empty_view)
    TextView mRecyclerviewEmpty;

    @BindString(R.string.youtube_base_url)
    String mYoutubeUrl;
    @BindBool(R.bool.isTablet)
    boolean mIsTablet;

    private Media mMedia;
    private DisplayMetrics mDisplayMetrics = new DisplayMetrics();

    public static MediaDetailFragment insertOnTarget(AppCompatActivity activity, int target, Media media) {
        Bundle bundle = new Bundle();
        bundle.putParcelable(RESOURCE, media);
        MediaDetailFragment fragment = (MediaDetailFragment) MediaDetailFragment.instantiate(activity, DETAILS_FRAGMENT_NAME, bundle);
        FragmentManager fm = activity.getSupportFragmentManager();
        fm.beginTransaction()
                .replace(target, fragment, DETAILS_FRAGMENT_NAME)
                .commit();
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMedia = getArguments().getParcelable(RESOURCE);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.media_details_fragment, container, false);
        ((BaseActivity) getActivity()).activityComponent().inject(this);
        ButterKnife.bind(this, rootView);

        getActivity().getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);

        mTitleTextView.setText(mMedia.title());
        mOverviewTextView.setText(mMedia.overview());

        mKeywordRecyclerView.setAdapter(new KeywordAdapter(getContext()));
        mCastRecyclerView.setAdapter(R.layout.item_credit_normal, mRecyclerviewEmpty);
        mCrewRecyclerView.setAdapter(R.layout.item_credit_normal, mRecyclerviewEmpty);

        return rootView;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        final View view = getView();
        if (view == null) return;
        mListener = () -> {
            view.post(() -> {
                if (mIsTablet) {
                    mImageFlipper.getLayoutParams().height =
                            ViewUtil.setHeightForAspectRatio(view.getWidth(), ViewUtil.STANDARD);
                } else {
                    mImageFlipper.getLayoutParams().height =
                            ViewUtil.setHeightForAspectRatio(mDisplayMetrics.widthPixels, ViewUtil.STANDARD);
                }
                view.getViewTreeObserver().removeOnGlobalLayoutListener(mListener);
            });
        };
        view.getViewTreeObserver().addOnGlobalLayoutListener(mListener);
    }

    @Override
    public void onStart() {
        super.onStart();
        mShare.setOnClickListener(v -> startActivity(new Intent(Intent.ACTION_SEND)
                .putExtra(Intent.EXTRA_TEXT, getActivity().getString(R.string.base_youtube_url)
                        + POSTER_SIZE + mMedia.posterPath() + "\n\n"
                        + mMedia.title() + "\n\n" + mMedia.overview())
                .setType("text/plain")));
        mTrailers.setOnClickListener(v -> mDetailPresenter.loadMovies(mMedia.id()));
        mReviews.setOnClickListener(v -> ((FragmentHandler) getActivity()).onReviewsRequested(mMedia));

        mDetailPresenter.attachView(this);
        mDetailPresenter.loadImages(mMedia.id());
        mDetailPresenter.loadKeywords(mMedia.id());

        mCreditPresenter.attachCastView(mCastRecyclerView);
        mCreditPresenter.attachCrewView(mCrewRecyclerView);
        mCreditPresenter.loadCredits(mMedia.id());
    }

    @Override
    public void onStop() {
        mDetailPresenter.detachView();
        mCreditPresenter.detachCastView();
        mCreditPresenter.detachCrewView();
        super.onStop();
    }

    @Override
    public void showImages(String images) {
        ImageView imageView = new ImageView(getContext());
        mImageFlipper.addView(imageView, new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        ViewUtil.loadImage(images, imageView, getContext(), true, true);
    }

    @Override
    public void showKeywords(List<String> keywords) {
        ((KeywordAdapter) mKeywordRecyclerView.getAdapter()).setKeywords(keywords);
    }

    @Override
    public void showVideos(List<Video> videos) {
        TrailerDialogFragment.newInstance(videos).show(getFragmentManager(), null);
    }

    @Override
    public void showError() {

    }
}

And, in case it can help, this is my presenter:

@ConfigPersistent
public class DetailPresenter extends BasePresenter<DetailsMvpView> {

    private final DataManager mDataManager;
    private List<Subscription> mSubscriptions = new ArrayList<>();


    @Inject
    public DetailPresenter(DataManager dataManager) {
        mDataManager = dataManager;
    }

    @Override
    public void attachView(DetailsMvpView mvpView) {
        super.attachView(mvpView);
    }

    @Override
    public void detachView() {
        super.detachView();
        if (mSubscriptions != null) {
            for (Subscription subscription : mSubscriptions) {
                subscription.unsubscribe();
            }
        }
    }

    public void loadImages(String id) {
        checkViewAttached();
        Subscription subscription = mDataManager.getMovieImages(id)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(getMvpView()::showImages);
        mSubscriptions.add(subscription);
    }

    public void loadKeywords(String id) {
        checkViewAttached();
        Subscription subscription = mDataManager.getMovieKeywords(id)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(getMvpView()::showKeywords);
        mSubscriptions.add(subscription);
    }

    public void loadMovies(String id) {
        checkViewAttached();
        Subscription subscription = mDataManager.getVideosForMovie(id)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe(getMvpView()::showVideos);
        mSubscriptions.add(subscription);
    }
}

Upvotes: 0

Views: 320

Answers (2)

Silo&#233; Bezerra Bispo
Silo&#233; Bezerra Bispo

Reputation: 2244

In your onStart you can't use or call anything of your layout, you are using in your onStart:

int newAlpha = getTransparencyRatio(mContainer.getScrollY());
    updateActionBarTransparency(newAlpha);

Put this in your onCreate or onResume.

You can't use findViewById as well.

Upvotes: 0

Lucy Fair
Lucy Fair

Reputation: 891

Maybe there is something wrong in DataManager

You should always implement onError action. Try to log an error:

  public void loadImages(String id) {
    checkViewAttached();
    Subscription subscription = mDataManager.getMovieImages(id)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .subscribe(
                 getMvpView()::showImages,
                 { error -> error.printStackTrace() }
            );
    mSubscriptions.add(subscription);
}

Upvotes: 1

Related Questions