上次讲了通过传感器实现真随机算法,今天讲一下另外的方法:借助手机和平板硬件生成物理随机数,并且生成的数字无法预测,无法重复。方法是利用系统摄像头获取视频数据,再转换成随机数种子,同理也可以用麦克风进行录制音频数据作为种子。
package com.zwxuf.randomgenerator;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.graphics.drawable.ColorDrawable;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.os.Handler;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.WindowManager;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import androidx.annotation.NonNull;
public class RandomGenerator implements TextureView.SurfaceTextureListener {
private static final String TAG = "RandomGenerator";
private Context mContext;
private CameraDevice mCameraDevice;
private ImageReader mImageReader;
private CameraCaptureSession mCameraCaptureSession;
private OnGenerateListener onGenerateListener;
private boolean mStarting;
private boolean mStopped;
private CameraManager mCameraManager;
private TextureView mTextureView;
private Size mPreviewSize;
private boolean mCanceled;
public RandomGenerator(Context mContext) {
this.mContext = mContext;
mCameraManager = (CameraManager) mContext.getSystemService(Service.CAMERA_SERVICE);
}
public void start() {
mTextureView.setSurfaceTextureListener(this);
openCamera(mTextureView.getWidth(), mTextureView.getHeight());
}
@SuppressLint("MissingPermission")
private void openCamera(int width, int height) {
if (isStarting()) return;
mStarting = true;
mStopped = false;
mCanceled = false;
try {
String cameraId = getMainCameraId(width, height);
Log.i(TAG, "cameraId=" + cameraId);
mCameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
startPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
if (!mStopped) {
assertTask("相机断开");
}
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
assertTask("打开相机失败,错误代码" + error);
}
}, null);
} catch (Exception e) {
Log.e(TAG, e.toString());
assertTask("打开相机失败:" + e.toString());
}
}
private String getMainCameraId(int width, int height) {
String id = null;
try {
for (String cameraId : mCameraManager.getCameraIdList()) {
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
//默认打开后置摄像头
if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT)
continue;
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
id = cameraId;
break;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
return id == null ? "0" : id;
}
/**
* 从摄像头支持的预览Size中选择大于并且最接近width和height的size
*
* @param sizeMap
* @param width
* @param height
* @return
*/
private Size getOptimalSize(Size[] sizeMap, int width, int height) {
List<Size> sizeList = new ArrayList<>();
for (Size option : sizeMap) {
//Dbg.print("系统支持的尺寸大小==width:" + option.getWidth() + " height:" + option.getHeight());
if (width > height) {
if (option.getWidth() > width && option.getHeight() > height) {
sizeList.add(option);
}
} else {
if (option.getWidth() > height && option.getHeight() > width) {
sizeList.add(option);
}
}
}
if (sizeList.size() > 0) {
return Collections.min(sizeList, new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
}
});
}
if (sizeList.size() > 0)
return sizeList.get(0);
return sizeMap[0];
}
private void startPreview() {
try {
SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface mSurface = new Surface(mSurfaceTexture);
final CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
builder.addTarget(mSurface);
mCameraDevice.createCaptureSession(Collections.singletonList(mSurface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
mCameraCaptureSession = session;
try {
session.setRepeatingRequest(builder.build(), mCaptureCallback, null); // 开始连续捕获图片
} catch (CameraAccessException e) {
e.printStackTrace();
assertTask(null);
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
assertTask(null);
}
}, null);
} catch (Exception e) {
e.printStackTrace();
assertTask(null);
}
}
private void release() {
mStopped = true;
if (mCameraCaptureSession != null) {
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
// try {
// if (mImageReader != null) {
// mImageReader.close();
// mImageReader = null;
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
try {
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
} catch (Exception e) {
e.printStackTrace();
}
mStarting = false;
}
private void assertTask(String msg) {
release();
if (onGenerateListener != null) {
onGenerateListener.onError(msg);
}
}
public void stop() {
if (!isStarting()) return;
release();
if (mCanceled) return;
if (onGenerateListener == null) return;
Bitmap bitmap = mTextureView.getBitmap();
if (bitmap != null) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
byte[] data = out.toByteArray();
long seed = makeRandomSeed(data);
mTextureView.invalidate();
if (onGenerateListener != null) {
onGenerateListener.onSuccess(seed);
}
}
}
/**
* 制作随机数种子
* @param source
* @return
*/
private long makeRandomSeed(byte[] source) {
if (source == null || source.length == 0) {
return 0;
}
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(source);
byte[] hashBytes = digest.digest();
BigInteger bi = new BigInteger(1, hashBytes);
return bi.remainder(BigInteger.valueOf(Long.MAX_VALUE)).longValue(); //取余,也可以取中间8个字节
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
};
public void setOnGenerateListener(OnGenerateListener onGenerateListener) {
this.onGenerateListener = onGenerateListener;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.i(TAG, "onSurfaceTextureAvailable");
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Log.i(TAG, "onSurfaceTextureDestroyed");
assertTask(null);
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
public void cancel() {
mCanceled = true;
release();
if (onGenerateListener != null) {
onGenerateListener.onCancel();
}
}
public boolean isCanceled() {
return mCanceled;
}
public interface OnGenerateListener {
void onSuccess(long seed);
void onError(String msg);
void onCancel();
}
public boolean isStarting() {
return mStarting;
}
public void setTextureView(TextureView mTextureView) {
this.mTextureView = mTextureView;
}
}
package com.zwxuf.randomgenerator;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements View.OnTouchListener, RandomGenerator.OnGenerateListener {
private static final String TAG = "RandomGenerator";
private Button bn_generate;
private RandomGenerator mGenerator;
private TextureView mTextureView;
private TextView tv_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextureView = findViewById(R.id.mTextureView);
bn_generate = findViewById(R.id.bn_generate);
tv_content = findViewById(R.id.tv_content);
mGenerator = new RandomGenerator(this);
mGenerator.setOnGenerateListener(this);
mGenerator.setTextureView(mTextureView);
bn_generate.setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
mTextureView.setVisibility(View.VISIBLE);
tv_content.setVisibility(View.INVISIBLE);
mGenerator.start();
} else {
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.CAMERA
}, 1000);
}
break;
case MotionEvent.ACTION_MOVE:
if (mGenerator.isCanceled()) return false;
int x = (int) event.getX();
int y = (int) event.getY();
Rect localRect = new Rect();
bn_generate.getLocalVisibleRect(localRect);
if (!localRect.contains(x, y)) {
mGenerator.cancel();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
bn_generate.releasePointerCapture();
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mGenerator.stop();
break;
}
return false;
}
@Override
public void onSuccess(long seed) {
Log.i(TAG, "seed=" + seed);
Toast.makeText(this, "" + seed, Toast.LENGTH_SHORT).show();
mTextureView.setVisibility(View.INVISIBLE);
tv_content.setVisibility(View.VISIBLE);
//使用Random正常生成随机数
Random random = new Random(seed);
//例:生成五注双色球
tv_content.setText(null);
for (int i = 0; i < 5; i++) {
String numbers = generateUnionLotto(random);
tv_content.append(numbers);
tv_content.append("\n");
}
}
private String generateUnionLotto(Random random) {
LinkedList<Integer> numbers = new LinkedList<>();
for (int i = 0; i < 33; i++) {
numbers.add(i + 1);
}
StringBuilder sb = new StringBuilder();
int[] reds = new int[6];
for (int i = 0; i < 6; i++) {
int index = random.nextInt(numbers.size());
reds[i] = numbers.get(index);
numbers.remove(index);
}
int[] blues = new int[1];
for (int i = 0; i < blues.length; i++) {
blues[i] = random.nextInt(16) + 1;
}
Arrays.sort(reds);
Arrays.sort(blues);
for (int red : reds) {
sb.append(String.format("%02d", red)).append(" ");
}
sb.append(" + ");
for (int blue : blues) {
sb.append(String.format("%02d", blue));
}
return sb.toString();
}
@Override
public void onError(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onCancel() {
mTextureView.setVisibility(View.INVISIBLE);
Toast.makeText(this, "取消", Toast.LENGTH_SHORT).show();
}
}

1106

被折叠的 条评论
为什么被折叠?



