1. 程式人生 > >【Debug-NullPointerException】findViewById(): Attempt to invoke ...on a null object reference

【Debug-NullPointerException】findViewById(): Attempt to invoke ...on a null object reference

空指標異常很常見,也很好找,今天重構程式碼的時候,愣是報了一個很神奇的NullPointerException

java.lang.RuntimeException: Unable to start activity ComponentInfo{com...Activity}: java.lang.NullPointerException: 
    Attempt to invoke virtual method 'void android.widget.TextView.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2315)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2375)
        at android.app.ActivityThread.access$900(ActivityThread.java:147)
        .
        .
        .
     Caused by: java.lang.NullPointerException: 
    Attempt to invoke virtual method 'void android.widget.TextView.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
        .
        .

所有的跡象都指向我繫結的控制元件為“null”,怎麼可能,我的控制元件都是老老實實地用【findViewById()】方法一個一個繫結的id,經檢查id沒錯,R檔案也沒錯,折騰半天突然想到,重構程式碼時為了讓程式碼結構清晰,給所有的Activity加了一個父類BaseActivity,並在【onCreate();】方法中整了一個“initViews()”方法用來初始化一些東西。

BaseActivity的程式碼結構是:

public abstract class BaseActivity extends AppCompatActivity {

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

        initViews();
    }


    /** 初始化檢視 **/
    protected abstract void initViews();
}

這樣的好處是讓子Activity在繼承的時候都要複寫父類Activity的抽象方法“initViews()”,使得子Activity的【onCreate();】方法不至於太臃腫龐大。

但是會產生一個問題,在子Activity中,【initViews()】部分會先於【onCreate()】部分執行(如下)

這就導致【findViewById()】部分會先於【setContentView(R.layout.activity_main);】部分執行,這是不對的

子Activity的結構:

public class MainActivity extends BaseActivity {

    private TextView textView ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);        // 罪魁禍首,錯誤位置

        log.d("TAG", "本程式碼塊中的程式碼的執行順序為:2222222222222222");

    }

    @Override
    protected void initViews() {

        log.d("TAG", "本程式碼塊中的程式碼的執行順序為:1111111111111111");
        
        // 【注意】“setContentView”的位置放在onCreate()中會導致執行的先後順序出錯而報空指標錯誤。
        setContentView(R.layout.activity_main);        // 正確位置

        // 繫結控制元件
        textView = (TextView)findViewById(R.id.tv_text);
        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //...
            }
        });
}

修正錯誤:

方法:按照上述程式碼,把【setContentView(R.layout.activity_main);】這一行從【onCreate()】中移到【initViews()】中。