从片段调用 Activity 方法时出现 NullPointerException,成为 ClassCastException

NullPointerException when calling an Activity method from a fragment, became a ClassCastException

这是我第一次遇到这个错误,老实说,在阅读了这里的 nullpointerexception 线程后,我仍然不太理解它。根据我的搜索,当某些内容指向 null 并导致错误时,就会发生这种情况。我不确定我的代码哪里错了。

错误

03-31 21:40:34.841 31029-31029/com.sti.ravago.researchlayout E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.sti.ravago.researchlayout, PID: 31029
    java.lang.NullPointerException
        at com.sti.ravago.researchlayout.MainActivity.redirect(MainActivity.java:70)
        at com.sti.ravago.researchlayout.ListFragment.onClick(ListFragment.java:93)
        at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:169)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:5292)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644)
        at dalvik.system.NativeStart.main(Native Method)

主要Activity

public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {

    private final Fragment mTimerFragment = new TimerFragment();
    private final Fragment mListFragment = new ListFragment();
    private final Fragment mReminderFragment = new RemindFragment();
    private final TimerFragment timeFrag = (TimerFragment)getSupportFragmentManager().findFragmentByTag("TimerFragment");

    private Fragment mActiveFragment = mTimerFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        getSupportFragmentManager().beginTransaction()
        .add(R.id.fragContainer, mReminderFragment, "TimerFragment")
        .hide(mReminderFragment)
        .commit();
        getSupportFragmentManager().beginTransaction()
        .add(R.id.fragContainer, mListFragment, "ListFragment")
        .hide(mListFragment)
        .commit();
        getSupportFragmentManager().beginTransaction()
        .add(R.id.fragContainer, mTimerFragment, "ReminderFragment")
        .commit();

        BottomNavigationView navigation = findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(this);
    }

    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.navigation_home:
                getSupportFragmentManager().beginTransaction()
                .hide(mActiveFragment)
                .show(mTimerFragment)
                .commit();
                mActiveFragment = mTimerFragment;
                return true;
            case R.id.navigation_dashboard:
                getSupportFragmentManager().beginTransaction()
                .hide(mActiveFragment)
                .show(mListFragment)
                .commit();
                mActiveFragment = mListFragment;
                return true;
            case R.id.navigation_notifications:
                getSupportFragmentManager().beginTransaction()
                .hide(mActiveFragment)
                .show(mReminderFragment)
                .commit();
                mActiveFragment = mReminderFragment;
                return true;
        }
        return false;
    }

    public void redirect(String app) {
        timeFrag.startTime(app);
        // Toast.makeText(this, "Test", Toast.LENGTH_SHORT).show();
        // Toast.makeText(this, app, Toast.LENGTH_SHORT).show();
    }
}

这个只是切换片段,以及调用片段的方法activity。

列表Activity

