我正在制作一个与json rest api通信的Android应用程序,为用户提供电影和电视节目的详细信息。
目前,我正在测试应用程序(手动)以在应用程序离线时修复错误。
当我点击主Recyclerview中的项目并启动详细信息活动时,我收到一个奇怪的错误。 Android监视器仅显示以下内容:
09-13 13:39:43.860 30608-30608/<MY-PACKAGE-NAME> E/AndroidRuntime: FATAL EXCEPTION: main
Process: <MY-PACKAGE-NAME>, PID: 30608
它没有打印堆栈跟踪,所以我没有一个起点可以从中开始调试。
我知道崩溃发生在细节活动启动之后,因为我设置了几个断点来尝试识别它何时发生。
崩溃发生在我的详细信息片段的onstart()回调完成之后。
如何获得完整的堆栈跟踪?
另外,如果有一个我不明白的明显错误,你能指点一下吗?
这是我的详细活动:
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);
}
}
答案 0 :(得分:1)
可能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);
}
答案 1 :(得分:0)
在onStart
中,您无法使用或调用任何布局,而是在onStart
中使用
int newAlpha = getTransparencyRatio(mContainer.getScrollY());
updateActionBarTransparency(newAlpha);
将其放入onCreate
或onResume
。
您也不能使用findViewById
。