Android 是否按顺序执行任务?

Android executes task sequentially or not?

我的项目中 Android Studio 出现了一个非常有趣的行为。对不同活动的调用没有按顺序执行!

我正在构建一个项目,其中的调用是这样的:

button.setOnClickListener()
{
    (1) call to cusom camera acivity
    (2) call to activity showing preview of latest captured image         
    (3) call to a doCrop() function
} 

但是执行的时候,实际的流程是:

    (1) call to the doCrop() function
    (2) call to activity showing preview of image captured
    (3) call to cusom camera acivity

自定义相机 activity 处理所有必要的 SurfaceHolderSurfaceView 操作。

这种效果是因为 SurfaceView 布局创建和销毁需要更多时间并且 android 首先切换到更简单的任务吗?

即便如此,它也应该跳到 preview activity 而不是 doCrop() 调用。

这里发生了什么?请指点!

谢谢!

编辑:

命名为:

MainActivity - 主要 activity

Preview - 创建相机实例

CameraPreview - 处理 SurfaceView 等

ImagePreview - 显示指定图片

Main activity代码:

photo.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                File temp = new File(//path to store image);
                imageUri=Uri.fromFile(temp);

                 Intent intent = new Intent(MainActivity.this,Preview.class);

                startActivity(intent);

               // Image Preview Activity
                Intent intent1=new Intent(MainActivity.this,ImagePreview.class);
                startActivity(intent1);

                //Crop function
                doCrop();

            }

        });

preview activity代码:

public class Preview extends Activity {
    private static final String TAG = "CamTestActivity";
    CameraPreview preview;
    Button buttonClick;
    Camera camera;
    Activity act;
    Context ctx;
    Uri uri;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ctx = this;
        act = this;
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.activity_preview);

        preview = new CameraPreview(this, (SurfaceView)findViewById(R.id.surfaceView));
        preview.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        ((FrameLayout) findViewById(R.id.layout)).addView(preview);
        preview.setKeepScreenOn(true);

        buttonClick = (Button) findViewById(R.id.btnCapture);

        buttonClick.setOnClickListener(new OnClickListener()
        {
            public void onClick(View v) {
                camera.takePicture(shutterCallback, rawCallback, pngCallback);
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        int numCams = Camera.getNumberOfCameras();
        if(numCams > 0){
            try{
                camera = Camera.open(0);
                camera.startPreview();
                preview.setCamera(camera);
            } catch (RuntimeException ex){
                Toast.makeText(ctx, getString(R.string.camera_not_found), Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    protected void onPause() {
        if(camera != null) {
            camera.stopPreview();
            preview.setCamera(null);
            camera.release();
            camera = null;
        }
        super.onPause();
    }

    private void resetCam() {
        camera.startPreview();
        preview.setCamera(camera);
    }

    private void refreshGallery(File file) {
        Intent mediaScanIntent = new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        mediaScanIntent.setData(Uri.fromFile(file));
        sendBroadcast(mediaScanIntent);
    }

    ShutterCallback shutterCallback = new ShutterCallback() {
        public void onShutter() {

        }
    };

    PictureCallback rawCallback = new PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {

        }
    };

    PictureCallback pngCallback = new PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {
            new SaveImageTask().execute(data);
            resetCam();
            Log.d(TAG, "onPictureTaken - png");
        }
    };

    private class SaveImageTask extends AsyncTask<byte[], Void, Void> {
        @Override
        protected Void doInBackground(byte[]... data) {
              // save the image
            }
            catch (Exception e) { e.printStackTrace();}

            return null;
        }
    }
}

CameraPreview代码:

class CameraPreview extends ViewGroup implements SurfaceHolder.Callback {
    private final String TAG = "Preview";

    SurfaceView mSurfaceView;
    SurfaceHolder mHolder;
    Size mPreviewSize;
    List<Size> mSupportedPreviewSizes;
    Camera mCamera;

    CameraPreview(Context context, SurfaceView sv) {
        super(context);

        mSurfaceView = sv;

        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public void setCamera(Camera camera) {
        mCamera = camera;
        if (mCamera != null) {
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            requestLayout();

            // get Camera parameters
            Camera.Parameters params = mCamera.getParameters();

            List<String> focusModes = params.getSupportedFocusModes();
            if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
                // set the focus mode
                params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);

                // set Camera parameters
                mCamera.setParameters(params);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);

        if (mSupportedPreviewSizes != null) {
            mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed && getChildCount() > 0) {
            final View child = getChildAt(0);

            final int width = r - l;
            final int height = b - t;

            int previewWidth = width;
            int previewHeight = height;
            if (mPreviewSize != null) {
                previewWidth = mPreviewSize.width;
                previewHeight = mPreviewSize.height;
            }

            // Center the child SurfaceView within the parent.
            if (width * previewHeight > height * previewWidth) {
                final int scaledChildWidth = previewWidth * height / previewHeight;
                child.layout((width - scaledChildWidth) / 2, 0,
                        (width + scaledChildWidth) / 2, height);
            } else {
                final int scaledChildHeight = previewHeight * width / previewWidth;
                child.layout(0, (height - scaledChildHeight) / 2,
                        width, (height + scaledChildHeight) / 2);
            }
        }
    }

    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, acquire the camera and tell it where
        // to draw.
        try {
            if (mCamera != null) {
                mCamera.setPreviewDisplay(holder);
            }
        } catch (IOException exception) {
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface will be destroyed when we return, so stop the preview.
        if (mCamera != null) {
            mCamera.stopPreview();
        }
    }


    private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio = (double) w / h;
        if (sizes == null) return null;
        Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        // Try to find an size match aspect ratio and size
        for (Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        // Cannot find the one match the aspect ratio, ignore the requirement
        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        if(mCamera != null) {
            Camera.Parameters parameters = mCamera.getParameters();
            parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
            requestLayout();

            mCamera.setParameters(parameters);
            mCamera.startPreview();
        }
    }

}

我认为这更像是一个设计问题。 您真的应该一次打开多个 activity 吗?

当 activity 启动时,它被放在历史堆栈的顶部,这可能给人一种它正在以相反方式发生的错觉。

但在看到代码之前我无法进一步评论。

更新:是的,设计问题。

这是它应该的工作方式:

  1. MainActivity 中的 Onclick 操作应仅使用 startActivityForResult() 打开预览 activity。
  2. 用户拍照,触发setResult()并关闭相机activity。
  3. 结果返回到 MainActivity,触发 onActivityResult()
  4. onActivityResult 中的代码启动 ImagePreview activity。
  5. ImagePreview 中的 onCreate 函数触发 doCrop();

我建议您仔细阅读此处的 Android 文档: http://developer.android.com/guide/components/activities.html

Android 不如其他平台灵活(或更有帮助),因此您必须很好地理解 API.

Activity 启动代码是异步的,如果同时启动多个活动,如下所示:

startActivity(intent1);
startActivity(intent2);
startActivity(intent3);

不保证它们会按顺序显示给用户,即 "launch order"。

这是预期的行为,您通常不应该这样做,因为这表明您的应用程序存在设计缺陷

所以您要做的是启动 3 个活动 simultaneously.These,所有活动都需要在 onActivityResult() 中处理。但是一个用户不能同时访问 3 个活动。 所以修改你的流程。 应该是这样的

  1. 启动 CameraActivity
  2. 在你调用 activity 的 onActivityResult() 中,你调用 do crop 并做你想做的事。

您需要重新审视您的工作流程,因为:

  1. 您没有启动 3 个活动。
  2. 您正在根据用户交互触发事件,因此不应从您的代码中控制函数的顺序。

您的任务执行顺序错误,因为它们被异步调用。要按照您指定的顺序执行它们,请调用 synchronous,可以使用 startActivityForResult(intent, requestCode);