Android提供了两个API用于录音的实现:MediaRecorder 和 AudioRecord,各有优劣。

1、MediaRecorder

已经集成了录音、编码、压缩等,支持少量的录音音频格式,大概有.aac(API = 16) .amr .3gp

优点:大部分已经集成,直接调用相关接口即可,代码量小

缺点:无法实时处理音频;输出的音频格式不是很多,例如没有输出mp3格式文件

2、AudioRecord

主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的实时处理(如会说话的汤姆猫、语音)

优点:语音的实时处理,可以用代码实现各种音频的封装

缺点:输出是PCM语音数据,如果保存成音频文件,是不能够被播放器播放的,所以必须先写代码实现数据编码以及压缩

这次我们说说 AudioRecord,由于MediaRecorder 不能实现暂停和继续,在网上看了N多博客等资料,使用了AudioRecord来实现。

大部分代码来自 参考资料:http://blog.csdn.net/imhxl/article/details/52190451

小demo:

AudioRecorder:封装了录音的方法:创建录音对象、开始、暂停、停止、取消,使用静态枚举类Status来记录录音的状态。

FileUtils:文件工具类,用于文件路径的获取

PcmToWav:封装了将.pcm文件转化.wav文件的方法

WaveHeader: wav文件头

RecordStreamListener:监听录音音频流,用于拓展业务的处理

①用于管理录音的工具类

import android.content.Context;

import android.media.AudioFormat;

import android.media.AudioRecord;

import android.media.MediaRecorder;

import android.text.TextUtils;

import android.text.format.DateFormat;

import android.util.Log;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.util.ArrayList;

import java.util.Calendar;

import java.util.List;

import java.util.Locale;

/**

* Created by HXL on 16/8/11.

* 用于实现录音 暂停录音

*/

public class AudioRecorder {

private static AudioRecorder audioRecorder;

//音频输入-麦克风

private final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;

//采用频率

//44100是目前的标准,但是某些设备仍然支持22050,16000,11025

//采样频率一般共分为22.05KHz、44.1KHz、48KHz三个等级

private final static int AUDIO_SAMPLE_RATE = 16000;

//声道 单声道

private final static int AUDIO_CHANNEL = AudioFormat.CHANNEL_IN_MONO;

//编码

private final static int AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

// 缓冲区字节大小

private int bufferSizeInBytes = 0;

//录音对象

private AudioRecord audioRecord;

//录音状态

private Status status = Status.STATUS_NO_READY;

//文件名

private String fileName;

//录音文件

private List filesName = new ArrayList<>();

private AudioRecorder() {

}

//单例模式

public static AudioRecorder getInstance() {

if (audioRecorder == null) {

audioRecorder = new AudioRecorder();

}

return audioRecorder;

}

/**

* 创建录音对象

*/

public void createAudio(String fileName, int audioSource, int sampleRateInHz, int channelConfig, int audioFormat) {

// 获得缓冲区字节大小

bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz,

channelConfig, audioFormat);

audioRecord = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);

this.fileName = fileName;

}

/**

* 创建默认的录音对象

* @param fileName 文件名

*/

public void createDefaultAudio(String fileName) {

mContext = ctx;

mHandler = handler;

// 获得缓冲区字节大小

bufferSizeInBytes = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE,

AUDIO_CHANNEL, AUDIO_ENCODING);

audioRecord = new AudioRecord(AUDIO_INPUT, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING, bufferSizeInBytes);

this.fileName = fileName;

status = Status.STATUS_READY;

}

/**

* 开始录音

* @param listener 音频流的监听

*/

public void startRecord(final RecordStreamListener listener) {

if (status == Status.STATUS_NO_READY || TextUtils.isEmpty(fileName)) {

throw new IllegalStateException("录音尚未初始化,请检查是否禁止了录音权限~");

}

if (status == Status.STATUS_START) {

throw new IllegalStateException("正在录音");

}

Log.d("AudioRecorder","===startRecord==="+audioRecord.getState());

audioRecord.startRecording();

new Thread(new Runnable() {

@Override

public void run() {

writeDataTOFile(listener);

}

}).start();

}

/**

* 暂停录音

*/

public void pauseRecord() {

Log.d("AudioRecorder","===pauseRecord===");

if (status != Status.STATUS_START) {

throw new IllegalStateException("没有在录音");

} else {

audioRecord.stop();

status = Status.STATUS_PAUSE;

}

}

/**

* 停止录音

*/

public void stopRecord() {

Log.d("AudioRecorder","===stopRecord===");

if (status == Status.STATUS_NO_READY || status == Status.STATUS_READY) {

throw new IllegalStateException("录音尚未开始");

} else {

audioRecord.stop();

status = Status.STATUS_STOP;

release();

}

}

/**

* 释放资源

*/

public void release() {

Log.d("AudioRecorder","===release===");

//假如有暂停录音

try {

if (filesName.size() > 0) {

List filePaths = new ArrayList<>();

for (String fileName : filesName) {

filePaths.add(FileUtils.getPcmFileAbsolutePath(fileName));

}

//清除

filesName.clear();

//将多个pcm文件转化为wav文件

mergePCMFilesToWAVFile(filePaths);

} else {

//这里由于只要录音过filesName.size都会大于0,没录音时fileName为null

//会报空指针 NullPointerException

// 将单个pcm文件转化为wav文件

//Log.d("AudioRecorder", "=====makePCMFileToWAVFile======");

//makePCMFileToWAVFile();

}

} catch (IllegalStateException e) {

throw new IllegalStateException(e.getMessage());

}

if (audioRecord != null) {

audioRecord.release();

audioRecord = null;

}

status = Status.STATUS_NO_READY;

}

/**

* 取消录音

*/

public void canel() {

filesName.clear();

fileName = null;

if (audioRecord != null) {

audioRecord.release();

audioRecord = null;

}

status = Status.STATUS_NO_READY;

}

/**

* 将音频信息写入文件

* @param listener 音频流的监听

*/

private void writeDataTOFile(RecordStreamListener listener) {

// new一个byte数组用来存一些字节数据,大小为缓冲区大小

byte[] audiodata = new byte[bufferSizeInBytes];

FileOutputStream fos = null;

int readsize = 0;

try {

String currentFileName = fileName;

if (status == Status.STATUS_PAUSE) {

//假如是暂停录音 将文件名后面加个数字,防止重名文件内容被覆盖

currentFileName += filesName.size();

}

filesName.add(currentFileName);

File file = new File(FileUtils.getPcmFileAbsolutePath(currentFileName));

if (file.exists()) {

file.delete();

}

fos = new FileOutputStream(file);// 建立一个可存取字节的文件

} catch (IllegalStateException e) {

Log.e("AudioRecorder", e.getMessage());

throw new IllegalStateException(e.getMessage());

} catch (FileNotFoundException e) {

Log.e("AudioRecorder", e.getMessage());

}

//将录音状态设置成正在录音状态

status = Status.STATUS_START;

while (status == Status.STATUS_START) {

readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);

if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos != null) {

try {

fos.write(audiodata);

if (listener != null) {

//用于拓展业务

listener.recordOfByte(audiodata, 0, audiodata.length);

}

} catch (IOException e) {

Log.e("AudioRecorder", e.getMessage());

}

}

}

try {

if (fos != null) {

fos.close();// 关闭写入流

}

} catch (IOException e) {

Log.e("AudioRecorder", e.getMessage());

}

}

/**

* 将pcm合并成wav

* @param filePaths

*/

private void mergePCMFilesToWAVFile(final List filePaths) {

new Thread(new Runnable() {

@Override

public void run() {

if (PcmToWav.mergePCMFilesToWAVFile(filePaths, FileUtils.getWavFileAbsolutePath(fileName))) {

//操作成功

LogUtil.d("录音合成成功");

wavToM4a();

} else {

//操作失败

Log.e("AudioRecorder", "mergePCMFilesToWAVFile fail");

throw new IllegalStateException("mergePCMFilesToWAVFile fail");

}

fileName = null;

}

}).start();

}

/**

* 将单个pcm文件转化为wav文件

*/

private void makePCMFileToWAVFile() {

new Thread(new Runnable() {

@Override

public void run() {

if (PcmToWav.makePCMFileToWAVFile(FileUtils.getPcmFileAbsolutePath(fileName), FileUtils.getWavFileAbsolutePath(fileName), true)) {

//操作成功

} else {

//操作失败

Log.e("AudioRecorder", "makePCMFileToWAVFile fail");

throw new IllegalStateException("makePCMFileToWAVFile fail");

}

fileName = null;

}

}).start();

}

/**

* 获取录音对象的状态

* @return

*/

public Status getStatus() {

return status;

}

/**

* 获取本次录音文件的个数

* @return

*/

public int getPcmFilesCount() {

return filesName.size();

}

/**

* 录音对象的状态

*/

public enum Status {

//未开始

STATUS_NO_READY,

//预备

STATUS_READY,

//录音

STATUS_START,

//暂停

STATUS_PAUSE,

//停止

STATUS_STOP

}

}

② PcmToWAV 将PCM合并转码为WAV格式音频文件

import android.util.Log;

import java.io.BufferedInputStream;

import java.io.BufferedOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.List;

/**

* Created by HXL on 16/8/11.

* 将pcm文件转化为wav文件

*/

public class PcmToWav {

/**

* 合并多个pcm文件为一个wav文件

*

* @param filePathList pcm文件路径集合

* @param destinationPath 目标wav文件路径

* @return true|false

*/

public static boolean mergePCMFilesToWAVFile(List filePathList,

String destinationPath) {

File[] file = new File[filePathList.size()];

byte buffer[] = null;

int TOTAL_SIZE = 0;

int fileNum = filePathList.size();

for (int i = 0; i < fileNum; i++) {

file[i] = new File(filePathList.get(i));

TOTAL_SIZE += file[i].length();

}

// 填入参数,比特率等等。这里用的是16位单声道 8000 hz

WaveHeader header = new WaveHeader();

// 长度字段 = 内容的大小(TOTAL_SIZE) +

// 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)

header.fileLength = TOTAL_SIZE + (44 - 8);

header.FmtHdrLeth = 16;

header.BitsPerSample = 16;

header.Channels = 2;

header.FormatTag = 0x0001;

header.SamplesPerSec = 8000;

header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);

header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;

header.DataHdrLeth = TOTAL_SIZE;

byte[] h = null;

try {

h = header.getHeader();

} catch (IOException e1) {

Log.e("PcmToWav", e1.getMessage());

return false;

}

if (h.length != 44) // WAV标准,头部应该是44字节,如果不是44个字节则不进行转换文件

return false;

//先删除目标文件

File destfile = new File(destinationPath);

if (destfile.exists())

destfile.delete();

//合成所有的pcm文件的数据,写到目标文件

try {

buffer = new byte[1024 * 4]; // Length of All Files, Total Size

InputStream inStream = null;

OutputStream ouStream = null;

ouStream = new BufferedOutputStream(new FileOutputStream(

destinationPath));

ouStream.write(h, 0, h.length);

for (int j = 0; j < fileNum; j++) {

inStream = new BufferedInputStream(new FileInputStream(file[j]));

int size = inStream.read(buffer);

while (size != -1) {

ouStream.write(buffer);

size = inStream.read(buffer);

}

inStream.close();

}

ouStream.close();

} catch (FileNotFoundException e) {

Log.e("PcmToWav", e.getMessage());

return false;

} catch (IOException ioe) {

Log.e("PcmToWav", ioe.getMessage());

return false;

}

clearFiles(filePathList);

Log.i("PcmToWav", "mergePCMFilesToWAVFile success!" + new SimpleDateFormat("yyyy-MM-dd hh:mm").format(new Date()));

return true;

}

/**

* 将一个pcm文件转化为wav文件

* @param pcmPath pcm文件路径

* @param destinationPath 目标文件路径(wav)

* @param deletePcmFile 是否删除源文件

* @return

*/

public static boolean makePCMFileToWAVFile(String pcmPath, String destinationPath, boolean deletePcmFile) {

byte buffer[] = null;

int TOTAL_SIZE = 0;

File file = new File(pcmPath);

if (!file.exists()) {

return false;

}

TOTAL_SIZE = (int) file.length();

// 填入参数,比特率等等。这里用的是16位单声道 8000 hz

WaveHeader header = new WaveHeader();

// 长度字段 = 内容的大小(TOTAL_SIZE) +

// 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)

header.fileLength = TOTAL_SIZE + (44 - 8);

header.FmtHdrLeth = 16;

header.BitsPerSample = 16;

header.Channels = 2;

header.FormatTag = 0x0001;

header.SamplesPerSec = 8000;

header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);

header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;

header.DataHdrLeth = TOTAL_SIZE;

byte[] h = null;

try {

h = header.getHeader();

} catch (IOException e1) {

Log.e("PcmToWav", e1.getMessage());

return false;

}

if (h.length != 44) // WAV标准,头部应该是44字节,如果不是44个字节则不进行转换文件

return false;

//先删除目标文件

File destfile = new File(destinationPath);

if (destfile.exists())

destfile.delete();

//合成所有的pcm文件的数据,写到目标文件

try {

buffer = new byte[1024 * 4]; // Length of All Files, Total Size

InputStream inStream = null;

OutputStream ouStream = null;

ouStream = new BufferedOutputStream(new FileOutputStream(

destinationPath));

ouStream.write(h, 0, h.length);

inStream = new BufferedInputStream(new FileInputStream(file));

int size = inStream.read(buffer);

while (size != -1) {

ouStream.write(buffer);

size = inStream.read(buffer);

}

inStream.close();

ouStream.close();

} catch (FileNotFoundException e) {

Log.e("PcmToWav", e.getMessage());

return false;

} catch (IOException ioe) {

Log.e("PcmToWav", ioe.getMessage());

return false;

}

if (deletePcmFile) {

file.delete();

}

Log.i("PcmToWav", "makePCMFileToWAVFile success!" + new SimpleDateFormat("yyyy-MM-dd hh:mm").format(new Date()));

return true;

}

/**

* 清除文件

* @param filePathList

*/

private static void clearFiles(List filePathList) {

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

File file = new File(filePathList.get(i));

if (file.exists()) {

file.delete();

}

}

}

}

③WaveHeader类

import java.io.ByteArrayOutputStream;

import java.io.IOException;

/**

* Created by HXL on 16/3/9.

* wav文件头

*/

public class WaveHeader {

public final char fileID[] = {'R', 'I', 'F', 'F'};

public int fileLength;

public char wavTag[] = {'W', 'A', 'V', 'E'};;

public char FmtHdrID[] = {'f', 'm', 't', ' '};

public int FmtHdrLeth;

public short FormatTag;

public short Channels;

public int SamplesPerSec;

public int AvgBytesPerSec;

public short BlockAlign;

public short BitsPerSample;

public char DataHdrID[] = {'d','a','t','a'};

public int DataHdrLeth;

public byte[] getHeader() throws IOException {

ByteArrayOutputStream bos = new ByteArrayOutputStream();

WriteChar(bos, fileID);

WriteInt(bos, fileLength);

WriteChar(bos, wavTag);

WriteChar(bos, FmtHdrID);

WriteInt(bos,FmtHdrLeth);

WriteShort(bos,FormatTag);

WriteShort(bos,Channels);

WriteInt(bos,SamplesPerSec);

WriteInt(bos,AvgBytesPerSec);

WriteShort(bos,BlockAlign);

WriteShort(bos,BitsPerSample);

WriteChar(bos,DataHdrID);

WriteInt(bos,DataHdrLeth);

bos.flush();

byte[] r = bos.toByteArray();

bos.close();

return r;

}

private void WriteShort(ByteArrayOutputStream bos, int s) throws IOException {

byte[] mybyte = new byte[2];

mybyte[1] =(byte)( (s << 16) >> 24 );

mybyte[0] =(byte)( (s << 24) >> 24 );

bos.write(mybyte);

}

private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException {

byte[] buf = new byte[4];

buf[3] =(byte)( n >> 24 );

buf[2] =(byte)( (n << 8) >> 24 );

buf[1] =(byte)( (n << 16) >> 24 );

buf[0] =(byte)( (n << 24) >> 24 );

bos.write(buf);

}

private void WriteChar(ByteArrayOutputStream bos, char[] id) {

for (int i=0; i

char c = id[i];

bos.write(c);

}

}

}

④RecordStreamListener 扩展接口

/**

* Created by HXL on 16/8/11.

* 获取录音的音频流,用于拓展的处理

*/

public interface RecordStreamListener {

void recordOfByte(byte[] data, int begin, int end);

}

⑤AudioFileUtils工具类

import android.os.Environment;

import android.text.TextUtils;

import java.io.File;

import java.util.ArrayList;

import java.util.List;

/**

* Created by HXL on 16/8/11.

* 管理录音文件的类

*/

