简体   繁体   中英

Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference error

I am trying to get a notification to trigger when a long is below a certain number. However whenever sendNotification() is called it throws the above error.

I am new to android.

Below is the section of the code where the issue is.

I am not sure what is causing this error. I supect I made need to change the method to sendNotification(View view) but in that case what do I send as the view?

I can provide the full code if needed.

package com.mple.seriestracker;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;

import com.mple.seriestracker.activity.HomeScreenActivity;
import com.mple.seriestracker.api.episodate.entities.show.Episode;
import com.mple.seriestracker.util.NotificationGenerator;

import org.threeten.bp.Duration;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.OffsetDateTime;
import org.threeten.bp.ZoneId;
import org.threeten.bp.ZoneOffset;
import org.threeten.bp.ZonedDateTime;
import org.threeten.bp.format.DateTimeFormatter;

import java.util.Locale;

public class Countdown extends AppCompatActivity{



    private int season;
    private int episode;
    private String name;
    private ZonedDateTime airDate;
    private Context context;

    public Countdown(String name, int episode,int season,ZonedDateTime airDate, Context context){
        this.airDate = airDate;
        this.name = name;
        this.episode = episode;
        this.season = season;
        this.context = context;
    }

    public Countdown(com.mple.seriestracker.api.episodate.entities.show.Countdown countdown){
        this.airDate = parseToLocal(countdown.air_date);
        this.name = countdown.name;
        this.episode = countdown.episode;
        this.season = countdown.season;
    }

    public void getSecondsTillAiring(){
        Duration duration = Duration.between(LocalDateTime.now(),airDate);
        long days = duration.toDays();
        //No idea why this returns an absurd number, possibly something wrong with the time conversion
        //So the simple fix is to convert the days into hours, subtract the total hours with the days.
        //This returns the real value, and makes it accurate.
        long hours = duration.toHours()-(days*24);
        long minutes = (int) ((duration.getSeconds() % (60 * 60)) / 60);
        long seconds = (int) (duration.getSeconds() % 60);
        if(days > 0){
            hours += days * 24;
        }
        if(hours > 0){
            minutes += 60* hours;
        }

        if(minutes > 0){
            seconds  += 60 * minutes;
        }
        if (seconds < 432000){
            sendNotification();
        }
    }

    public void sendNotification()
    {
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "M_CH_ID");

        //Create the intent that’ll fire when the user taps the notification//

        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.androidauthority.com/"));
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

        notificationBuilder.setContentIntent(pendingIntent);


        NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        NotificationChannel notificationChannel =
                new NotificationChannel("M_CH_ID", "M_CH_ID", NotificationManager.IMPORTANCE_DEFAULT);
        notificationChannel.setDescription("Test");
        nm.createNotificationChannel(notificationChannel);

        notificationBuilder.setAutoCancel(true)
                .setDefaults(Notification.DEFAULT_ALL)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.drawable.ic_launcher)
                .setTicker("Hearty365")
                .setContentTitle("Default notification")
                .setContentText("Random words")
                .setContentInfo("Info");

        nm.notify(1, notificationBuilder.build());
    }

    public String getCountdownFormat(){
        getSecondsTillAiring();
        Duration duration = Duration.between(LocalDateTime.now(),airDate);
        long days = duration.toDays();
        //No idea why this returns an absurd number, possibly something wrong with the time conversion
        //So the simple fix is to convert the days into hours, subtract the total hours with the days.
        //This returns the real value, and makes it accurate.
        long hours = duration.toHours()-(days*24);
        int minutes = (int) ((duration.getSeconds() % (60 * 60)) / 60);
        int seconds = (int) (duration.getSeconds() % 60);
        String timeString = "";
        if(days > 0){
            timeString+=formatDay(days);
        }
        if(hours > 0){
            timeString+=formatHour(hours);
        }
        if(minutes > 0){
            timeString+= formatMinutes(minutes);
        }
        if(seconds > 0){
            timeString += formatSeconds(seconds);
        }
        return timeString;
    }

    public String getName() {
        return name;
    }

    public int getEpisode() {
        return episode;
    }

    public int getSeason() {
        return season;
    }

    private String formatDay(long days){
        return format(days,"day");
    }

    private String formatHour(long hours){
        return format(hours,"hour");
    }

    private String formatMinutes(long minutes){
        return format(minutes,"minute");
    }

    private String formatSeconds(long seconds){
        return format(seconds,"second");
    }

    private String format(long x,String nonPlural){
        //Checks whether or not a plural should be added
        String string = nonPlural;
        if(x > 1)
            string+="s";

        return String.format("%s %s ",x,string);
    }

    //All air dates are formatted in this format
    static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);

    public static ZonedDateTime parseToLocal(String s){
        if(s == null) return null;
        return LocalDateTime.parse(s, DATE_TIME_FORMATTER)
                .atOffset(ZoneOffset.UTC)
                .atZoneSameInstant(ZoneId.systemDefault());
    }

    public static boolean isOlderEpisode(LocalDateTime airDate, LocalDateTime currEpDate){
        return currEpDate.isBefore(airDate);
    }

    public static boolean isOlderEpisode(OffsetDateTime airDate, OffsetDateTime currEpDate){
        return currEpDate.toLocalDate().isBefore(airDate.toLocalDate());
    }

    //Responsible for finding a certain episode
    public Countdown getUpcomingAiringEp(Episode[] episodes, int episode, int season) {
        if (episodes == null) {
            return null;
        }

        //Loop in reverse, since the episodes are ordered from start to finish
        //So looping from reverse will start with the newer shows first
        for (int i = (episodes.length - 1); i >= 0; i--) {
            Episode newEpisode = episodes[i];


            if (newEpisode.air_date != null && newEpisode.season == season && newEpisode.episode == episode) {
                return new Countdown(newEpisode.name,newEpisode.episode, newEpisode.season, parseToLocal(newEpisode.air_date),this);
            }

            if(newEpisode.season <= (newEpisode.season - 1)) {
                break;
            }
        }

        return null;
    }
}

