android 应用程序崩溃时未获取堆栈跟踪

Not getting stacktrace when android app crashes

我正在制作一个 android 应用程序,它与 json 休息 api 进行通信,以便为用户提供电影和电视节目的详细信息。

目前,我正在(手动)测试应用程序以修复应用程序离线时的错误。

当我在主回收器视图中单击某个项目并启动详细信息 activity 时,我遇到了一个奇怪的错误。 Android 监视器仅显示以下内容:

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

它不打印堆栈跟踪,所以我没有可以开始调试的起点。

我知道崩溃发生在详细信息 activity 启动后,因为我设置了几个断点来尝试确定它何时发生。

崩溃发生在我的详细信息片段的 onstart() 回调完成之后。

如何获得完整的堆栈跟踪?

此外,如果有一个明显的错误我没有看到,你能帮我指出吗?

这是我的详细信息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);
}
}

这是我的详细信息片段:

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() {

    }
}

而且,如果它可以帮助,这是我的主持人:

@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);
    }
}

在您的 onStart 中,您不能使用或调用任何布局,您在 onStart 中使用:

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

将此放入您的 onCreateonResume

你也不能使用 findViewById

可能DataManager有问题

您应该始终执行 onError 操作。尝试记录错误:

  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);
}