개발자 콘솔 오류 보고서에서 때때로 NPE 문제가있는 보고서가 표시됩니다. 내 코드의 문제점을 이해하지 못합니다. 에뮬레이터에서 내 장치 응용 프로그램은 강제 종료없이 잘 작동하지만 getActivity () 메서드가 호출되면 일부 사용자가 조각 클래스에서 NullPointerException을 얻습니다.
활동
pulic class MyActivity extends FragmentActivity{
private ViewPager pager;
private TitlePageIndicator indicator;
private TabsAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
pager = (ViewPager) findViewById(R.id.pager);
indicator = (TitlePageIndicator) findViewById(R.id.indicator);
adapter = new TabsAdapter(getSupportFragmentManager(), false);
adapter.addFragment(new FirstFragment());
adapter.addFragment(new SecondFragment());
indicator.notifyDataSetChanged();
adapter.notifyDataSetChanged();
// push first task
FirstTask firstTask = new FirstTask(MyActivity.this);
// set first fragment as listener
firstTask.setTaskListener((TaskListener) adapter.getItem(0));
firstTask.execute();
}
indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Fragment currentFragment = adapter.getItem(position);
((Taskable) currentFragment).executeTask();
}
@Override
public void onPageScrolled(int i, float v, int i1) {}
@Override
public void onPageScrollStateChanged(int i) {}
});
}
AsyncTask 클래스
public class FirstTask extends AsyncTask{
private TaskListener taskListener;
...
@Override
protected void onPostExecute(T result) {
...
taskListener.onTaskComplete(result);
}
}
프래그먼트 클래스
public class FirstFragment extends Fragment immplements Taskable, TaskListener{
public FirstFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.first_view, container, false);
}
@Override
public void executeTask() {
FirstTask firstTask = new FirstTask(MyActivity.this);
firstTask.setTaskListener(this);
firstTask.execute();
}
@Override
public void onTaskComplete(T result) {
// NPE is here
Resources res = getActivity().getResources();
...
}
}
응용 프로그램이 백그라운드에서 다시 시작되면이 오류가 발생할 수 있습니다. 이 경우 어떻게이 상황을 올바르게 처리해야합니까?
답변
내 문제에 대한 해결책을 찾은 것 같습니다. 여기 와 여기에 아주 좋은 설명이 있습니다 . 내 예는 다음과 같습니다.
pulic class MyActivity extends FragmentActivity{
private ViewPager pager;
private TitlePageIndicator indicator;
private TabsAdapter adapter;
private Bundle savedInstanceState;
@Override
public void onCreate(Bundle savedInstanceState) {
....
this.savedInstanceState = savedInstanceState;
pager = (ViewPager) findViewById(R.id.pager);;
indicator = (TitlePageIndicator) findViewById(R.id.indicator);
adapter = new TabsAdapter(getSupportFragmentManager(), false);
if (savedInstanceState == null){
adapter.addFragment(new FirstFragment());
adapter.addFragment(new SecondFragment());
}else{
Integer count = savedInstanceState.getInt("tabsCount");
String[] titles = savedInstanceState.getStringArray("titles");
for (int i = 0; i < count; i++){
adapter.addFragment(getFragment(i), titles[i]);
}
}
indicator.notifyDataSetChanged();
adapter.notifyDataSetChanged();
// push first task
FirstTask firstTask = new FirstTask(MyActivity.this);
// set first fragment as listener
firstTask.setTaskListener((TaskListener) getFragment(0));
firstTask.execute();
}
private Fragment getFragment(int position){
return savedInstanceState == null ? adapter.getItem(position) : getSupportFragmentManager().findFragmentByTag(getFragmentTag(position));
}
private String getFragmentTag(int position) {
return "android:switcher:" + R.id.pager + ":" + position;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tabsCount", adapter.getCount());
outState.putStringArray("titles", adapter.getTitles().toArray(new String[0]));
}
indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Fragment currentFragment = adapter.getItem(position);
((Taskable) currentFragment).executeTask();
}
@Override
public void onPageScrolled(int i, float v, int i1) {}
@Override
public void onPageScrollStateChanged(int i) {}
});
이 코드의 주요 아이디어는 애플리케이션을 정상적으로 실행하는 동안 새 단편을 작성하여 어댑터에 전달한다는 것입니다. 응용 프로그램 프래그먼트 관리자를 재개 할 때이 프래그먼트의 인스턴스가 이미 있으며 프래그먼트 관리자에서 가져 와서 어댑터로 전달해야합니다.
최신 정보
또한 getActivity ()가 호출되기 전에 프래그먼트를 사용하여 iPart를 확인하는 것이 좋습니다. 이것은 프래그먼트가 활동에서 분리 될 때 널 포인터 예외를 피하는 데 도움이됩니다. 예를 들어, 활동에는 비동기 작업을 푸시하는 조각이 포함될 수 있습니다. 작업이 완료되면 onTaskComplete 리스너가 호출됩니다.
@Override
public void onTaskComplete(List<Feed> result) {
progress.setVisibility(View.GONE);
progress.setIndeterminate(false);
list.setVisibility(View.VISIBLE);
if (isAdded()) {
adapter = new FeedAdapter(getActivity(), R.layout.feed_item, result);
list.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}
프래그먼트를 열고 작업을 푸시 한 다음 신속하게 뒤로 눌러 이전 활동으로 돌아 가면 작업이 완료되면 getActivity () 메소드를 호출하여 onPostExecute ()의 활동에 액세스하려고 시도합니다. 활동이 이미 분리되어 있고이 점검이없는 경우 :
if (isAdded())
그런 다음 응용 프로그램이 충돌합니다.
답변
좋아, 나는이 질문이 실제로 해결되었다는 것을 알고 있지만 이것에 대한 해결책을 공유하기로 결정했다. 내 추상 부모 클래스를 만들었습니다 Fragment
.
public abstract class ABaseFragment extends Fragment{
protected IActivityEnabledListener aeListener;
protected interface IActivityEnabledListener{
void onActivityEnabled(FragmentActivity activity);
}
protected void getAvailableActivity(IActivityEnabledListener listener){
if (getActivity() == null){
aeListener = listener;
} else {
listener.onActivityEnabled(getActivity());
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (aeListener != null){
aeListener.onActivityEnabled((FragmentActivity) activity);
aeListener = null;
}
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (aeListener != null){
aeListener.onActivityEnabled((FragmentActivity) context);
aeListener = null;
}
}
}
보시다시피, 나는 리스너를 추가 했으므로 Fragments
Activity
standard 대신에 필요할 때마다 getActivity()
전화해야합니다.
getAvailableActivity(new IActivityEnabledListener() {
@Override
public void onActivityEnabled(FragmentActivity activity) {
// Do manipulations with your activity
}
});
답변
이것을 제거하는 가장 좋은 방법 onAttach
은 호출 될 때 활동 참조를 유지 하고 필요한 경우 활동 참조를 사용하는 것입니다.
@Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
@Override
public void onDetach() {
super.onDetach();
mContext = null;
}
onAttach(Activity)
감가 상각되어 현재 onAttach(Context)
사용 중이므로 수정되었습니다.
답변
부모 활동에서 onStart가 될 때까지 Fragment 내에서 getActivity ()가 필요한 메소드를 호출하지 마십시오.
private MyFragment myFragment;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
myFragment = new MyFragment();
ft.add(android.R.id.content, youtubeListFragment).commit();
//Other init calls
//...
}
@Override
public void onStart()
{
super.onStart();
//Call your Fragment functions that uses getActivity()
myFragment.onPageSelected();
}
답변
나는 이런 종류의 문제 를 잠시 동안 싸우고 있었고, 나는 신뢰할 수있는 해결책을 생각해 냈다고 생각합니다.
그것은 그 확실히 알고 꽤 어려운 this.getActivity()
반환하지 않을 null
A에 대한 Fragment
당신의 코드를 철회 할 수있는 충분한 시간 제공 네트워크 동작의 모든 종류의 상대하고 특히, Activity
참조.
아래 솔루션에서 나는이라는 작은 관리 클래스를 선언합니다 ActivityBuffer
. 본질적으로 이것은 class
소유에 대한 신뢰할 수있는 참조를 유지 하고 유효한 참조가있을 때마다 유효한 컨텍스트 내 Activity
에서 실행을 약속 합니다. 의는 즉시 경우 UI 스레드에서 실행 예정이다 그 때까지 그렇지 않으면 실행이 지연되고 있습니다 준비가되어 있습니다.Runnable
Activity
Runnable
Context
Context
/** A class which maintains a list of transactions to occur when Context becomes available. */
public final class ActivityBuffer {
/** A class which defines operations to execute once there's an available Context. */
public interface IRunnable {
/** Executes when there's an available Context. Ideally, will it operate immediately. */
void run(final Activity pActivity);
}
/* Member Variables. */
private Activity mActivity;
private final List<IRunnable> mRunnables;
/** Constructor. */
public ActivityBuffer() {
// Initialize Member Variables.
this.mActivity = null;
this.mRunnables = new ArrayList<IRunnable>();
}
/** Executes the Runnable if there's an available Context. Otherwise, defers execution until it becomes available. */
public final void safely(final IRunnable pRunnable) {
// Synchronize along the current instance.
synchronized(this) {
// Do we have a context available?
if(this.isContextAvailable()) {
// Fetch the Activity.
final Activity lActivity = this.getActivity();
// Execute the Runnable along the Activity.
lActivity.runOnUiThread(new Runnable() { @Override public final void run() { pRunnable.run(lActivity); } });
}
else {
// Buffer the Runnable so that it's ready to receive a valid reference.
this.getRunnables().add(pRunnable);
}
}
}
/** Called to inform the ActivityBuffer that there's an available Activity reference. */
public final void onContextGained(final Activity pActivity) {
// Synchronize along ourself.
synchronized(this) {
// Update the Activity reference.
this.setActivity(pActivity);
// Are there any Runnables awaiting execution?
if(!this.getRunnables().isEmpty()) {
// Iterate the Runnables.
for(final IRunnable lRunnable : this.getRunnables()) {
// Execute the Runnable on the UI Thread.
pActivity.runOnUiThread(new Runnable() { @Override public final void run() {
// Execute the Runnable.
lRunnable.run(pActivity);
} });
}
// Empty the Runnables.
this.getRunnables().clear();
}
}
}
/** Called to inform the ActivityBuffer that the Context has been lost. */
public final void onContextLost() {
// Synchronize along ourself.
synchronized(this) {
// Remove the Context reference.
this.setActivity(null);
}
}
/** Defines whether there's a safe Context available for the ActivityBuffer. */
public final boolean isContextAvailable() {
// Synchronize upon ourself.
synchronized(this) {
// Return the state of the Activity reference.
return (this.getActivity() != null);
}
}
/* Getters and Setters. */
private final void setActivity(final Activity pActivity) {
this.mActivity = pActivity;
}
private final Activity getActivity() {
return this.mActivity;
}
private final List<IRunnable> getRunnables() {
return this.mRunnables;
}
}
구현 측면에서 Pawan M의 위에서 설명한 동작과 일치하도록 수명주기 방법을 적용하도록주의를 기울여야합니다 .
public class BaseFragment extends Fragment {
/* Member Variables. */
private ActivityBuffer mActivityBuffer;
public BaseFragment() {
// Implement the Parent.
super();
// Allocate the ActivityBuffer.
this.mActivityBuffer = new ActivityBuffer();
}
@Override
public final void onAttach(final Context pContext) {
// Handle as usual.
super.onAttach(pContext);
// Is the Context an Activity?
if(pContext instanceof Activity) {
// Cast Accordingly.
final Activity lActivity = (Activity)pContext;
// Inform the ActivityBuffer.
this.getActivityBuffer().onContextGained(lActivity);
}
}
@Deprecated @Override
public final void onAttach(final Activity pActivity) {
// Handle as usual.
super.onAttach(pActivity);
// Inform the ActivityBuffer.
this.getActivityBuffer().onContextGained(pActivity);
}
@Override
public final void onDetach() {
// Handle as usual.
super.onDetach();
// Inform the ActivityBuffer.
this.getActivityBuffer().onContextLost();
}
/* Getters. */
public final ActivityBuffer getActivityBuffer() {
return this.mActivityBuffer;
}
}
마지막으로 에 대한 호출에 대해 신뢰할 수없는 Fragment
확장 영역 내 에서 간단하게 전화를 걸어 작업을 선언 하십시오!BaseFragment
getActivity()
this.getActivityBuffer().safely(...)
ActivityBuffer.IRunnable
그러면 콘텐츠가 void run(final Activity pActivity)
UI 스레드를 따라 실행됩니다.
는 ActivityBuffer
다음과 같이 사용할 수 있습니다 :
this.getActivityBuffer().safely(
new ActivityBuffer.IRunnable() {
@Override public final void run(final Activity pActivity) {
// Do something with guaranteed Context.
}
}
);
답변
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// run the code making use of getActivity() from here
}
답변
나는 이것이 오래된 질문이라는 것을 알고 있지만 내 문제는 다른 사람들에 의해 해결되지 않았기 때문에 그것에 대한 대답을 제공해야한다고 생각합니다.
우선 : 나는 fragmentTransactions를 사용하여 조각을 동적으로 추가하고있었습니다. 둘째 : 내 조각은 AsyncTasks (서버의 DB 쿼리)를 사용하여 수정되었습니다. 세 번째 : 활동 시작시 내 조각이 인스턴스화되지 않았습니다. 네 번째 : 조각 변수를 가져 오기 위해 사용자 정의 조각 인스턴스화를 사용하여 “생성 또는로드”했습니다. 넷째 : 방향 변경으로 인해 활동이 다시 작성되었습니다.
문제는 쿼리 응답으로 인해 조각을 “제거”하고 싶었지만 조각이 직전에 잘못 만들어졌습니다. 나중에 “커밋”으로 인해 조각을 제거 할 때 조각이 아직 추가되지 않은 이유를 모르겠습니다. 따라서 getActivity ()가 널을 리턴했습니다.
해결 방법 : 1) 새 인스턴스를 만들기 전에 조각의 첫 번째 인스턴스를 올바르게 찾으려고했는지 확인해야했습니다 .2) 방향 변경을 통해 유지하기 위해 해당 조각에 serRetainInstance (true)를 배치해야합니다 (백 스택 없음) 3) “제거하기”직전에 “오래된 조각을 재생성하거나 얻는 것”대신, 나는 활동을 시작할 때 조각을 직접 넣었다. 프래그먼트 변수를 제거하기 전에 “로드”(또는 인스턴스화)하는 대신 활동 시작시이를 초기화하면 getActivity 문제가 발생하지 않습니다.