Full stack trace

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.mple.seriestracker, PID: 2348
    java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
        at android.content.ContextWrapper.getPackageName(ContextWrapper.java:145)
        at android.app.PendingIntent.getActivity(PendingIntent.java:344)
        at android.app.PendingIntent.getActivity(PendingIntent.java:311)
        at com.mple.seriestracker.Countdown.sendNotification(Countdown.java:76)
        at com.mple.seriestracker.Countdown.getSecondsTillAiring(Countdown.java:65)
        at com.mple.seriestracker.Countdown.getCountdownFormat(Countdown.java:100)
        at com.mple.seriestracker.fragments.CountdownFragment$RecyclerViewAdapter$1.run(CountdownFragment.java:95)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

And the main class

package com.mple.seriestracker.activity;

import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.viewpager.widget.ViewPager;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;
import com.jakewharton.threetenabp.AndroidThreeTen;
import com.mple.seriestracker.R;
import com.mple.seriestracker.ShowInfo;
import com.mple.seriestracker.ShowTracker;
import com.mple.seriestracker.TvShow;
import com.mple.seriestracker.api.episodate.Episodate;
import com.mple.seriestracker.api.episodate.entities.show.TvShowResult;
import com.mple.seriestracker.database.EpisodeTrackDatabase;
import com.mple.seriestracker.fragments.CountdownFragment;
import com.mple.seriestracker.fragments.SectionsPagerAdapter;
import com.mple.seriestracker.fragments.MyShowsFragment;
import com.mple.seriestracker.util.NotificationGenerator;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import retrofit2.Response;


public class HomeScreenActivity extends AppCompatActivity {

    static final int NEW_SHOW_REQUEST_CODE = 1;
    static final int FILE_PERMISSION_RREQUEST_CODE = 1;
    static final int NEW_SHOW_REQUEST_RESULT_CODE = 1;

    Context context = this;

    SectionsPagerAdapter mSectionsPagerAdapter;
    ViewPager mViewPager;
    TabLayout mTabs;
    boolean started = false;

    //TODO allow more than 3 shows to display on countdown page
    //TODO sort the countdown tab based on time
    //TODO notify the user when a show is airing
    //TODO re-obtain the next countdown (if any new episodes) otherwise remove the countdown from the tab
    //TODO add delete button to delete shows (holding on image already has checkboxes implemented)
    //All done after that

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

        EpisodeTrackDatabase.setInstance(new EpisodeTrackDatabase(getApplicationContext()));

        //Sets all date time stuff to correct sync
        AndroidThreeTen.init(this);

        //Initialize fragments
        mSectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
        mViewPager = findViewById(R.id.view_pager);
        setupViewPager(mViewPager);
        mTabs = findViewById(R.id.tabs);
        mTabs.setupWithViewPager(mViewPager);

