如何初始化TextView以避免NullPointer错误?

时间:2018-03-19 14:36:19

标签: java android

我试图学习如何使用绑定服务。每当我单击“开始/暂停”按钮时,我希望计时器根据需要进行更改。但是,单击这些按钮将终止该程序。我不确定为什么这个TextView对象在我的服务类中给出了NullPointerException错误。有人可以帮帮我吗?

public class LocalService extends Service {
    //Binder
    private final IBinder mBinder = new LocalBinder();

    Button btnStart,btnPause,btnLap;
    TextView txtTimer;
    Handler customHandler = new Handler();
    LinearLayout container;

    long startTime=0L,timeInMilliseconds=0L,timeSwapBuff=0L,updateTime=0L;


    public class LocalBinder extends Binder {
        LocalService getService() {
            //Return this instance of LocalService so public methods can be called
            return LocalService.this;
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    Runnable updateTimerThread = new Runnable() {
        @Override
        public void run() {
            timeInMilliseconds = SystemClock.uptimeMillis()-startTime;
            updateTime = timeSwapBuff+timeInMilliseconds;
            int secs=(int)(updateTime/1000);
            int mins=secs/60;
            secs%=60;
            int milliseconds=(int)(updateTime%1000);
            txtTimer.setText(""+mins+":"+String.format("%02d",secs)+":"
                                        +String.format("%03d",milliseconds));
            customHandler.postDelayed(this,0);
        }
    };
}

此对象已在我的Main类中初始化:

public class MainActivity extends AppCompatActivity {

    LocalService mService = new LocalService();
    boolean mBound = false;
    Button btnStart, btnPause, btnLap;
    TextView txtTimer;
    LinearLayout container;
    Handler customHandler = new Handler();
    long startTime=0L,timeInMilliseconds=0L,timeSwapBuff=0L,updateTime=0L;


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

        btnStart = (Button)findViewById(R.id.btnStart);
        btnPause = (Button)findViewById(R.id.btnPause);
        btnLap = (Button)findViewById(R.id.btnLap);
        txtTimer = (TextView)findViewById(R.id.timerValue);
        container = (LinearLayout)findViewById(R.id.container);

        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startTime = SystemClock.uptimeMillis();
                customHandler.postDelayed(mService.updateTimerThread,0);

            }
        });

        btnPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                timeSwapBuff=+timeInMilliseconds;
                customHandler.removeCallbacks(mService.updateTimerThread);
            }
        });

        btnLap.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LayoutInflater inflater = (LayoutInflater)getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                View addView = inflater.inflate(R.layout.row,(ViewGroup)null);
                TextView txtValue = (TextView)addView.findViewById(R.id.textContent);
                txtValue.setText(txtTimer.getText());
                container.addView(addView);
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        //Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(mConnection);
        mBound = false;
    }

    //Define callbacks for service binding, passed to bindService()
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName mainActivity, IBinder service) {
            //We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
}

当此服务运行时,我希望使用绑定的服务值更新主活动中的txtTimer。我得到一个NullPointer错误代码,将我带到第56行附近的LocalService类:

txtTimer.setText(""+mins+":"+String.format("%02d",secs)+":"
                            +String.format("%03d",milliseconds));    

更新这个的最佳方法是什么,以便txtTimer能够显示变化的值?我真的很感激一些帮助

2 个答案:

答案 0 :(得分:3)

LocalService class:

上定义此构造函数
public LocalService(TextView textView){
   txtTimer = textView;
   super();
}

并在btnStart.setOnClickListener(new View ...之前添加此行:

mService = new LocalService(txtTimer);

希望它会有所帮助

答案 1 :(得分:1)

问题是您的txtTimer对象永远不会在您的服务中初始化。修改服务中的UI元素很棘手,最好不要直接从服务本身完成。

通常,解决方案是实现BroadcastReceiver。您只需将您想要的任何数据(在本例中为字符串)发送到您的活动,然后该活动将接收该数据。

首先,在您的服务中,您创建一个Intent对象,在其中您将Bundle包含您要发送到活动的任何数据:

Runnable updateTimerThread = new Runnable() {
    @Override
    public void run() {
        timeInMilliseconds = SystemClock.uptimeMillis()-startTime;
        updateTime = timeSwapBuff+timeInMilliseconds;
        int secs=(int)(updateTime/1000);
        int mins=secs/60;
        secs%=60;
        int milliseconds=(int)(updateTime%1000);

        // Get the data to send
        String string = ""+mins+":"+String.format("%02d",secs)+":"
                +String.format("%03d",milliseconds);

        Intent intent = new Intent("MainActivityListener");

        Bundle bundle = new Bundle();
        bundle.putString("txtTimerString", string);

        intent.putExtras(bundle);

        // Send broadcast
        getApplicationContext().sendBroadcast(intent);

        customHandler.postDelayed(this,0);
    }
};

然后在您的活动中,您需要创建BroadcastReciever,它将从服务中接收您想要的字符串并相应地设置TextView:

private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle extras = intent.getExtras();

        if (extras != null) {
            String txtTimerString = extras.getString("txtTimerString");
            if (txtTimerString != null) {
                txtTimer.setText(txtTimerString);
            }
        }
    }
};

最后,您需要在活动的BroadcastReciever方法中注册onResume

@Override
public void onResume() {
    super.onResume();
    getApplicationContext().registerReceiver(mMessageReceiver, 
            new IntentFilter("MainActivityListener"));
}

这比尝试找到将TextView直接传递到服务的方法要安全得多,因为服务可以独立于活动运行。如果服务尝试修改TextView并且活动当前未运行,则可能会崩溃。这样,服务就会发出一个广播 - 如果活动没有运行,那就无所谓了。