安卓应用开发
目录
项目一:双摄像头显示,HTTP实时上传预览数据
1.1、软件简介
? 打开APK后会自动打开摄像头,在上下界面中会显示前置和后置的画面,在下方可设置HTTP服务器的IP及端口,点击链接后,会一直发送当前的预览画面。
1.2、源码分析
因为要截取图像,使用拍照的话会特别慢,所以项目中采用截取预览画面的方案。方案中使用的Camera API 为 Camera1可兼容大部分机器。(Android 从5.0开始就有了Camera 2 API)
在界面配置中添加两个SurfaceView,用于显示两个摄像头预览。
activity_main.xml 部分
<SurfaceView
android:id="@+id/surfaceView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.5" />
<SurfaceView
android:id="@+id/surfaceView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0.5"/>
在MainActivity.java中配置摄像头显示
MainActivity.java 部分
surfaceView1 = (SurfaceView) this.findViewById(R.id.surfaceView1);
surfaceHolder1 = surfaceView1.getHolder();
surfaceView2 = (SurfaceView) this.findViewById(R.id.surfaceView2);
surfaceHolder2 = surfaceView2.getHolder();
设置摄像头
private void openCamera() {
try {
camera1 = Camera.open(1);
camera2 = Camera.open(0);
} catch (Exception e) {
Log.e("camera", "open camera error!");
e.printStackTrace();
return;
}
Camera.Parameters params = camera1.getParameters();
params.setPictureFormat(PixelFormat.JPEG);
camera1.setParameters(params);
params = camera2.getParameters();
params.setPictureFormat(PixelFormat.JPEG);
//params.setRotation(90);
camera2.setParameters(params);
try {
camera1.setPreviewDisplay(surfaceHolder1);
camera2.setPreviewDisplay(surfaceHolder2);
} catch (IOException e) {
// TODO Auto-generated catch block
Log.e("camera", "preview failed.");
e.printStackTrace();
}
camera1.startPreview();
camera2.startPreview();
camera1.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
camera.addCallbackBuffer(data);
Camera.Size previewSize = camera.getParameters().getPreviewSize();
YuvImage image = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
if (image != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 80, stream);
final Bitmap bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
if (bitmap != null) {
if (isSending == false){
saveBitmap2ByteArray(bitmap, 1);
}
}
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
camera2.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
camera.addCallbackBuffer(data);
Camera.Size previewSize = camera.getParameters().getPreviewSize();
//Log.d(">>>>>>>>>>>", "Camera 2 parameters" + Integer.toString(previewSize.height) + Integer.toString(previewSize.width));
YuvImage image = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
if (image != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 80, stream);
final Bitmap bitmap = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
if (bitmap != null) {
if (isSending == false){
saveBitmap2ByteArray(bitmap, 2);
}
}
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
调整摄像头方向可直接使用
//params.setRotation(90);//参数0 90 180 270
预览数据转为jpg数据
private void saveBitmap2ByteArray(Bitmap bitmap, int cameraChoose)
{
try {
if(isSendStart) {
if (cameraChoose == 1) {
if (byteArrayOutputStreamBitmapCamera1 != null) {
byteArrayOutputStreamBitmapCamera1.close();
}
byteArrayOutputStreamBitmapCamera1 = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStreamBitmapCamera1);
} else {
if (byteArrayOutputStreamBitmapCamera2 != null) {
byteArrayOutputStreamBitmapCamera2.close();
}
byteArrayOutputStreamBitmapCamera2 = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStreamBitmapCamera2);
}
}
} catch (Exception e) {
e.printStackTrace();
Log.d(">>>>>>>>>>>>>>>>>>>>>", "saveBitmap2ByteArray err" + e);
}
}
Http上传数据
public void httpPostImage(String urlstr) {
String end = "\r\n";
String twoHyphens = "--";
String boundary = "*****";//边界标识
int TIME_OUT = 2*1000; //超时时间
HttpURLConnection con = null;
DataOutputStream ds = null;
InputStream is = null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(System.currentTimeMillis());
//OutputStream outputStream;
try {
URL url = new URL(urlstr);
con = (HttpURLConnection) url.openConnection();
con.setReadTimeout(TIME_OUT);
con.setConnectTimeout(TIME_OUT);
/* 允许Input、Output,不使用Cache */
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
// 设置http连接属性
con.setRequestMethod("POST");//请求方式
con.setRequestProperty("Connection", "Keep-Alive");//在一次TCP连接中可以持续发送多份数据而不会断开连接
con.setRequestProperty("Charset", "UTF-8");//设置编码
con.setRequestProperty("Content-Type",//multipart/form-data能上传文件的编码格式
"multipart/form-data;boundary=" + boundary);
ds = new DataOutputStream(con.getOutputStream());
ds.writeBytes("Content-Disposition: form-data; "
+ "name=\"stblog\";filename=\"" + "camera1.jpg+camera2.jpg+" + "\"" + end);
ds.writeBytes(end);
byte[] buffer1 = byteArrayOutputStreamBitmapCamera1.toByteArray();
byte[] decodeBytes1 = Base64.encode(buffer1, 0, buffer1.length, Base64.DEFAULT);
ds.write(decodeBytes1, 0, decodeBytes1.length);
ds.writeBytes("\r\n----------\r\n");
byte[] buffer2 = byteArrayOutputStreamBitmapCamera2.toByteArray();
byte[] decodeBytes2 = Base64.encode(buffer2, 0, buffer2.length, Base64.DEFAULT);
ds.write(decodeBytes2, 0, decodeBytes2.length);
ds.writeBytes(end);
ds.writeBytes(twoHyphens + boundary + twoHyphens + end);//结束
ds.flush();
StringBuffer b = new StringBuffer();
int responseCode = con.getResponseCode();
//Log.d(">>>>>>>>>>>>>>>>>>>", "responseCode " + Integer.toString(responseCode));
if (responseCode == 200) {
is = con.getInputStream();
int ch;
while ((ch = is.read()) != -1) {
b.append((char) ch);
}
}
Log.d(">>>>>>>>>>>>>>>", "Send OK!!!!!!");
} catch (Exception e) {
e.printStackTrace();
Log.d(">>>>>>>>>>>>>>>>>", "Http send err " + e);
textViewMessage.setText(simpleDateFormat.format(date) + " Cnt: " + Integer.toString(sendCnt) + " Err;Res > " + e);
} finally {
/* 关闭DataOutputStream */
if(ds!=null){
try {
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (con != null) {
con.disconnect();
}
}
}
最后别忘了增加申请权限、动态申请权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
public void checkPermission()
{
int targetSdkVersion = 0;
try {
final PackageInfo info = this.getPackageManager().getPackageInfo(this.getPackageName(), 0);
targetSdkVersion = info.applicationInfo.targetSdkVersion;//获取应用的Target版本
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (targetSdkVersion >= Build.VERSION_CODES.M) {
boolean isAllGranted = checkPermissionAllGranted(PermissionString);
if (isAllGranted) {
return;
}
ActivityCompat.requestPermissions(this,
PermissionString, 1);
}
}
}
/**
* 检查是否拥有指定的所有权限
*/
private boolean checkPermissionAllGranted(String[] permissions) {
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
//申请权限结果返回处理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
boolean isAllGranted = true;
// 判断是否所有的权限都已经授予了
for (int grant : grantResults) {
if (grant != PackageManager.PERMISSION_GRANTED) {
isAllGranted = false;
break;
}
}
if (isAllGranted) {
// 所有的权限都授予了
Log.e("err","权限都授权了");
}
}
}
项目二:界面全屏打开一个网页
2.1、软件简介
一个WebView的Demo,只可用于简单演示。全屏显示一个网页。
2.2、源码分析
全屏显示
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 全屏展示
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 全屏显示,隐藏状态栏和导航栏,拉出状态栏和导航栏显示一会儿后消失。
MainActivity.this.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
} else {
// 全屏显示,隐藏状态栏
MainActivity.this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
}
}
setContentView(R.layout.activity_main);
WebView
webView = findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("http://www.bilibili.com/");
webView.setWebViewClient(new WebViewClient());
项目三:UDP接收ANT数据
3.1、软件简介
? Ant数据是有板子上的NRF52832接收后,通过串口上传过来的,用C应用将接收的数据通过UDP方式上传到本机的50001端口。相当于透传。本APK仅用作演示。
3.2、源码分析
建立UDP连接并解析数据
DatagramSocket socket = null;
try {
socket = new DatagramSocket(50001);
byte[] data2=new byte[1024];
byte[] recvData;
DatagramPacket packet2=new DatagramPacket(data2, data2.length);
while(true) {
socket.receive(packet2);
recvData = packet2.getData();
//Log.d(">>>>>>>>>>>", "length" + Integer.toString(packet2.getLength()));
udpRecvDataHandle(packet2.getData(), packet2.getLength());
Thread.sleep(1);
}
} catch (Exception e) {
e.printStackTrace();
Log.d(">>>>>>>>>>>", "err:" + e);
} finally {
if(socket != null){
socket.close();
}
}