public class ListFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frag_list,null);
        ListView list1 = view.findViewById(R.id.listview1);
        ActivityManager am = (ActivityManager)getActivity().getSystemService(Context.ACTIVITY_SERVICE);
        PackageManager packageManager = getActivity().getPackageManager();
        ArrayAdapter<String> aa;

        List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfo = am.getRunningAppProcesses();
        final String pnArr[] = new String[runningAppProcessInfo.size()];
        List<String> filteredApps = new ArrayList<>();
        final List<String> filteredRunningApps = new ArrayList<>();
        final List<String> appNames = new ArrayList<>();

        //user-apps loop
        List<PackageInfo> apps = getActivity().getPackageManager().getInstalledPackages(0);
        for (int i = 0; i < apps.size(); i++){
            if ((apps.get(i).applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1){
                filteredApps.add(apps.get(i).packageName);
            }
        }

        //running apps + comparison loop
        for (int i = 0; i < runningAppProcessInfo.size(); i++) {
            pnArr[i] = runningAppProcessInfo.get(i).processName;
            if (filteredApps.contains(runningAppProcessInfo.get(i).processName)) {
                filteredRunningApps.add(runningAppProcessInfo.get(i).processName);
            }
        }

        //package name -> app name conversion
        for (int i = 0; i < filteredRunningApps.size();i++) {
            ApplicationInfo ai = null;
            try{
                ai = packageManager.getApplicationInfo(filteredRunningApps.get(i),0);

            }catch (PackageManager.NameNotFoundException e){}
            String title = (String)((ai != null) ? packageManager.getApplicationLabel(ai) : "???");
            appNames.add(title);
        }

        Toast.makeText(getActivity(), "Filtered Apps : " + filteredApps.size() + " | Running Apps : " + runningAppProcessInfo.size() + " | Filtered Running Apps : " + filteredRunningApps.size(), Toast.LENGTH_LONG).show();

         ArrayList<String> pnList = new ArrayList<String>();
         pnList.addAll(Arrays.asList(pnArr));
         aa = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1,appNames);
         list1.setAdapter(aa);
         aa.notifyDataSetChanged();
         final MainActivity mA = (MainActivity)getActivity();

         list1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             @Override
             public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
                 AlertDialog.Builder adb = new AlertDialog.Builder(getActivity());
                 adb.setTitle("Monitor " + appNames.get(position) + "?");
                 adb.setMessage("Track this application's usage time?");

                 adb.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         String app = appNames.get(position);
                         Toast.makeText(getActivity(), "Currently monitoring " + appNames.get(position) + ".", Toast.LENGTH_SHORT).show();

                             ((MainActivity) getActivity()).redirect(app);
                         }
                 });

                 adb.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         //Toast.makeText(getActivity(), "test", Toast.LENGTH_SHORT).show();
                     }
                 });

                 adb.show();
             }
         });

        return view;
    }
}

它说我在MainActivity的第70行和ListFragment的第93行有错误,指向这个

timeFrag.startTime(app); 

((MainActivity) getActivity()).redirect(app);

如果非要我猜的话,是 startTimeredirect 指向 null,但它们是如何指向 null 的,我该如何修复它?


更新

所以我将片段声明移到了方法中,它不再是 NullPointerException。现在是 ClassCastException。

04-01 05:51:17.520 31117-31117/com.sti.ravago.researchlayout E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.sti.ravago.researchlayout, PID: 31117
    java.lang.ClassCastException: com.sti.ravago.researchlayout.RemindFragment cannot be cast to com.sti.ravago.researchlayout.TimerFragment
        at com.sti.ravago.researchlayout.MainActivity.redirect(MainActivity.java:69)
        at com.sti.ravago.researchlayout.ListFragment.onClick(ListFragment.java:93)
        at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:169)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:5292)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644)
        at dalvik.system.NativeStart.main(Native Method)

这真的很奇怪,因为我没有任何调用 RemindFragment 的代码,除了 MainActivity。 MainActivity 代码可以在上面找到,唯一的变化是我将一个片段声明移到了一个方法中以避免 NullPointerException。这是 TimerFragment 和 RemindFragment 的代码,尽管我认为它们在这种情况下并不重要,但由于在错误中提到了它们,我认为它们可能会发挥作用,但老实说我不知道​​。

提醒片段

package com.sti.ravago.researchlayout;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;


public class RemindFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.frag_remind,null);
        return view;

    }

}

TimerFragment

