接上一篇文章,[Android多媒體一]調用系統相機拍照並存儲到指定位置,適配安卓 7.0
本文講述如何調用系統錄音機,完成錄音後,對錄音進行指定位置的保存。
一、開始編寫
首先,還是理清一下思路,在着手編寫代碼。
1、啓動系統錄音機並保存到指定位置依然設計讀寫權限,此時需要向用戶請求權限,並根據用戶操作進行相應的動作。錄音使用到的權限有:
<!-- 讀寫權限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 錄音權限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
2、將錄音文件保存到SD的指定位置,需要創建一定的目錄層級,像上一篇文章講述的一樣,這次,把錄音文件保存爲SD根目錄下的TestDir/voice/xxx.amr。
3、根據用戶錄音的結果,進行存儲操作。
4、不同於啓動相機拍照,這次,不把uri加入到啓動錄音機的額外數據,因爲不管加還是不加,錄音成功後,獲取到的uri都是系統存放剛剛的錄音文件的uri。
廢話不多說,直接上代碼:
主界面佈局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拍照並保存" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="錄音並保存" />
</LinearLayout>
MainActivity代碼:
package com.my.example.multimediatest;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.StrictMode;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
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.Calendar;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
public static final String SD_APP_DIR_NAME = "TestDir"; //存儲程序在外部SD卡上的根目錄的名字
public static final String PHOTO_DIR_NAME = "photo"; //存儲照片在根目錄下的文件夾名字
public static final String VOICE_DIR_NAME = "voice"; //存儲音頻在根目錄下的文件夾名字
public static final String VIDEO_DIR_NAME = "video"; //存儲視頻在根目錄下的文件夾名字
public static final int PHOTO_RESULT_CODE = 100; //標誌符,圖片的結果碼,判斷是哪一個Intent
public static final int VOICE_RESULT_CODE = 101; //標誌符,音頻的結果碼,判斷是哪一個Intent
public static final int VIDEO_RESULT_CODE = 102; //標誌符,視頻的結果碼,判斷是哪一個Intent
private String mImagePath; //用於存儲圖片的最終目錄,即根目錄 / 圖片的文件夾 / 圖片
private Uri mImageUri; //存儲相機返回的uri
private String mImageName; //保存的圖片的名字
private File mImageFile; //圖片文件
private String mVoicePath; //用於存儲錄音的最終目錄,即根目錄 / 錄音的文件夾 / 錄音
private String mVoiceName; //保存的錄音的名字
private File mVoiceFile; //錄音文件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "開始...");
// android 7.0系統解決拍照的問題
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
builder.detectFileUriExposure();
//拍照按鈕的點擊事件
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]
{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 200);
}
});
//錄音按鈕的點擊事件
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]
{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 201);
}
});
}
/**
* 返回用戶是否允許權限的結果,並處理
*/
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResult) {
if (requestCode == 200) {
//用戶允許權限
if (grantResult[0] == PackageManager.PERMISSION_GRANTED && grantResult[1] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "用戶已允許權限,準備啓動相機。");
//啓動照相機
startCamera();
} else { //用戶拒絕
Log.d(TAG, "用戶已拒絕權限,程序終止。");
Toast.makeText(this, "程序需要足夠權限才能運行", Toast.LENGTH_SHORT).show();
}
}
if (requestCode == 201) {
//用戶允許權限
if (grantResult[0] == PackageManager.PERMISSION_GRANTED && grantResult[1] == PackageManager.PERMISSION_GRANTED) {
//啓動錄音機
startRecord();
} else {
Log.d(TAG, "用戶已拒絕權限,程序終止。");
Toast.makeText(this, "程序需要足夠權限才能運行", Toast.LENGTH_SHORT).show();
}
}
}
/**
* 啓動錄音機,創建文件
*/
private void startRecord() {
Intent intent = new Intent();
intent.setAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
createVoiceFile();
Log.d(TAG, "創建錄音文件");
//添加權限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.d(TAG, "啓動系統錄音機,開始錄音...");
startActivityForResult(intent, VOICE_RESULT_CODE);
}
/**
* 創建音頻目錄
*/
private void createVoiceFile() {
mVoiceName = getMyTime() + ".amr";
Log.d(TAG, "錄音文件名稱:" + mVoiceName);
mVoiceFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/" + SD_APP_DIR_NAME + "/" + VOICE_DIR_NAME + "/", mVoiceName);
mVoicePath = mVoiceFile.getAbsolutePath();
mVoiceFile.getParentFile().mkdirs();
Log.d(TAG, "按設置的目錄層級創建音頻文件,路徑:" + mVoicePath);
mVoiceFile.setWritable(true);
}
/**
* 啓動相機,創建文件,並要求返回uri
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void startCamera() {
Intent intent = new Intent();
//指定動作,啓動相機
intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
Log.d(TAG, "指定啓動相機動作,完成。");
//創建文件
createImageFile();
Log.d(TAG, "創建圖片文件結束。");
//添加權限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.d(TAG, "添加權限。");
//獲取uri
mImageUri = FileProvider.getUriForFile(this, "com.my.example.multimediatest.provider", mImageFile);
Log.d(TAG, "根據圖片文件路徑獲取uri。");
//將uri加入到額外數據
intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
Log.d(TAG, "將uri加入啓動相機的額外數據。");
Log.d(TAG, "啓動相機...");
//啓動相機並要求返回結果
startActivityForResult(intent, PHOTO_RESULT_CODE);
Log.d(TAG, "拍攝中...");
}
/**
* 創建圖片文件
*/
private void createImageFile() {
Log.d(TAG, "開始創建圖片文件...");
//設置圖片文件名(含後綴),以當前時間的毫秒值爲名稱
mImageName = getMyTime() + ".jpg";
Log.d(TAG, "設置圖片文件的名稱爲:" + mImageName);
//創建圖片文件
mImageFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/" + SD_APP_DIR_NAME + "/" + PHOTO_DIR_NAME + "/", mImageName);
//將圖片的絕對路徑設置給mImagePath,後面會用到
mImagePath = mImageFile.getAbsolutePath();
//按設置好的目錄層級創建
mImageFile.getParentFile().mkdirs();
Log.d(TAG, "按設置的目錄層級創建圖片文件,路徑:" + mImagePath);
//不加這句會報Read-only警告。且無法寫入SD
mImageFile.setWritable(true);
Log.d(TAG, "將圖片文件設置可寫。");
}
/**
* 處理返回結果。
* 1、圖片
* 2、音頻
* 3、視頻
*
* @param requestCode 請求碼
* @param resultCode 結果碼 成功 -1 失敗 0
* @param data 返回的數據
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Log.d(TAG, "拍攝結束。");
Log.d(TAG, "錄音結束。");
if (resultCode == Activity.RESULT_OK) {
Log.d(TAG, "返回成功。");
Log.d(TAG, "請求碼:" + requestCode + " 結果碼:" + resultCode + " data:" + data);
switch (requestCode) {
case PHOTO_RESULT_CODE: {
Bitmap bitmap = null;
try {
//根據uri設置bitmap
bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), mImageUri);
Log.d(TAG, "根據uri設置bitmap。");
} catch (IOException e) {
e.printStackTrace();
}
//將圖片保存到SD的指定位置
savePhotoToSD(bitmap);
//更新系統圖庫
updateSystemGallery();
Log.d(TAG, "結束。");
break;
}
case VOICE_RESULT_CODE: {
try {
Uri uri = data.getData();
String filePath = getAudioFilePathFromUri(uri);
Log.d(TAG, "根據uri獲取文件路徑:" + filePath);
Log.d(TAG, "開始保存錄音文件");
saveVoiceToSD(filePath);
} catch (Exception e) {
throw new RuntimeException(e);
}
break;
}
case VIDEO_RESULT_CODE: {
// saveVideoTOSD();
break;
}
}
}
}
/**
* 保存照片到SD卡的指定位置
*/
private void savePhotoToSD(Bitmap bitmap) {
Log.d(TAG, "將圖片保存到指定位置。");
//創建輸出流緩衝區
BufferedOutputStream os = null;
try {
//設置輸出流
os = new BufferedOutputStream(new FileOutputStream(mImageFile));
Log.d(TAG, "設置輸出流。");
//壓縮圖片,100表示不壓縮
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
Log.d(TAG, "保存照片完成。");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
//不管是否出現異常,都要關閉流
os.flush();
os.close();
Log.d(TAG, "刷新、關閉流");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 更新系統圖庫
*/
private void updateSystemGallery() {
//把文件插入到系統圖庫
try {
MediaStore.Images.Media.insertImage(this.getContentResolver(),
mImageFile.getAbsolutePath(), mImageName, null);
Log.d(TAG, "將圖片文件插入系統圖庫。");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 最後通知圖庫更新
this.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + mImagePath)));
Log.d(TAG, "通知系統圖庫更新。");
}
/**
* 獲取日期並格式化
* 如:2017_10_20 週三 上午 11:20:35
*
* @return 格式化好的日期字符串
*/
private String getMyTime() {
//存儲格式化後的時間
String time;
//存儲上午下午
String ampTime = "";
//判斷上午下午,am上午,值爲 0 ; pm下午,值爲 1
int apm = Calendar.getInstance().get(Calendar.AM_PM);
if (apm == 0) {
ampTime = "上午";
} else {
ampTime = "下午";
}
//設置格式化格式
SimpleDateFormat format = new SimpleDateFormat("yyyy_MM_dd E " + ampTime + " kk:mm:ss");
time = format.format(new Date());
return time;
}
/**
* 保存音頻到SD卡的指定位置
*
* @param path 錄音文件的路徑
*/
private void saveVoiceToSD(String path) {
//創建輸入輸出
InputStream isFrom = null;
OutputStream osTo = null;
try {
//設置輸入輸出流
isFrom = new FileInputStream(path);
osTo = new FileOutputStream(mVoicePath);
byte bt[] = new byte[1024];
int len;
while ((len = isFrom.read(bt)) != -1) {
Log.d(TAG, "len = " + len);
osTo.write(bt, 0, len);
}
Log.d(TAG, "保存錄音完成。");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (osTo != null) {
try {
//不管是否出現異常,都要關閉流
osTo.close();
Log.d(TAG, "關閉輸出流");
} catch (IOException e) {
e.printStackTrace();
}
}
if (isFrom != null) {
try {
isFrom.close();
Log.d(TAG, "關閉輸入流");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 保存視頻到SD卡的指定位置
*/
private void saveVideoTOSD() {
}
/**
* 通過Uri,獲取錄音文件的路徑(絕對路徑)
*
* @param uri 錄音文件的uri
* @return 錄音文件的路徑(String)
*/
private String getAudioFilePathFromUri(Uri uri) {
Cursor cursor = getContentResolver()
.query(uri, null, null, null, null);
cursor.moveToFirst();
int index = cursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);
String temp = cursor.getString(index);
cursor.close();
return temp;
}
}
實現思路和拍攝圖片並保存的思路一致,都是在啓動系統程序前檢查有無讀寫權限,有權限則創建對應的目錄層級和對應的文件,操作完成後通過返回的結果進行保存操作。
二、運行結果
還是完整的執行流程。
接下來去系統的文件夾下找到剛剛錄音的文件。
可以看到保存的位置、文件名稱都符合預期。並且可以播放,文件大小由於是amr格式,所以非常小。
實現啓動錄音機錄音,並保存到指定位置還是非常簡單的,代碼量相對於相機來說少了很多。邏輯也更清晰了。
相關文章:[Android多媒體一]調用系統相機拍照並存儲到指定位置,適配安卓 7.0
原創文章,轉載請註明出處:https://blog.csdn.net/Lone1yCode/article/details/79951477