简体   繁体   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

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.但是,每当调用 sendNotification() 时,都会引发上述错误。

I am new to android.我是 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?我怀疑我需要将方法更改为 sendNotification(View 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和主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.我想知道此时链接到 github 是否更容易,因为除了这两个之外还有更多的类。

You must supply the proper Context from you Activity class.您必须从活动 class 中提供正确的上下文。 First in your YOUR_ACTIVITY.java declare this首先在您的 YOUR_ACTIVITY.java 声明这个

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.只需在调用 Countdown 方法时添加上下文。

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

then supply the context to NotificationManager like this.然后像这样向 NotificationManager 提供上下文。

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.只需删除“扩展 AppCompatActivity”可能对您有用,因为显然您的 class 没有从 AppCompatActivity 继承任何内容。

EDIT:编辑:

NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 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:您可以考虑将这部分代码移动到您的 Activity class,或者将 Activity 引用传递给该方法并调用它:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 尝试调用虚拟方法&#39;java.lang.String android.content.Context.getPackageName() - Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName() 由于尝试调用虚拟方法&#39;java.lang.String android.content.Context.getPackageName(),因此意图失败 - Intent fails because of Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName() 空对象引用错误时出现&#39;java.lang.String android.content.Context.getPackageName()&#39; - 'java.lang.String android.content.Context.getPackageName()' on a null object reference error 空对象引用上的DialogFragment&#39;java.lang.String android.content.Context.getPackageName()&#39; - DialogFragment 'java.lang.String android.content.Context.getPackageName()' on a null object reference 尝试在空对象引用上调用虚拟方法“java.lang.Object android.content.Context.getSystemService(java.lang.String)” - Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference 尝试在空对象引用上调用虚拟方法 &#39;android.content.Context.getSharedPreferences(java.lang.String, int)&#39; - Attempt to invoke virtual method 'android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference 错误:尝试在空对象引用上调用虚方法&#39;java.lang.Object android.content.Context.getSystemService(java.lang.String)&#39; - error:Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference Android错误:尝试在空对象引用上调用虚拟方法&#39;java.lang.String android.content.Intent.getStringExtra(java.lang.String)&#39; - Android Error : Attempt to invoke virtual method 'java.lang.String android.content.Intent.getStringExtra(java.lang.String)' on a null object reference ViewPagerAdapter尝试在空对象引用上调用虚拟方法Context.getSystemService(java.lang.String)&#39; - ViewPagerAdapter Attempt to invoke virtual method Context.getSystemService(java.lang.String)' on a null object reference 尝试在null对象引用上调用虚拟方法&#39;java.lang.String [] android.os.Bundle.getStringArray(java.lang.String)&#39; - Attempt to invoke virtual method 'java.lang.String[] android.os.Bundle.getStringArray(java.lang.String)' on a null object reference
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM