【问题标题】:How to debug Null Pointer Exception raised by Butterknife in an Android app?如何在 Android 应用程序中调试 Butterknife 引发的空指针异常?
【发布时间】:2016-05-03 12:52:09
【问题描述】:

我遇到了另一个新手问题。正如您在我的错误消息中看到的那样。在我的 MainActivity.java 中,第 125 行出现空指针异常:

    05-03 22:19:17.559 22615-22615/com.example.andrewjakevillegas.stormy E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.andrewjakevillegas.stormy, PID: 22615
java.lang.
NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.example.andrewjakevillegas.stormy.MainActivity.updateDisplay(MainActivity.java:125)
at com.example.andrewjakevillegas.stormy.MainActivity.access$200(MainActivity.java:38)
at com.example.andrewjakevillegas.stormy.MainActivity$1$1.run(MainActivity.java:100)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

这是我的 MainActivity.java

package com.example.andrewjakevillegas.stormy;

import...    

public class MainActivity extends ActionBarActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    private CurrentWeather mCurrentWeather;

    @BindView(R.id.timeLabel)
    TextView mTimeLabel;
    @BindView(R.id.temperatureLabel)
    TextView mTemperatureLabel;
    @BindView(R.id.humidityLabel)
    TextView mHumidityValue;
    @BindView(R.id.precipLabel)
    TextView mPrecipValue;
    @BindView(R.id.summaryLabel)
    TextView mSummaryLabel;
    @BindView(R.id.iconImageView)
    ImageView mIconImageView;
    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient mClient;


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



        if (isNetworkAvailable()) {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url( forecastURL )
                    .build();

            Call call = client.newCall( request );
            call.enqueue( new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {

                    try {
                        String jsonData = response.body().string();
                        Log.v( TAG, jsonData );
                        if (response.isSuccessful()) {
                            mCurrentWeather = getCurrentDetails( jsonData );
                            runOnUiThread( new Runnable() {
                                @Override
                                public void run() {
                                    updateDisplay();
                                }
                            } );
                        } else {
                            alertUserAboutError();
                        }
                    } catch (IOException e) {
                        Log.e( TAG, "Exception caught: ", e );
                    } catch (JSONException e) {
                        Log.e( TAG, "Exception caught: ", e );
                    }
                }
            } );
        } else {
            Toast.makeText( this, getString( R.string.network_unavailable_message ),
                    Toast.LENGTH_LONG ).show();
        }
        Log.d( TAG, "Main UI code is running!" );

        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        mClient = new GoogleApiClient.Builder( this ).addApi( AppIndex.API ).build();
    }

    private void updateDisplay() {
        mTemperatureLabel.setText( mCurrentWeather.getTemperature() + "" );
    }

    private CurrentWeather getCurrentDetails(String jsonData) throws JSONException {
        JSONObject forecast = new JSONObject( jsonData );
        String timezone = forecast.getString( "timezone" );
        Log.i( TAG, "From JSON: " + timezone );

        JSONObject currently = forecast.getJSONObject( "currently" );
        CurrentWeather currentWeather = new CurrentWeather();
        currentWeather.setHumidity( currently.getDouble( "humidity" ) );
        currentWeather.setTime( currently.getLong( "time" ) );
        currentWeather.setIcon( currently.getString( "icon" ) );
        currentWeather.setPrecipChance( currently.getDouble( "precipProbability" ) );
        currentWeather.setSummary( currently.getString( "summary" ) );
        currentWeather.setTemperature( currently.getDouble( "temperature" ) );

        return new CurrentWeather();
    }

    private boolean isNetworkAvailable() {
        ConnectivityManager manager = (ConnectivityManager)
                getSystemService( Context.CONNECTIVITY_SERVICE );
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        boolean isAvailable = false;
        if (networkInfo != null && networkInfo.isConnected()) {
            isAvailable = true;
        }

        return isAvailable;
    }

    private void alertUserAboutError() {
        AlertDialogFragment dialog = new AlertDialogFragment();
        dialog.show( getFragmentManager(), "error_dialog" );
    }

    @Override
    public void onStart() {
        super.onStart();

        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        mClient.connect();
        Action viewAction = Action.newAction(
                Action.TYPE_VIEW, // TODO: choose an action type.
                "Main Page", // TODO: Define a title for the content shown.
                // TODO: If you have web page content that matches this app activity's content,
                // make sure this auto-generated web page URL is correct.
                // Otherwise, set the URL to null.
                Uri.parse( "http://host/path" ),
                // TODO: Make sure this auto-generated app URL is correct.
                Uri.parse( "android-app://com.example.andrewjakevillegas.stormy/http/host/path" )
        );
        AppIndex.AppIndexApi.start( mClient, viewAction );
    }

    @Override
    public void onStop() {
        super.onStop();

        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        Action viewAction = Action.newAction(
                Action.TYPE_VIEW, // TODO: choose an action type.
                "Main Page", // TODO: Define a title for the content shown.
                // TODO: If you have web page content that matches this app activity's content,
                // make sure this auto-generated web page URL is correct.
                // Otherwise, set the URL to null.
                Uri.parse( "http://host/path" ),
                // TODO: Make sure this auto-generated app URL is correct.
                Uri.parse( "android-app://com.example.andrewjakevillegas.stormy/http/host/path" )
        );
        AppIndex.AppIndexApi.end( mClient, viewAction );
        mClient.disconnect();
    }
}

这行代码让我头疼。这是我的错误日志中的第 125 行。

private void updateDisplay() {
        mTemperatureLabel.setText( mCurrentWeather.getTemperature() + "" );
    }

我已经检查了我的布局。我得到了正确的 id 和 TextView 类型。

这是我的 activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            android:paddingBottom="@dimen/activity_vertical_margin"
            tools:context=".MainActivity"
            android:id="@+id/relativeLayout"
            android:background="#fffc970b">

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/temperatureLabel"
    android:layout_centerVertical="true"
    android:layout_centerHorizontal="true"
    android:textColor="@android:color/white"
    android:textSize="150dp"
    android:text="100"/>

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/degreeImageView"
    android:layout_alignTop="@+id/temperatureLabel"
    android:layout_toRightOf="@+id/temperatureLabel"
    android:layout_toEndOf="@+id/temperatureLabel"
    android:layout_marginTop="50dp"
    android:src="@drawable/degree"/>

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="At 5:00 PM it will be"
    android:id="@+id/timeLabel"
    android:layout_above="@+id/temperatureLabel"
    android:layout_centerHorizontal="true"
    android:textColor="#95ffffff"
    android:textSize="18sp"/>

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Alcatraz Island, CA"
    android:id="@+id/locationLabel"
    android:layout_above="@+id/timeLabel"
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="60dp"
    android:textColor="@android:color/white"
    android:textSize="24sp"/>

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/iconImageView"
    android:layout_alignBottom="@+id/locationLabel"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:src="@drawable/cloudy_night"/>

<LinearLayout
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@+id/temperatureLabel"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="10dp"
    android:weightSum="100"
    android:id="@+id/linearLayout">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="50">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="HUMIDITY"
            android:id="@+id/humidityLabel"
            android:textColor="#95ffffff"
            android:gravity="center_horizontal"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="0.88"
            android:id="@+id/humidityValue"
            android:textColor="@android:color/white"
            android:textSize="24sp"
            android:gravity="center_horizontal"/>
    </LinearLayout>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_weight="50">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="RAIN/SNOW?"
            android:id="@+id/precipLabel"
            android:textColor="#95ffffff"
            android:gravity="center_horizontal"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="100"
            android:id="@+id/precipValue"
            android:textColor="@android:color/white"
            android:textSize="24sp"
            android:gravity="center_horizontal"/>
    </LinearLayout>
</LinearLayout>

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Stormy with a chance of meatballs"
    android:id="@+id/summaryLabel"
    android:layout_below="@+id/linearLayout"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="40dp"
    android:textColor="@android:color/white"
    android:textSize="18dp"
    android:gravity="center_horizontal"/>

我的当前天气 Java 类应该获取并设置正确的值。

public int getTemperature() {
    return (int)Math.round(mTemperature);
}

public void setTemperature(double temperature) {
    mTemperature = temperature;
}

这是我的 CurrentWeather.java 的副本,以备您需要参考。

    package com.example.andrewjakevillegas.stormy;

public class CurrentWeather {
    private String mIcon;
    private long mTime;
    private double mTemperature;
    private double mHumidity;
    private double mPrecipChance;
    private String mSummary;

    public String getIcon() {
        return mIcon;
    }

    public void setIcon(String icon) {
        mIcon = icon;
    }

    public int getIconId(){
        int iconId = R.drawable.clear_day;

        if (mIcon.equals("clear-day")){
            iconId = R.drawable.clear_day;
        }

        else if (mIcon.equals("clear-night")){
            iconId = R.drawable.clear_night;
        }

        else if (mIcon.equals("rain")) {
            iconId = R.drawable.rain;
        }
        else if (mIcon.equals("snow")) {
            iconId = R.drawable.snow;
        }
        else if (mIcon.equals("sleet")) {
            iconId = R.drawable.sleet;
        }
        else if (mIcon.equals("wind")) {
            iconId = R.drawable.wind;
        }
        else if (mIcon.equals("fog")) {
            iconId = R.drawable.fog;
        }
        else if (mIcon.equals("cloudy")) {
            iconId = R.drawable.cloudy;
        }
        else if (mIcon.equals("partly-cloudy-day")) {
            iconId = R.drawable.partly_cloudy;
        }
        else if (mIcon.equals("partly-cloudy-night")) {
            iconId = R.drawable.cloudy_night;
        }


        return iconId;
    }

    public long getTime() {
        return mTime;
    }

    public void setTime(long time) {
        mTime = time;
    }

    public int getTemperature() {
        return (int)Math.round(mTemperature);
    }

    public void setTemperature(double temperature) {
        mTemperature = temperature;
    }

    public double getHumidity() {
        return mHumidity;
    }

    public void setHumidity(double humidity) {
        mHumidity = humidity;
    }

    public double getPrecipChance() {
        double precipPercentage = mPrecipChance * 100;
        return (int)Math.round(precipPercentage);
    }

    public void setPrecipChance(double precipChance) {
        mPrecipChance = precipChance;
    }

    public String getSummary() {
        return mSummary;
    }

    public void setSummary(String summary) {
        mSummary = summary;
    }


}

这是我运行调试器时的图像结果。

黄油刀看起来不像。你有什么建议我可以解决这个问题吗?

【问题讨论】:

  • 你在哪里初始化“mTemperatureLabel” textView..?
  • 你还没有初始化你的任何textViews......
  • 你能用调试器检查什么是空的吗?在第 125 行设置断点,查看 TextView 或 mCurrentWeather 是否为空。
  • 另外,我认为您不应该在网上发布您的 API 密钥... :)

标签: java android nullpointerexception


【解决方案1】:

我找到了问题的答案。

您必须下载 - 将整个黄油刀编译器(下面的代码)复制并粘贴到您的构建 gradle 中。我只是复制依赖项并将其添加到我的 gradle 依赖项中。这是给我的问题。

buildscript {
  repositories {
    mavenCentral()
   }
  dependencies {
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  }
}

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
  compile 'com.jakewharton:butterknife:8.0.1'
  apt 'com.jakewharton:butterknife-compiler:8.0.1'
}

【讨论】:

  • 哪个 build.gradle - 模块或应用级别?!
【解决方案2】:
package com.example.com;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;


import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.common.api.GoogleApiClient;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

 import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;

import butterknife.Bind;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity {

public static final String TAG = MainActivity.class.getSimpleName();

private CurrentWeather mCurrentWeather;

@Bind(R.id.timeLabel)
TextView mTimeLabel;
@Bind(R.id.temperatureLabel)
TextView mTemperatureLabel;
@Bind(R.id.humidityLabel)
TextView mHumidityValue;
@Bind(R.id.precipLabel)
TextView mPrecipValue;
@Bind(R.id.summaryLabel)
TextView mSummaryLabel;
[![enter image description here][1]][1]@Bind(R.id.iconImageView)
ImageView mIconImageView;
/**
 * ATTENTION: This was auto-generated to implement the App Indexing API.
 * See https://g.co/AppIndexing/AndroidStudio for more information.
 */
private GoogleApiClient mClient;


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


    String apiKey = "ur_key";
    double latitude = 37.8267;
    double longitude = -122.423;
    String forecastURL = "https://api.forecast.io/forecast/" + apiKey +
            "/" + latitude + "," + longitude;

    if (isNetworkAvailable()) {
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url( forecastURL )
                .build();

        Call call = client.newCall( request );
        call.enqueue( new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {

            }

            @Override
            public void onResponse(Response response) throws IOException {

                try {
                    String jsonData = response.body().string();
                    Log.v( TAG, jsonData );
                    if (response.isSuccessful()) {
                        mCurrentWeather = getCurrentDetails( jsonData );
                        runOnUiThread( new Runnable() {
                            @Override
                            public void run() {
                                updateDisplay();
                            }
                        } );
                    } else {
                        alertUserAboutError();
                    }
                } catch (IOException e) {
                    Log.e( TAG, "Exception caught: ", e );
                } catch (JSONException e) {
                    Log.e( TAG, "Exception caught: ", e );
                }
            }
        } );
    } else {
        Toast.makeText( this,  "not available",Toast.LENGTH_SHORT )
               .show();
    }
    Log.d( TAG, "Main UI code is running!" );

    // ATTENTION: This was auto-generated to implement the App Indexing API.
    // See https://g.co/AppIndexing/AndroidStudio for more information.
    mClient = new GoogleApiClient.Builder( this ).addApi( AppIndex.API ).build();
            }


private void updateDisplay() {
    mTemperatureLabel.setText( mCurrentWeather.getTemperature() + "" );
}

private CurrentWeather getCurrentDetails(String jsonData) throws JSONException {
    JSONObject forecast = new JSONObject( jsonData );
    String timezone = forecast.getString( "timezone" );
    Log.i( TAG, "From JSON: " + timezone );

    JSONObject currently = forecast.getJSONObject( "currently" );
    CurrentWeather currentWeather = new CurrentWeather();
    currentWeather.setHumidity( currently.getDouble( "humidity" ) );
    currentWeather.setTime( currently.getLong( "time" ) );
    currentWeather.setIcon( currently.getString( "icon" ) );
    currentWeather.setPrecipChance( currently.getDouble( "precipProbability" ) );
    currentWeather.setSummary( currently.getString( "summary" ) );
    currentWeather.setTemperature( currently.getDouble( "temperature" ) );

    return new CurrentWeather();
}

private boolean isNetworkAvailable() {
    ConnectivityManager manager = (ConnectivityManager)
            getSystemService( Context.CONNECTIVITY_SERVICE );
    NetworkInfo networkInfo = manager.getActiveNetworkInfo();
    boolean isAvailable = false;
    if (networkInfo != null && networkInfo.isConnected()) {
        isAvailable = true;
    }

    return isAvailable;
}

private void alertUserAboutError() {
    AlertDialogFragment dialog = new AlertDialogFragment();
    dialog.show( getFragmentManager(), "error_dialog" );
}

@Override
public void onStart() {
    super.onStart();

    // ATTENTION: This was auto-generated to implement the App Indexing API.
    // See https://g.co/AppIndexing/AndroidStudio for more information.
    mClient.connect();
    Action viewAction = Action.newAction(
            Action.TYPE_VIEW, // TODO: choose an action type.
            "Main Page", // TODO: Define a title for the content shown.
            // TODO: If you have web page content that matches this app activity's content,
            // make sure this auto-generated web page URL is correct.
            // Otherwise, set the URL to null.
            Uri.parse( "http://host/path" ),
            // TODO: Make sure this auto-generated app URL is correct.
            Uri.parse( "android-app://com.example.com/http/host/path" )
    );
    AppIndex.AppIndexApi.start( mClient, viewAction );
}

