我录屏的方式是分别录制音频和视频,最后合并成mp4格式,比较麻烦,因为网上完整的教程比较少,所以我打算写一个完整版的,照着我的代码写完之后,至少是能够实现功能的,而不是简单的介绍下用法。

1既然是录制视频,我们应该有一个按钮控制开始和结束。

2在录制之前,需要先判断一下Android系统的版本是否大于5.0,并且动态申请一下权限(读写,录音,照相机),这一步可以在点开始按钮的时候执行

if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)

!= PackageManager.PERMISSION_GRANTED) {

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 102);

}

if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)

!= PackageManager.PERMISSION_GRANTED) {

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 103);

}

if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)

!= PackageManager.PERMISSION_GRANTED) {

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 104);

}

Intent intent = null;

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {

intent = mediaProjectionManager.createScreenCaptureIntent();

startActivityForResult(intent, 101);//正常情况是要执行到这里的,作用是申请捕捉屏幕

} else {

ShowUtil.showToast(context, "Android版本太低,无法使用该功能");

}

3定义MediaProjection和MediaProjectionManager等一些其他必要的变量

boolean isrun = false;//用来标记录屏的状态private MediaProjectionManager mediaProjectionManager;

private MediaProjection mediaProjection;//录制视频的工具private int width, height, dpi;//屏幕宽高和dpi,后面会用到

private ScreenRecorder screenRecorder;//这个是自己写的录视频的工具类,下文会放完整的代码

Thread thread;//录视频要放在线程里去执行

在onCreat里写好实例化

mediaProjectionManager = (MediaProjectionManager) context.getSystemService(MEDIA_PROJECTION_SERVICE);

WindowManager manager = this.getWindowManager();

DisplayMetrics outMetrics = new DisplayMetrics();

manager.getDefaultDisplay().getMetrics(outMetrics);

width = outMetrics.widthPixels;

height = outMetrics.heightPixels;

dpi = outMetrics.densityDpi;

4我们在onActivityResult回调方法中,来处理返回的事件

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

if (requestCode == 102) {

Toast.makeText(context, "缺少读写权限", Toast.LENGTH_SHORT).show();

return;

}

if (requestCode == 103) {

Toast.makeText(context, "缺少录音权限", Toast.LENGTH_SHORT).show();

return;

}

if (requestCode == 104) {

Toast.makeText(context, "缺少相机权限", Toast.LENGTH_SHORT).show();

return;

}

if (requestCode != 101) {

Log.e("HandDrawActivity", "error requestCode =" + requestCode);

}

if (resultCode != RESULT_OK) {

Toast.makeText(context, "捕捉屏幕被禁止", Toast.LENGTH_SHORT).show();

return;

}

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);

if (mediaProjection != null) {

screenRecorder = new ScreenRecorder(width, height, mediaProjection, dpi);

}

thread = new Thread() {

@Override

public void run() {

screenRecorder.startRecorder();//跟ScreenRecorder有关的下文再说,总之这句话的意思就是开始录屏的意思

}

};

thread.start();

binding.startPlayer.setText("停止");//开始和停止我用的同一个按钮,所以开始录屏之后把按钮文字改一下

isrun = true;//录屏状态改成真

}

5先放上ScreenRecorder代码,只想要结果的朋友呢,直接把类粘贴走,把报错的地方改一改(在我自己的项目里可是不报错的),就实现了录制屏幕的功能了,还想看看的,可以往下看看

import android.hardware.display.DisplayManager;

import android.media.MediaCodec;

import android.media.MediaCodecInfo;

import android.media.MediaFormat;

import android.media.MediaMuxer;

import android.media.MediaRecorder;

import android.media.projection.MediaProjection;

import android.os.Build;

import android.os.Environment;

import android.text.TextUtils;

import android.util.Log;

import android.view.Surface;

import com.coremedia.iso.boxes.Container;

import com.googlecode.mp4parser.authoring.Movie;

import com.googlecode.mp4parser.authoring.Track;

import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;

import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;

import com.googlecode.mp4parser.authoring.tracks.AppendTrack;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.RandomAccessFile;

import java.nio.ByteBuffer;

import java.nio.channels.FileChannel;

import java.util.ArrayList;

import java.util.LinkedList;

import java.util.List;

public class ScreenRecorder {

private int mWidth, mHeight, mDensty;

private MediaProjection mediaProjection;

private MediaCodec.BufferInfo mBufferInfo;

private MediaCodec mEncorder;

private Surface mInputSurface;

private MediaMuxer mMuxer;

private boolean isQuit = false;

private boolean mMuxerStarted = false;

private int mTrackIndex;

private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/cache";

private MediaRecorder mediaRecorder;

public ScreenRecorder(int mWidth, int mHeight, MediaProjection mediaProjection, int mDensty) {

this.mWidth = mWidth;

this.mHeight = mHeight;

this.mediaProjection = mediaProjection;

this.mDensty = mDensty;

File file = new File(path);

if (!file.exists()) {

file.mkdirs();

}

}

public void startRecorder() {

prepareRecorder();

startLuYin();

startRecording();

}

public void stop() {

isQuit = true;

releaseEncorders(1);

List filePath = new ArrayList<>();

filePath.add(path + "/APlanyinpin.amr");

filePath.add(path + "/APlanshipin.mp4");

joinVideo(filePath, path);

}

public void destory() {

releaseEncorders(0);

}

private void startLuYin() {

File file = new File(path, "APlanyinpin.amr");

mediaRecorder = new MediaRecorder();

mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);

mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);

mediaRecorder.setOutputFile(file.getAbsolutePath());

try {

mediaRecorder.prepare();

mediaRecorder.start();

Log.e("HandDrawActivity", "已经开始录音");

} catch (IOException e) {

e.printStackTrace();

}

}

private void prepareRecorder() {

mBufferInfo = new MediaCodec.BufferInfo(); //元数据,描述bytebuffer的数据,尺寸,偏移

//创建格式化对象 MIMI_TYPE 传入的 video/avc 是H264编码格式

MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);

int frameRate = 45;

format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);

format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);

format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);

format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);

format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);

format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);

format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);

try {

mEncorder = MediaCodec.createEncoderByType("video/avc");

mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

mInputSurface = mEncorder.createInputSurface();

mEncorder.start();

} catch (IOException e) {

e.printStackTrace();

releaseEncorders(0);

}

}

private void startRecording() {

File saveFile = new File(path, "APlanshipin.mp4");

try {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,

mInputSurface, null, null);

drainEncoder();

}

} catch (Exception e) {

e.printStackTrace();

}

}

private void drainEncoder() {

while (!isQuit) {

Log.e("TAG", "drain.....");

int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);

if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());

if (!mMuxerStarted && mTrackIndex >= 0) {

mMuxer.start();

mMuxerStarted = true;

Log.e("HandDrawActivity", "已经开始录屏");

}

}

if (bufferIndex >= 0) {

Log.e("TAG", "drain...write..");

ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);

if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {

mBufferInfo.size = 0;

}

if (mBufferInfo.size != 0) {

if (mMuxerStarted) {

bufferData.position(mBufferInfo.offset);

bufferData.limit(mBufferInfo.offset + mBufferInfo.size);

mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);

}

}

mEncorder.releaseOutputBuffer(bufferIndex, false);

if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {

break;

}

}

}

Log.e("HandDrawActivity", "已经结束录屏");

}

private void releaseEncorders(int i) {

if (mediaProjection != null) {

mediaProjection.stop();

}

mBufferInfo = null;

if (mEncorder != null) {

mEncorder.stop();

}

mInputSurface = null;

if (mMuxer != null && i == 1) {

mMuxer.stop();

}

if (mediaRecorder != null) {

mediaRecorder.stop();

mediaRecorder.reset();

mediaRecorder.release();

}

}

private boolean joinVideo(List filePaths, String resultPath) {

Log.e("HandDrawActivity", "准备合成中");

boolean result = false;

if (filePaths == null || filePaths.size() <= 0 || TextUtils.isEmpty(resultPath)) {

throw new IllegalArgumentException();

}

if (filePaths.size() == 1) { // 只有一个视频片段,不需要合并

return true;

}

try {

Movie[] inMovies = new Movie[filePaths.size()];

for (int i = 0; i < filePaths.size(); i++) {

Log.e("HandDrawActivity", "filePaths=" + filePaths.get(i));

File f = new File(filePaths.get(i));

if (f.exists()) {

inMovies[i] = MovieCreator.build(filePaths.get(i));

}

}

// 分别取出音轨和视频

List videoTracks = new LinkedList<>();

List audioTracks = new LinkedList<>();

for (Movie m : inMovies) {

for (Track t : m.getTracks()) {

if (t.getHandler().equals("soun")) {

audioTracks.add(t);

}

if (t.getHandler().equals("vide")) {

videoTracks.add(t);

}

}

}

// 合并到最终的视频文件

Movie outMovie = new Movie();

if (audioTracks.size() > 0) {

outMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));

}

if (videoTracks.size() > 0) {

outMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));

}

Container mp4file = new DefaultMp4Builder().build(outMovie);

// 将文件输出

File resultFile = new File(resultPath, "APlanTeacherAnswer.mp4");

if (resultFile.exists() && resultFile.isFile()) {

resultFile.delete();

}

FileChannel fc = new RandomAccessFile(resultFile, "rw").getChannel();

mp4file.writeContainer(fc);

fc.close();

Log.e("HandDrawActivity", "合成完毕");

// 合成完成后把原片段文件删除

for (String filePath : filePaths) {

File file = new File(filePath);

file.delete();

}

result = true;

HandDrawActivity.sendVideo();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (Exception e) {

e.printStackTrace();

}

return result;

}

}

6从startRecorder方法说起

public void startRecorder() {

prepareRecorder();//录视频前的准备

startLuYin();//直接录音频(不用准备)

startRecording();//录视频

}

录音的方法

private void startLuYin() {

File file = new File(path, "APlanyinpin.amr");

mediaRecorder = new MediaRecorder();

mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//声音来源,麦克

mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//音频格式,默认,其实就是上面定义好的amr了,除此之外还有mp4

mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//编码格式,问题是我不知道编码格式对什么有影响,是音质高低还是文件大小还是解析快慢,等我有时间去专门研究一下

mediaRecorder.setOutputFile(file.getAbsolutePath());

try {

mediaRecorder.prepare();

mediaRecorder.start();

Log.e("HandDrawActivity", "已经开始录音");

} catch (IOException e) {

e.printStackTrace();

}

}

//录视频前的准备工作

private void prepareRecorder() {

mBufferInfo = new MediaCodec.BufferInfo(); //元数据,描述bytebuffer的数据,尺寸,偏移

//创建格式化对象 MIMI_TYPE 传入的 video/avc 是H264编码格式

MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);

int frameRate = 45;

format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);

format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);

format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);

format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);

format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);

format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);

format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);//编码器的设置,具体是设置的啥我也不太清楚,但是网上查一查都是这么写的!!!

try {

mEncorder = MediaCodec.createEncoderByType("video/avc");

mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

mInputSurface = mEncorder.createInputSurface();

mEncorder.start();//让编码器先跑起来

} catch (IOException e) {

e.printStackTrace();

releaseEncorders(0);

}

}

这里也是准备工作

private void startRecording() {

File saveFile = new File(path, "APlanshipin.mp4");

try {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);//百度一下MediaMuxer,讲的很详细的

mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,

mInputSurface, null, null);

drainEncoder();

}

} catch (Exception e) {

e.printStackTrace();

}

}

这个就是开始写视频文件了

private void drainEncoder() {

while (!isQuit) {

Log.e("TAG", "drain.....");

int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);

if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {

mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());

if (!mMuxerStarted && mTrackIndex >= 0) {

mMuxer.start();

mMuxerStarted = true;

Log.e("HandDrawActivity", "已经开始录屏");

}

}

if (bufferIndex >= 0) {

Log.e("TAG", "drain...write..");

ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);

if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {

mBufferInfo.size = 0;

}

if (mBufferInfo.size != 0) {

if (mMuxerStarted) {

bufferData.position(mBufferInfo.offset);

bufferData.limit(mBufferInfo.offset + mBufferInfo.size);

mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);

}

}

mEncorder.releaseOutputBuffer(bufferIndex, false);

if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {

break;

}

}

}