public class TimerFragment extends Fragment {
    Button btnMonitor;
    Button btnRemind;
    TimePicker tp;
    CountDownTimer cdt;
    TextView tvTimer, tvRemind;
    long startTime = 0;
    Handler timerHandler = new Handler();
    Runnable timerRunnable = new Runnable() {
        @Override
        public void run() {
            long millis = System.currentTimeMillis() - startTime;
            int seconds = (int)(millis/1000);
            int minutes = seconds / 60;
            seconds = seconds % 60;
            tvTimer.setText(String.format("%d:%02d", minutes, seconds));
            timerHandler.postDelayed(this,500);
        }
    };



    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frag_timer, null);
        tp = (TimePicker)view.findViewById(R.id.timepick1);
        tvTimer = (TextView)view.findViewById(R.id.tvTimer);
        tvRemind = view.findViewById(R.id.textView3);
        btnMonitor = (Button)view.findViewById(R.id.btn1);
        btnRemind = (Button)view.findViewById(R.id.btn2);

        btnRemind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Calendar cal = Calendar.getInstance();

                //current
                Date c = cal.getTime();
                long currentTime = c.getTime();
                SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy h:mm a");
                //String d = sdf.format(c); //output 27/03/2019 3:05 AM

                //timepicker time
                String tpTime = String.valueOf(tp.getCurrentHour()) +":"+ String.valueOf(tp.getCurrentMinute());
                int tpDateDay = cal.get(Calendar.DAY_OF_MONTH);
                int tpDateMonth = cal.get(Calendar.MONTH);
                int tpDateYear = cal.get(Calendar.YEAR);
                Calendar c2 = Calendar.getInstance();
                c2.set(tpDateYear,tpDateMonth,tpDateDay,tp.getCurrentHour(),tp.getCurrentMinute(),0);
                long tpMillis = c2.getTimeInMillis();

                long remaining = tpMillis - currentTime;

                Toast.makeText(getActivity(),"Remaining time "+ remaining/1000, Toast.LENGTH_SHORT).show();

                cdt = new CountDownTimer(remaining,1000) {
                    @Override
                    public void onTick(long millisUntilFinished) {
                        tvRemind.setText(" " + millisUntilFinished/1000 + " seconds");
                    }

                    @Override
                    public void onFinish() {
                        Intent intent = new Intent (getActivity(),MainActivity.class);
                        PendingIntent pIntent = PendingIntent.getActivity(getActivity(),(int)System.currentTimeMillis(),intent,0);
                        Notification n = new Notification.Builder(getActivity())
                                .setContentTitle("Time is up!")
                                .setContentText("Consider stopping the application now.")
                                .setSmallIcon(R.drawable.ic_notifications_black_24dp)
                                .setContentIntent(pIntent)
                                .setAutoCancel(true)
                                .build();
                        NotificationManager nm =  (NotificationManager)getActivity().getSystemService(getActivity().NOTIFICATION_SERVICE);
                        nm.notify(0,n);

                        tvRemind.setText("0");
                    }
                }.start();
            }
        });

        btnMonitor.setText("Start");
        btnMonitor.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Button b = (Button) v;

                if (b.getText().equals("Stop")){
                    timerHandler.removeCallbacks(timerRunnable);
                    b.setText("Start Monitoring");
                }

                else {
                    startTime = System.currentTimeMillis();
                    timerHandler.postDelayed(timerRunnable, 0);
                    b.setText("Stop");
                    Toast.makeText(getActivity(),"Monitoring started.",Toast.LENGTH_SHORT).show();
                }
            }
        });

        return view;
    }

    public void startTime(String app) {
        btnMonitor.performClick();
        Toast.makeText(getActivity(), "Monitoring for " + app + " started." , Toast.LENGTH_LONG).show();
    }
}

错误指向这些行

final TimerFragment timeFrag = (TimerFragment)getSupportFragmentManager().findFragmentByTag("TimerFragment");

这很奇怪,因为我把它放在 redirect 方法中来解决 NullPointerException。

((MainActivity) getActivity()).redirect(app);

不知道这里出了什么问题。

我该如何解决这个问题?

private final TimerFragment timeFrag = (TimerFragment)getSupportFragmentManager().findFragmentByTag("TimerFragment");

您在创建 Activity 时调用它,这意味着您的 Fragment 将不存在。把这段代码移到你需要使用的地方:

void redirect(String app) {
    final TimerFragment timeFrag = (TimerFragment)getSupportFragmentManager().findFragmentByTag("TimerFragment");
    if(timeFrag != null) timeFrag.startTime(app);      
}