public class AudioFileUtils {

private static String rootPath="audiorecord";

//原始文件(不能播放)

private final static String AUDIO_PCM_BASEPATH = "/"+rootPath+"/pcm/";

//可播放的高质量音频文件

private final static String AUDIO_WAV_BASEPATH = "/"+rootPath+"/wav/";

private static void setRootPath(String rootPath){

FileUtils.rootPath=rootPath;

}

public static String getPcmFileAbsolutePath(String fileName){

if(TextUtils.isEmpty(fileName)){

throw new NullPointerException("fileName isEmpty");

}

if(!isSdcardExit()){

throw new IllegalStateException("sd card no found");

}

String mAudioRawPath = "";

if (isSdcardExit()) {

if (!fileName.endsWith(".pcm")) {

fileName = fileName + ".pcm";

}

String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + AUDIO_PCM_BASEPATH;

File file = new File(fileBasePath);

//创建目录

if (!file.exists()) {

file.mkdirs();

}

mAudioRawPath = fileBasePath + fileName;

}

return mAudioRawPath;

}

public static String getWavFileAbsolutePath(String fileName) {

if(fileName==null){

throw new NullPointerException("fileName can't be null");

}

if(!isSdcardExit()){

throw new IllegalStateException("sd card no found");

}

String mAudioWavPath = "";

if (isSdcardExit()) {

if (!fileName.endsWith(".wav")) {

fileName = fileName + ".wav";

}

String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + AUDIO_WAV_BASEPATH;

File file = new File(fileBasePath);

//创建目录

if (!file.exists()) {

file.mkdirs();

}

mAudioWavPath = fileBasePath + fileName;

}

return mAudioWavPath;

}

/**

* 判断是否有外部存储设备sdcard

* @return true | false

*/

public static boolean isSdcardExit() {

if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))

return true;

else

return false;

}

/**

* 获取全部pcm文件列表

* @return

*/

public static List getPcmFiles() {

List list = new ArrayList<>();

String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + AUDIO_PCM_BASEPATH;

File rootFile = new File(fileBasePath);

if (!rootFile.exists()) {

} else {

File[] files = rootFile.listFiles();

for (File file : files) {

list.add(file);

}

}

return list;

}

/**

* 获取全部wav文件列表

* @return

*/

public static List getWavFiles() {

List list = new ArrayList<>();

String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath() + AUDIO_WAV_BASEPATH;

File rootFile = new File(fileBasePath);

if (!rootFile.exists()) {

} else {

File[] files = rootFile.listFiles();

for (File file : files) {

list.add(file);

}

}

return list;

}

}

⑥在Activity中使用就简单了

btnStart.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

status = Status.AUDIO_RECORDING;

btnStart.setEnabled(false);

btnPause.setEnabled(true);

btnStop.setEnabled(true);

String fileName = "temp";

audioRecorder.createDefaultAudio(Record3Activity.this,fileName);

audioRecorder.startRecord(null);

timeThread = new Thread(new Runnable() {

@Override

public void run() {

// 记录录音时长并显示

countTime();

}

});

timeThread.start();

}

});

btnPause.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

btnStart.setEnabled(false);

btnPause.setEnabled(true);

btnStop.setEnabled(true);

if (status == Status.AUDIO_RECORDING) {

status = Status.AUDIO_PUASE;

audioRecorder.pauseRecord();

btnPause.setText("继续");

} else if (status == Status.AUDIO_PUASE) {

status = Status.AUDIO_RECORDING;

audioRecorder.startRecord(null);

btnPause.setText("暂停");

}

}

});

btnStop.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

status = Status.AUDIO_STOP;

resetButton();

audioRecorder.stopRecord();

}

});

Status 为自定义的枚举 表示录音状态:正在录音、暂停录音、停止录音。

总结:为了暂停功能踩了N多坑,google大大们也不弄个暂停的 api...

再次感谢:http://blog.csdn.net/imhxl/article/details/52190451 博主的无私分享。