        //Initialize floating menu button
        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startSearchIntent();
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        //On start is called when the search intent is destroyed
        //This prevents it from being used more than once.
        //As it's intended for loading settings only (after all UI elements are initialized)
        if(started) return;
        loadSettings();
        started = true;
    }

    //Responsible for setting up the fragments for each tab
    private void setupViewPager(ViewPager viewPager){
        mSectionsPagerAdapter.addFragment(new MyShowsFragment(),"My Shows");
        mSectionsPagerAdapter.addFragment(new CountdownFragment(),"Countdowns");
        viewPager.setAdapter(mSectionsPagerAdapter);
    }

    private void loadSettings(){
        //Loads settings from database
        new LoadShowsTask().execute();
    }

    //Adds a show to the "my shows" tab
    public void addShow(ShowInfo showInfo){
        new TvShowTask().execute(showInfo); //Background task to get info from the api
        EpisodeTrackDatabase.INSTANCE.addShow(showInfo.name,showInfo.imagePath,showInfo.id); //Add the show to the database
        ((MyShowsFragment)mSectionsPagerAdapter.getItem(0)).addShow(showInfo); //Adds it to the fragment, fragment will then automatically update it
    }

    public void addCountdown(long showID){
        if(ShowTracker.INSTANCE.calenderCache.contains(showID))return;
        ((CountdownFragment)mSectionsPagerAdapter.getItem(1)).addCountdown(showID);
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == NEW_SHOW_REQUEST_CODE && resultCode == NEW_SHOW_REQUEST_RESULT_CODE) {
            //Create a new show, add it to the list and populate it
            ShowInfo showInfo = new ShowInfo(); //Creates a new object
            showInfo.id = data.getLongExtra("showID",0);
            showInfo.imagePath = data.getStringExtra("showImage");
            showInfo.name = data.getStringExtra("showName");
            ShowTracker.INSTANCE.addedShowsCache.add(showInfo.id);
            addShow(showInfo);
        }
    }

    class LoadShowsTask extends AsyncTask<String,Void,String>{

        @Override
        protected String doInBackground(String... strings) {
            ShowInfo[] showData =  EpisodeTrackDatabase.INSTANCE.getAllShows();
            runOnUiThread(() ->{
                new TvShowTask().execute(showData);
                for (ShowInfo show : showData) {
                    runOnUiThread(() ->addShow(show));
                }
            });
            return null;
        }
    }

    class TvShowTask extends AsyncTask<ShowInfo,Void, List<TvShowResult>> {
        //Responsible for obtaining info about each show
        //Automatically prompts on each show add/app initialization
        @Override
        protected List<TvShowResult> doInBackground(ShowInfo ... shows) {
            List<TvShowResult> tvShowResults = new ArrayList<>();
            for (ShowInfo show: shows) {
                try {
                    Response<com.mple.seriestracker.api.episodate.entities.show.TvShow> response = Episodate.INSTANCE
                            .show()
                            .textQuery(show.id + "")
                            .execute();

                    if(response.isSuccessful()){
                        tvShowResults.add(response.body().tvShow);
                    }
                } catch (IOException e) {}
            }
            return tvShowResults;
        }

        @Override
        protected void onPostExecute(List<TvShowResult> result) {
            for (TvShowResult tvShowResult : result) {
                TvShow tvShow = new TvShow(tvShowResult);
                ShowTracker.INSTANCE.addTvShow(tvShow);
                if(tvShow.getCountdown() != null){
                    addCountdown(tvShow.getId());
                }
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case FILE_PERMISSION_RREQUEST_CODE:
                if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    //Todo Finish this, if we will be implementing saving
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
                break;
        }
    }

    //Prevents the app opening multiple search intents, if spammed
    void startSearchIntent(){
        if(!ShowSearchActivity.destroyed) return;
        Intent intent = new Intent(getApplicationContext(),ShowSearchActivity.class);
        startActivityForResult(intent,NEW_SHOW_REQUEST_CODE);
    }

    //Will be used for writing saved data, later on to keep track of what shows are saved
    boolean hasFilePermissions(){
        return (Build.VERSION.SDK_INT > 22 && ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED);
    }

    void askForPermission(){
        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE}, FILE_PERMISSION_RREQUEST_CODE);
    }
}

Iam wondering if at this point it is just easiier to link to github as there are more classes than just these 2.

You must supply the proper Context from you Activity class. First in your YOUR_ACTIVITY.java declare this

Context context = this;

then add context parameters in your countdown method

private int season;
private int episode;
private String name;
private ZonedDateTime airDate;
private Context context;

public Countdown(String name, int episode,int season,ZonedDateTime airDate, Context context){
        this.airDate = airDate;
        this.name = name;
        this.episode = episode;
        this.season = season;
        this.context = context;
    }

Just add the context when calling the Countdown method.

new Countdown(name, episode, season, context).getSecondsTillAiring();

then supply the context to NotificationManager like this.

NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);

Just remove "extends AppCompatActivity" might work for you, because apparently your class does not inherit anything from AppCompatActivity.

EDIT:

NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

You may consider moving this part of the code to your Activity class, or passing activity reference to the method and call it like:

NotificationManager nm = (NotificationManager) activity.getSystemService(Context.NOTIFICATION_SERVICE);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM