簡體   English   中英

嘗試在 null object 引用錯誤上調用虛擬方法 'java.lang.String android.content.Context.getPackageName()'

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

我正在嘗試在多頭低於某個數字時觸發通知。 但是,每當調用 sendNotification() 時,都會引發上述錯誤。

我是 android 的新手。

以下是問題所在的代碼部分。

我不確定是什么導致了這個錯誤。 我懷疑我需要將方法更改為 sendNotification(View view) 但在這種情況下我應該發送什么作為視圖?

如果需要,我可以提供完整的代碼。

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

完整的堆棧跟蹤

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)

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

我想知道此時鏈接到 github 是否更容易,因為除了這兩個之外還有更多的類。

您必須從活動 class 中提供正確的上下文。 首先在您的 YOUR_ACTIVITY.java 聲明這個

Context context = this;

然后在倒計時方法中添加上下文參數

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

只需在調用 Countdown 方法時添加上下文。

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

然后像這樣向 NotificationManager 提供上下文。

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

只需刪除“擴展 AppCompatActivity”可能對您有用,因為顯然您的 class 沒有從 AppCompatActivity 繼承任何內容。

編輯:

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

您可以考慮將這部分代碼移動到您的 Activity class,或者將 Activity 引用傳遞給該方法並調用它:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM