实现AsyncTask时,按下空指针引用

时间:2018-06-03 22:22:21

标签: java android multithreading android-asynctask android-canvas

我创建了一个名为JoyStickView的文件,扩展了SurfaceView。 在我的JoyStickView.java文件中,我有以下函数调用AsyncTask来绘制两个操纵杆:

private void drawJoystick(float hatX, float hatY) {
    // Only draw the joystick when SurfaceView has been created
    if (getHolder().getSurface().isValid()) {
        new MultiplyTask().execute();
    }
}

内部课程AsyncTask如下所示:

 public class MultiplyTask extends AsyncTask<Void, Bitmap, Bitmap[]> {

        @Override
        protected void onPreExecute() {
            //super.onPreExecute();
        }

        @Override
        protected Bitmap[] doInBackground(Void... progress_data) {

            Bitmap[] bitmapArray = new Bitmap[2];
            bitmapArray[0] = BitmapFactory.decodeResource(getResources(), R.drawable.joystick_base);
            bitmapArray[1] = BitmapFactory.decodeResource(getResources(), R.drawable.joystick_hat);
            //publishProgress(bitmapArray);
            return bitmapArray;
        }

        @Override
        protected void onProgressUpdate(Bitmap... bitmapArray) {
            //super.onProgressUpdate(values);\
        }

        @Override
        protected void onPostExecute(Bitmap[] bitmapArray) {
            // super.onPostExecute(s);
            myCanvas = getHolder().lockCanvas();
            myCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            switch (getId()) {
                case R.id.joystickRight:
                    // draw_joystick_base(myCanvas, R.drawable.joystick_base);

                    float c = bitmapArray[0].getWidth() / 2;
                    float d = bitmapArray[0].getHeight() / 2;
                    myCanvas.drawBitmap(bitmapArray[0], centerX - c, centerY - d, new Paint());
                    break;
                case R.id.joystickLeft:
                    // draw_joystick_base(myCanvas, R.drawable.joystick_base);

                    float c1 = bitmapArray[0].getWidth() / 2;
                    float d1 = bitmapArray[0].getHeight() / 2;
                    myCanvas.drawBitmap(bitmapArray[0], centerX - c1, centerY - d1, new Paint());
                    break;

            }

            float a = bitmapArray[1].getWidth() / 2;
            float b = bitmapArray[1].getHeight() / 2;
            myCanvas.drawBitmap(bitmapArray[1], hatX_tmp - a, hatY_tmp - b, new Paint());
            getHolder().unlockCanvasAndPost(myCanvas);
            // Do things like update the progress bar
        }

        void stopTask() {
            MultiplyTask a = new MultiplyTask();
            a.cancel(true);
        }
    }

一切正常,直到我点击后退按钮,应用程序崩溃并给我以下空指针错误。

  06-03 15:06:15.505 4478-4478/com.example.android.toybot E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.android.toybot, PID: 4478
        java.lang.NullPointerException: Attempt to invoke virtual method 'void android.graphics.Canvas.drawColor(int, android.graphics.PorterDuff$Mode)' on a null object reference
            at com.example.android.toybot.JoyStickView$MultiplyTask.onPostExecute(JoyStickView.java:304)
            at com.example.android.toybot.JoyStickView$MultiplyTask.onPostExecute(JoyStickView.java:274)
            at android.os.AsyncTask.finish(AsyncTask.java:661)
            at android.os.AsyncTask.-wrap1(AsyncTask.java)
            at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:678)
            at android.os.Handler.dispatchMessage(Handler.java:111)
            at android.os.Looper.loop(Looper.java:207)
            at android.app.ActivityThread.main(ActivityThread.java:5688)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:749)

JoyStickView.java:304就是这一行:myCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);我想在AsyncTask期间取消onstop(),但这似乎不起作用。任何人都对我能做什么有任何建议?

更新post之后,我能够按照LunarLander示例并实施new Runnable()来实现我的需求。

1 个答案:

答案 0 :(得分:0)

您正在从不建议的单独线程访问UI元素。 NullPointerException非常简单。使用后退按钮完成活动时,myCanvas = getHolder().lockCanvas();正在设置myCanvas属性的空引用。

如果代码在您的UI线程内(即在您的活动生命周期中的任何函数内),这可能由活动生命周期函数(如onStoponDestroy)处理。但是,您尝试从除myCanvas资源管理范围之外的UI线程以外的单独线程更改UI元素(在您的情况下为Activity)。因此getHolder().lockCanvas()获取画布的空引用,并且您在绘图上获得NullPointerException

通过将接口实现为AsyncTask的侦听器,可以轻松避免这种情况,以便侦听器函数驻留在Activity中并正确处理资源。例如,您可以考虑像这样创建interface

public interface MultiplyResponseReceiver {
    void onMultiplyResponseReceived(Bitmap[] bitmapArray);
}

现在在AsyncTask中使用此界面的属性。

public class MultiplyTask extends AsyncTask<Void, Bitmap, Bitmap[]> {

    public MultiplyResponseReceiver listener;

    @Override
    protected void onPreExecute() {
        //super.onPreExecute();

    }

    @Override
    protected Bitmap[] doInBackground(Void... progress_data) {

        Bitmap[] bitmapArray = new Bitmap[2];
        bitmapArray[0] = BitmapFactory.decodeResource(getResources(), R.drawable.joystick_base);
        bitmapArray[1] = BitmapFactory.decodeResource(getResources(), R.drawable.joystick_hat);
        //publishProgress(bitmapArray);
        return bitmapArray;
    }

    @Override
    protected void onProgressUpdate(Bitmap... bitmapArray) {
        //super.onProgressUpdate(values);\


    }

    @Override
    protected void onPostExecute(Bitmap[] bitmapArray) {
        // super.onPostExecute(s);
        listener.onMultiplyResponseReceived(bitmapArray);
    }
}

现在在Activity中实现这样的界面。

public class YourActivity extends AppCompatActivity implements MultiplyResponseReceiver {

    @Override
    public void onMultiplyResponseReceived() {

        myCanvas = getHolder().lockCanvas();
        myCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        switch (getId()) {
            case R.id.joystickRight:
                // draw_joystick_base(myCanvas, R.drawable.joystick_base);

                float c = bitmapArray[0].getWidth() / 2;
                float d = bitmapArray[0].getHeight() / 2;
                myCanvas.drawBitmap(bitmapArray[0], centerX - c, centerY - d, new Paint());
                break;
            case R.id.joystickLeft:
                // draw_joystick_base(myCanvas, R.drawable.joystick_base);

                float c1 = bitmapArray[0].getWidth() / 2;
                float d1 = bitmapArray[0].getHeight() / 2;
                myCanvas.drawBitmap(bitmapArray[0], centerX - c1, centerY - d1, new Paint());
                break;
        }

        float a = bitmapArray[1].getWidth() / 2;
        float b = bitmapArray[1].getHeight() / 2;
        myCanvas.drawBitmap(bitmapArray[1], hatX_tmp - a, hatY_tmp - b, new Paint());
        getHolder().unlockCanvasAndPost(myCanvas);
        // Do things like update the progress bar
    }
}

在您的活动中启动AsyncTask时,请不要忘记将侦听器分配给它。

MultiplyTask task = new MultiplyTask();
task.listener = this; 
task.execute();

代码未经过测试,可能包含错误。请根据您的需要进行修改。我想你已经有了这个想法。