实现 AsyncTask 时返回空指针引用

back press on a null pointer reference when implemented AsyncTask

我创建了一个名为 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();
    }
}

而内class 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); 我想在 onstop() 期间取消我的 AsyncTask 但这似乎不起作用。有人对我能做什么有什么建议吗?

更新: 按照这个 post, I was able to follow the LunarLander 示例并实施 new Runnable() 来实现我需要的。

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

如果代码在您的 UI 线程内(即在您的 activity 生命周期中的任何函数内),这可以由 activity 生命周期处理onStoponDestroy 等函数。但是,您正试图从 UI 线程之外的单独线程更改 UI 元素(在您的情况下为 myCanvas),该线程超出了 Activity 资源管理范围。因此,getHolder().lockCanvas() 获得了 canvas 的空引用,而您在绘图时获得了 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
    }
}

并且在您的 activity 中启动 AsyncTask 时,不要忘记为其分配侦听器。

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

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