@Override
public void onStop() {
    super.onStop();

    // ATTENTION: This was auto-generated to implement the App Indexing API.
    // See https://g.co/AppIndexing/AndroidStudio for more information.
    Action viewAction = Action.newAction(
            Action.TYPE_VIEW, // TODO: choose an action type.
            "Main Page", // TODO: Define a title for the content shown.
            // TODO: If you have web page content that matches this app activity's content,
            // make sure this auto-generated web page URL is correct.
            // Otherwise, set the URL to null.
            Uri.parse( "http://host/path" ),
            // TODO: Make sure this auto-generated app URL is correct.
            Uri.parse( "android-app://com.example.andrewjakevillegas.stormy/http/host/path" )
    );
    AppIndex.AppIndexApi.end( mClient, viewAction );
    mClient.disconnect();
}

【讨论】:

  • Fasal 是布局显示。我需要来自 api 的 json 对象来填充显示。我的黄油刀部分给出了问题。我已经更新了我的帖子以显示我的调试结果。
  • 我解决的是你的问题“空点异常”检查我如何在代码中绑定..你的代码没有显示意味着存在一些逻辑错误..无论如何我在(私人 CurrentWeather getCurrentDetails ) 并且我在日志中得到了正确的值.. 在 (public void updateDisplay) 中调试您的代码并检查为什么值没有到达那里.. 据我所知,对于未显示在文本字段上的值可能是您使用的是旧的改造回调的风格,这就是 runOnUiThread 中发生( updateDisplay() )的原因 ..
  • 是的。值进入日志。但它不与我的布局绑定。我将手动调用变量。黄油刀不工作。我稍后会解决这个问题。感谢您的帮助 Fasal。
【解决方案3】:

为什么要在单独的可运行文件中使用 updateDisplay?然后你在主线程上运行这个新的可运行文件。似乎很奇怪。

无论如何,问题在于您的 mTemperatureLabel 未初始化。当你调用第一个 updateDisplay 方法时,可能你的 bindView 操作没有完成。

编辑:正如我在 cmets 中提到的,我不会使用 Butterknife,因为没有任何好处,而且在调试错误时会遇到问题。无论如何,如果你无论如何都想使用 Butterknife,请尝试 Bind 而不是 BindView。

【讨论】:

  • 你是对的。我的黄油刀过程出现问题。我试图弄清楚发生了什么。我已经更新了我的帖子以揭示我的调试结果。
  • “黄油刀无法正常工作。您有什么建议可以解决这个问题吗?谢谢。”是的,不要使用黄油刀。这是没有意义的,使用这个库对你没有任何好处。更糟糕的是,您会遇到调试错误和错误的问题。谷歌使用他们的方式从 xml 布局初始化 java 对象而不进行依赖注入是有原因的。
  • 谢谢@ChampS。我将手动调用我的所有变量。由于类似的原因,最新的黄油刀版本似乎给其他人带来了问题。 github.com/JakeWharton/butterknife/issues
【解决方案4】:

在我的情况下,我有不同的依赖项

首先你的 build.grade 文件中有类似的配置

问题不同版本 [8.0.1 != 8.2.1]

build.gradle(app)

dependencies {
         compile 'com.jakewharton:butterknife:8.2.1'
         apt 'com.jakewharton:butterknife-compiler:8.0.1'
}

我用类似的版本解决了

dependencies {
         compile 'com.jakewharton:butterknife:8.2.1'
         apt 'com.jakewharton:butterknife-compiler:8.2.1'
}

build.gradle

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.0'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

    }
}

祝你好运!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-28
    • 1970-01-01
    • 2018-03-17
    • 1970-01-01
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    相关资源
    最近更新 更多