Log.e("HandDrawActivity", "已经结束录屏");

}

这个就是把录好的音频和视频合并成mp4的方法了,也是点击停止录屏的时候用到的

private boolean joinVideo(List filePaths, String resultPath) {

Log.e("HandDrawActivity", "准备合成中");

boolean result = false;

if (filePaths == null || filePaths.size() <= 0 || TextUtils.isEmpty(resultPath)) {

throw new IllegalArgumentException();

}

if (filePaths.size() == 1) { // 只有一个视频片段,不需要合并

return true;

}

try {

Movie[] inMovies = new Movie[filePaths.size()];

for (int i = 0; i < filePaths.size(); i++) {

Log.e("HandDrawActivity", "filePaths=" + filePaths.get(i));

File f = new File(filePaths.get(i));

if (f.exists()) {

inMovies[i] = MovieCreator.build(filePaths.get(i));

}

}

// 分别取出音轨和视频

List videoTracks = new LinkedList<>();

List audioTracks = new LinkedList<>();

for (Movie m : inMovies) {

for (Track t : m.getTracks()) {

if (t.getHandler().equals("soun")) {

audioTracks.add(t);

}

if (t.getHandler().equals("vide")) {

videoTracks.add(t);

}

}

}

// 合并到最终的视频文件

Movie outMovie = new Movie();

if (audioTracks.size() > 0) {

outMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));

}

if (videoTracks.size() > 0) {

outMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));

}

Container mp4file = new DefaultMp4Builder().build(outMovie);

// 将文件输出

File resultFile = new File(resultPath, "APlanTeacherAnswer.mp4");

if (resultFile.exists() && resultFile.isFile()) {

resultFile.delete();

}

FileChannel fc = new RandomAccessFile(resultFile, "rw").getChannel();

mp4file.writeContainer(fc);

fc.close();

Log.e("HandDrawActivity", "合成完毕");

// 合成完成后把原片段文件删除

for (String filePath : filePaths) {

File file = new File(filePath);

file.delete();

}

result = true;

HandDrawActivity.sendVideo();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (Exception e) {

e.printStackTrace();

}

return result;

}

这个就是结束的时候了,该清空的清空,该注销的注销, i是用来判断录没录的,有可能刚进入这个页面都没录过,直接就返回到别的页面了,那就有可能空指针异常,因为有些变量都没初始化,所以用i判断一下,也可以自己写别的方法判端

private void releaseEncorders(int i) {

if (mediaProjection != null) {

mediaProjection.stop();

}

mBufferInfo = null;

if (mEncorder != null) {

mEncorder.stop();

}

mInputSurface = null;

if (mMuxer != null && i == 1) {

mMuxer.stop();

}

if (mediaRecorder != null) {

mediaRecorder.stop();

mediaRecorder.reset();

mediaRecorder.release();

}

}

7部分代码也是我从网上扒的,但是网上的代码就没怎么见过比较完整的版本的,我上面写的都是经过我自己测试绝对没问题的而且代码也没什么遗漏的,要是发现有遗漏的代码我后续再补上。

java实现录屏_Android5.0以上版本录屏实现代码(完整代码)相关推荐

  1. Java网络编程 Socket、ServerSocket 详解,方法介绍及完整代码示例

    Java网络编程 Socket.ServerSocket 详解,方法介绍及完整代码示例 概念 什么是网络编程? 网络编程是指编写运行在多个设备(计算机)的程序,这些设备通过网络连接起来.当这些通过网络 ...

  2. java 强制安卓竖屏,Android4.0强制横屏竖屏

    Android4.0强制横屏竖屏 方法一: android4.0/frameworks/base/services/Java/com/android/server/wm/WindowManagerSe ...

  3. java实现红包要多少钱_Java实现抢红包算法,附完整代码(公平版和手速版)

    当我们在群里抢红包时真的是手速越快红包金额越大吗? 答案当然是并不是,都说了是拼手气,岂能是拼手速! 不过也可以有拼手速的方法 二倍均值法(公平版) 这是一种很合理很公平的抢红包算法了,绝对不会让你拼 ...

  4. java微信支付 [统一下单接口] 与 [订单查询接口] 调用成功完整代码与结果

    公司最近要搞微信支付, 之前也没有做过, 但是搞过阿里云, 想来也不是很难. 在网上找了很多贴子, 在eclipse里做了5个测试工程, 没有测试成功, 后来下了微信SDK, 也做了个测试样例, 期间 ...

  5. Java开源的规则引擎 Drools 电商行业实战(含完整代码)

    前言 我所在项目组刚好接到一个领取优惠券需求,具体需求是用户领取的各种类型的优惠券(比如:代金券.折扣券)数量不能超过某个自定义数量,因考虑到领取限制数量是动态的,另外考虑到扩展性,满足将来业务规则的 ...

  6. Python 拼图成心2.0【重新梳理】【附完整代码】

    最新版本,点这里 之前一时兴起,写了个生成心形贴图的程序[链接在这里]但是只是把功能实现了,有很多鸡肋还有逻辑之类混乱的,于是又重新整理了一下. 需要用到的库 import os import os. ...

  7. 2020-10-19。小白自学:飞机大战源码改造。(新手入门,学习总结。从零开始,步步为营。不同版本,独立运行。完整代码,感谢原著。)

    #简介: #小白自学:飞机大战源码改造.(新手入门,学习总结.) #从零开始:完全重写代码框架.(步步为营,完整代码.) #简单就好:删除各种自定函数.(讨厌函数跳转,喜欢层次分明.) #文本归一:方 ...

  8. 基于 java Spring Boot 框架的人事管理系统的设计与实现 附完整代码+论文+ppt

    内容摘要: 人事管理在政府机关和企事业单位中占有极其重要的地位,各部门岗位之间协作和工效的提高,是人事管理的基本要求.本设计旨在实现微型人事管理信息系统,功能主要由人事在线通信.员工资料.人事信息管理 ...

  9. drm android 截屏,DRM-X4.0新增防盗录功能Windows智能防录屏,全面防止截屏和屏幕录制...

    据国家版权局网络版权产业研究基地研究调查显示,2018年中国网络版权产业规模达7423亿元,相比2017年6364.5亿元增长了16.6%,成为中国经济增长的新动能.随着互联网技术的急速发展,尤其是在 ...

最新文章

  1. Banner图的播放
  2. wifi boombox android,android filament入门,GLB和GLTF模型查看器
  3. Io 异常: The Network Adapter could not establish the connection(转)
  4. C++ 用vector创建数组对象
  5. SSE图像算法优化系列十九:一种局部Gamma校正对比度增强算法及其SSE优化。
  6. prefuse学习(二)显示一张图
  7. python websocket爬虫_详解python websocket获取实时数据的几种常见链接方式
  8. 本地环境用eclipse搭建spring源码环境
  9. 重用生成的JAXB类
  10. jquery获取iframe里的js事件
  11. 比特币交易信息 广播 服务器,《比特币白皮书》区块圣经(3)时间戳服务器(Timestamp serv...
  12. [转]一个计算机专业学生几年的编程经验汇总
  13. 爱奇艺、芒果TV相继涨价,长视频开启新一轮“加价不加量”游戏?
  14. gm220s路由器怎么设置_中国移动GM220-S光猫修改为桥接模式并启用IPV6
  15. asp big5 gb2312 转 Unicode
  16. 热门小说排行榜(JSP实现)
  17. 单片机无线通信学习笔记
  18. 很抱歉,三维地图当前不能在你的国家/地区使用 Excel绘制三维地图问题解决
  19. DBSCAN 对点云障碍物聚类
  20. 意想不到,这个神奇的bug让我加班到深夜

热门文章

  1. 应对影子IT MobileIron推内容安全服务
  2. “hellow world”哥 (2011-8-12 14:55)
  3. 2020从头到尾不变初心
  4. 楼教主男人八题,告一段落
  5. 软件测试与质量 之白盒测试
  6. EI从投稿到录用一般得多久?
  7. 竞赛经验分享 NUIST CEEE慕课计划【1】—— 全国大学生集成电路创新创业大赛
  8. 五,华为路由器单臂路由配置子接口dot1.q
  9. Java爬虫新浪微博的帖子
  10. C++实现线性顺序表的初始化,插入,删除,销毁,清空等功能