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 处理所有必要的 SurfaceHolder
和 SurfaceView
操作。
这种效果是因为 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 启动时,它被放在历史堆栈的顶部,这可能给人一种它正在以相反方式发生的错觉。
但在看到代码之前我无法进一步评论。
更新:是的,设计问题。
这是它应该的工作方式:
- MainActivity 中的 Onclick 操作应仅使用 startActivityForResult() 打开预览 activity。
- 用户拍照,触发setResult()并关闭相机activity。
- 结果返回到 MainActivity,触发 onActivityResult()
- onActivityResult 中的代码启动 ImagePreview activity。
- 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 个活动。
所以修改你的流程。
应该是这样的
- 启动 CameraActivity
- 在你调用 activity 的
onActivityResult()
中,你调用 do crop 并做你想做的事。
您需要重新审视您的工作流程,因为:
- 您没有启动 3 个活动。
- 您正在根据用户交互触发事件,因此不应从您的代码中控制函数的顺序。
您的任务执行顺序错误,因为它们被异步调用。要按照您指定的顺序执行它们,请调用 synchronous,可以使用 startActivityForResult(intent, requestCode);
我的项目中 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 处理所有必要的 SurfaceHolder
和 SurfaceView
操作。
这种效果是因为 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 启动时,它被放在历史堆栈的顶部,这可能给人一种它正在以相反方式发生的错觉。
但在看到代码之前我无法进一步评论。
更新:是的,设计问题。
这是它应该的工作方式:
- MainActivity 中的 Onclick 操作应仅使用 startActivityForResult() 打开预览 activity。
- 用户拍照,触发setResult()并关闭相机activity。
- 结果返回到 MainActivity,触发 onActivityResult()
- onActivityResult 中的代码启动 ImagePreview activity。
- 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 个活动。
所以修改你的流程。
应该是这样的
- 启动 CameraActivity
- 在你调用 activity 的
onActivityResult()
中,你调用 do crop 并做你想做的事。
您需要重新审视您的工作流程,因为:
- 您没有启动 3 个活动。
- 您正在根据用户交互触发事件,因此不应从您的代码中控制函数的顺序。
您的任务执行顺序错误,因为它们被异步调用。要按照您指定的顺序执行它们,请调用 synchronous,可以使用 startActivityForResult(intent, requestCode);