android 实时图像传输实现

android 实时图像传输实现(java)

本质就是图像传输,在次基础上的扩展。

实现原理

其实就是图像一帧一帧的传输,一直下去就能达到实时图像传输的效果了。

具体实现
socket对象调用 getInputStream()方法获得一个输入流 inputStream,同时使用 FileOutputStream(file)方法创建一个文件输出流用来保存图片数据其中 file为图片的绝对路径,之后对 inputStream不断调用 read ()方法进行数据读取,并且将读取的数据通过输出流的 write()方法写入本地,本地则是通过handle来获得的图片并通过BitmapFactory.decodeFile函数来得到一个Bitmap图像,最后通过进行相应比例的放大,通过 paint(画笔工具)在canvas(画布)上绘制并提交画布显示到 SurfaceView上。

涉及知识

TCP 、socket ,java文件流,android基础、SurfaceView

参考代码

TCP接受部分函数 image_transmission()
这里只是贴了图像接受部分修改的函数,并不是具体实现的文件,具体实现可参考[java图像传输]。(https://blog.csdn.net/NS_ice/article/details/124306479?spm=1001.2014.3001.5502)

// 图像传输客户端程序  这个服务端接受线程中使用的函数
    private void image_transmission() throws IOException {
            File file = new File("data/data/com.example.uidemo4/1.jpg");
            //com.example.uidemo4 是安卓软件的包名  这个文件夹下的文件 app才有权限进行读取和修改
        if (!file.exists()) {                //exists()  判断是否存在,存在返回true
            System.out.println("建立此文件");
            boolean a= false;                 //不存在就创建一个,空文件
            try {
                a = file.createNewFile();  	//文件不存在且新建成功会返回true
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (a) {
                System.out.println(file.toString().trim());
            }
        }
        inputStream = socket.getInputStream();
        FileOutputStream fos = null;
        byte[] temByte = new byte[2048]; // 读取缓冲数组
        byte[] preByte = new byte[15];	//数据头
        byte[] sizeByte = new byte[5];	//图片大小
        byte[] endByte = new byte[3];	// 数据尾
        int all = 0;// 已读取的文件总长度
        while (flag) {
            int len;
            len = inputStream.read(preByte);//读取数据头 
            String readTextString = new String(preByte, "UTF-8");
          	   if (readTextString.equals("start")) {
//                  System.out.println("开始接收图像");
                	len = inputStream.read(sizeByte); //读取图片大小
                	String a = new String(sizeByte, "UTF-8");
                	int length = Integer.parseInt(a);
//                  System.out.println("图片大小为:" + a);
               		fos = new FileOutputStream(file);

                for (all = 0;all<length;) {

                    if(all+2048<=length) {
//                        System.out.println("剩余部分大于1024");
                        len = inputStream.read(temByte);
                        all = all +len;
                        fos.write(temByte, 0, len); //通过文件流写入文件
//                        System.out.println("读取到"+len +"剩余"+(length-all));
                    }else{
                        len = inputStream.read(temByte,0,length-all);
                        all = all +len;
                        fos.write(temByte, 0, len);
//                        System.out.println("读取到"+len +"剩余"+(length-all));
                        if (all ==length) {
						//加这个判断的原因是及时你设置读取数组大小为2048,
						//但是每次读取的可能会比他小(可能由于网络原因)
                            len = inputStream.read(endByte);
                            readTextString = new String(endByte, "UTF-8");
//                        System.out.println("结束为:" + readTextString);
                            if (readTextString.equals("end")) {
//                       		 System.out.println("接收到图片");
								//确保读取完毕后通过handler发送消息在handler中更新ui
                                handler.sendEmptyMessage(MyHandler.READ_DATA);
                                //发送空内容消息,触发绘图 msg.what = READ_DATA =9;
                                fos.close();
                                //关闭文件流,循环开始时会创建,你可能觉得为啥不一直用一个
                             //用一个流没有结束,好像就那个图片文件不算传输结束了,后面无法使用该文件
                                break;
                            }
                        }

                    	}//else
               } //for (all = 0;all
            } //if (readTextString.equals("start"))
        }//while (flag)

    }//image_transmission()  
            

注意这里的接受,发送端需要根据接受端的接受顺序来发送数据
发送顺序为: start (数据头字符串)+ 图片大小(字符串)+ 图片文件 + end (数据结束尾)
在图片大小前和数据头前还可以加入字符串数据,图片文件尾后和end前也可以加入字符串数据,接收方法和数据头尾类似。

直接复制代码会报错:

  1. File file = new File(“data/data/com.example.uidemo4/1.jpg”); 路径问题
  2. inputStream = socket.getInputStream(); 需要自己定义全局变量
  3. .sendEmptyMessage(MyHandler.READ_DATA); 需要自己new 一个MyHandler对象。
MyHandler handler = new MyHandler(MainActivity.this ,mSurfaceholder);

此外 surfaceholder需要通过xml中的控件SurfaceView来获取。

		//全局变量
    	private SurfaceView mCameraView;                //surfaceView
   		private SurfaceHolder mSurfaceholder;

		//onCreate函数中通过id获取控件
        mSurfaceView =findViewById(R.id.surface_view);
        mSurfaceholder = mSurfaceView .getHolder();

xml文件 布局文件
大小宽度布局自己根据需求来设置

    <SurfaceView
        android:id="@+id/surface_view"
        android:layout_width="320dp"
        android:layout_height="240dp"
        android:layout_margin="30dp"
        />

handle 消息处理机制

用于更新ui界面,详情可以去网上搜具体功能(资源很多),这里只简单介绍使用。

public class MyHandler extends Handler
{
	public static final int READ_DATA = 9;  //数字没有具体意义就是为了区分消息的作用,用于下方switch判断。
	private Rect mSrcRect, mDestRect;
	private SurfaceHolder surfaceholder;
	//private SurfaceView surfaceView;
    private Paint paint;		//画笔工具,理解就好,没有特别需要注意的
    private Bitmap bitmap;		// 图片文件,用户绘制
    private Canvas canvas;		//画布工具,能用就行
    //  构造方法  可根据需求自己创建
    MyHandler(Context context, SurfaceHolder surfaceholder)
    {
        this.context = context;
        this.surfaceholder = surfaceholder;
    }
    
 @Override
    public void handleMessage(@NonNull Message msg)
    {

        switch (msg.what)
        {
            case READ_DATA:
              try {
                    canvas = surfaceholder.lockCanvas();//锁定画布
                    if(canvas!=null){
//                        System.out.println("开始绘制");
                        paint = new Paint();
                        paint.setAntiAlias(true);
                        paint.setStyle(Paint.Style.STROKE);
                           
//                        canvas.drawColor(Color.WHITE);   //绘制背景
                        bitmap = BitmapFactory.decodeFile("data/data/com.example.uidemo4/1.jpg");  // 获取bitmap
                        // 文件路径就是上面接受端保存路径的位置
                        mSrcRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
                        mDestRect = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
						//这两句是用来获取图片大小和画布大小,在下面绘制函数中用到的参数,使用这个可以让图片填充到画布相同大小
						//这里的画布大小,其实就是你xml文件中SurfaceHolder的大小
                        canvas.drawBitmap(bitmap, mSrcRect, mDestRect, paint); //执行绘制操作
//                        canvas.drawBitmap(bitmap, 0, 0, paint); //不调节图像与画布比例,直接画原图
                    }
                }catch (Exception e){

                }finally {
                    if (canvas != null) {
                        surfaceholder.unlockCanvasAndPost(canvas);//解锁并提交画布
//                        System.out.println("绘制完成");
                        if (bitmap!=null){                
                        bitmap.recycle();}
               			 break; 
                        }
   			 }
    }

以上就是图像实时传输的主要实现代码,并没有把完整工程文件代码贴出来,显得冗余,主要是提供一个思路和核心代码实现吧,代码中注释还是比较详细的,有不理解的可以网上查或者评论区留言,看到就会回复的,觉得还不错的话可以高抬贵手点个赞。有需要话可以整个手把手教程。

你可能感兴趣的:(Android开发,android,java)