android中录音断点播放,Android实现暂停--继续录音(AudioRecord)相关推荐

  1. android中录音断点播放,Android实现语音播放与录音功能

    本文实例为大家分享了Android实现语音播放与录音的具体代码,供大家参考,具体内容如下 项目用到的技术点和亮点 语音录音 (单个和列表) 语音播放(单个和列表) 语音录音封装 语音播放器封装 语音列 ...

  2. Android中http断点下载,Android HttpURLConnection断点下载(单线程)

    HttpCilent 跟 HttpURLConnection 是安卓原生的用来实现http请求的类: Android 6.0之后取消了HttpClient,不支持跟新 ,今天小编使用的是HttpURL ...

  3. 在Android中调用图片、视频、音频、录音、拍照

    在Android中调用图片.视频.音频.录音.拍照 //选择图片 requestCode 返回的标识 Intent innerIntent = new Intent(Intent.ACTION_GET ...

  4. Android开发之PCM录音实时播放的实现方法 | 边录音边播放 |PCM录音播放无延迟 | 录音无杂音 | 录音无噪音

    先说下录音得开启录音权限 <uses-permission android:name="android.permission.RECORD_AUDIO" /> 然后录音 ...

  5. android中edittext设置密码格式,Android 自定义EditText(带清理、密码可见、不可见)...

    实际开发中经常会遇到输入框,各种不同的需求,在一般情况下,清除内容,以及密码可见与不可见基本很常见,那么不废话直接上代码! 在res\values\attrs.xml中 .java import an ...

  6. android 视频格式不能播放,Android引入IjkPlayer无法播放mkv格式视频的解决方案

    写在前面 项目中直接引用或者直接编译源码得到的ijkplayer在播放mkv文件时出现(-10000)的错误,去项目github查看了才知道,默认是不支持mkv和rmvb格式视频的播放的. 用了一天时 ...

  7. android中的progressbar,解析android中ProgressBar的用法

    范例说明Android的Widget,有许多是为了与User交互而特别设计的,但也有部分是作为程序提示.显示程序运行状态的Widget.现在介绍的范例,与前一章介绍过的ProgressDialog对话 ...

  8. android中怎么网络判断,Android中判断网络是否连接实例详解

    Android中判断网络是否连接实例详解 在android中,如何监测网络的状态呢,这个有的时候也是十分重要的,方法如下: public class ConnectionDetector { priv ...

  9. android中自适应布局教程,Android自适应布局设计技巧

    由于目前在做的一款app需要适配手机和平板,所以我在研究怎么构建可适应所有屏幕尺寸的布局方法. 在web的自适应布局上我有很多经验,比如使用网格流,CSS3中的media queries属性等等,这些 ...

最新文章

  1. win下配置cmder
  2. Python 技术篇-python生成html源码功能实现演示,html代码自动生成技巧,列表生成式的灵活应用
  3. 8屏幕滚动_对标iOS?Android 11或无缘屏幕长截图
  4. 计算机专业学习资料总结(~持续更新中)
  5. 办公自动化系统开发实例导航 源码_本色世界源码搭建(系统开发)
  6. route map应用策略路由(下)
  7. 用单链表实现一个队列
  8. web测试常用的用例及知识
  9. C++ 四种智能指针详解
  10. 2017-05-24
  11. 非极大值抑制(non-maximum suppression)的理解与实现
  12. java移动文件导致tomcat死掉_利用平台自带的tomcat作为服务 当上传文件失败后服务总死掉...
  13. 计算机四级网络工程师(备考过程,避开误区,高效备考!)
  14. dell 1520 win7 x64 触摸板驱动 synaptics touchpad 16.2.10.3
  15. (2)JfreeChart之柱状图与折线图复合图
  16. java heap space 什么意思_java heap space是什么意思?
  17. python制作地图
  18. HTML5 第004篇 <abbr>标签【定义缩写】
  19. OpenCV分水岭分割算法2
  20. 静态库与共享库制作,及区别

热门文章

  1. 小程序生成二维码海报
  2. 计算机操作员要求,计算机操作员教学大纲
  3. Casbin-authz-plugin:基于Casbin的Docker权限管理、访问控制插件
  4. opencv2 加载RTSP视频流,内存溢出的问题
  5. 你应该知道的三相电机知识
  6. 10 本 Android PDF 书籍免费分享
  7. 植物大战僵尸用户存档修改(CSDN任务1,20210418)
  8. 泛微OA二次开发E8之UE富文本编辑器增加按钮操作
  9. python体能达标成绩_Python+Excel数据分析实战:军事体能考核成绩评定(九)评定个人等级...
  10. 【ModuleNotFoundError 与 ImportError】之 most likely due to a circular import