Android之音頻錄製-實例篇

2024-06-04 00:32

本文主要是介绍Android之音頻錄製-實例篇,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

使用AudioRecord&AudioTrack進行錄製音頻文件與播放操作

代碼稍微多了點,儘量一個章節寫完

首先看看介面圖


然後看看項目目錄


之後就是代碼環節了,首先看佈局文件

<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/background_audio"><LinearLayoutandroid:id="@+id/audio_layout"android:layout_width="match_parent"android:layout_height="wrap_content"android:paddingTop="12dp"android:layout_alignParentBottom="true"android:background="@drawable/background_key"><Buttonandroid:id="@+id/audio_record"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="15dp"android:layout_weight="1"android:background="@drawable/btn_record" /><Buttonandroid:id="@+id/audio_pause"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="15dp"android:layout_weight="1"android:background="@drawable/btn_pause" /><Buttonandroid:id="@+id/audio_stop"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:background="@drawable/btn_stop" /><Buttonandroid:id="@+id/play_audio"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:background="@drawable/btn_play" /><Buttonandroid:id="@+id/pause_audio"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:background="@drawable/btn_pause" /><Buttonandroid:id="@+id/audio_delete"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1"android:background="@drawable/btn_delete" /></LinearLayout><TextViewandroid:id="@+id/audio_timer"android:layout_above="@id/audio_layout"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="20dp"android:gravity="center"android:text="00:00:00"android:textColor="@android:color/white"android:textStyle="bold"android:textSize="30sp" /><SeekBarandroid:id="@+id/seekbar_audio"android:layout_above="@id/audio_timer"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="20dp"android:progressDrawable="@drawable/style_seekbar"android:thumb="@drawable/seekbar_play" />
</RelativeLayout>

然後看看AndroidManifest.xml文件中的權限

    <uses-permission android:name="android.permission.RECORD_AUDIO" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

下面,將會給出MainActivity、AudioRecordDecorated、AudioTrackDecorated中的代碼,代碼中都已經給出了註釋信息,在文章中,就不多說了

/*** 使用AudioRecord錄製音頻文件* 使用AudioTrack播放PCM、WAV格式音頻文件* @author Francis-ChinaFeng* @version 1.0 2013-08-31*/
public class MainActivity extends Activity implements OnClickListener {//	聲明控件對象private Button audio_record, audio_pause, audio_stop, play_audio, pause_audio, audio_delete;private TextView audio_timer;private SeekBar seekbar_audio;//	聲明AudioRecord參數
//	設置錄製音頻的硬件設備private int audioSource = AudioSource.MIC;
//	設置音頻採樣率,不同的設備採樣率也不同,如果錄製的音頻無聲、雜音可以修改採樣率的值private int sampleRateInHz = 44100;
//	設置音頻錄製聲道CHANNEL_IN_STEREO表示雙聲道private int channelConfig = AudioFormat.CHANNEL_IN_STEREO;
//	設置錄製音頻的編碼ENCODING_PCM_16BIT差不多可以被全部設備接受private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
//	設置錄製音頻緩衝區private int bufferSizeInBytes;
//	聲明AudioRecord對象,用於錄製音頻文件private AudioRecord audioRecord;
//	聲明AudioTrack對象,用於播放音頻文件private AudioTrack audioTrack;//	聲明一堆程序變量private boolean isCard, isRecord, isPlay;private String sdCard, recordPath, audioPath, recordFormat, format;private int offset;private Timer timer;private int hour, minute, second;private int what = 0x111;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();initView();setListener();}/*** 初始化操作*/private void init() {audio_record = (Button) findViewById(R.id.audio_record);audio_pause = (Button) findViewById(R.id.audio_pause);audio_stop = (Button) findViewById(R.id.audio_stop);play_audio = (Button) findViewById(R.id.play_audio);pause_audio = (Button) findViewById(R.id.pause_audio);audio_delete = (Button) findViewById(R.id.audio_delete);audio_timer = (TextView) findViewById(R.id.audio_timer);seekbar_audio = (SeekBar) findViewById(R.id.seekbar_audio);isCard = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);isRecord = false;isPlay = false;if (isCard) {sdCard = SDCardUtils.getExternalPath();}recordFormat = ".pcm";format = ".wav";}/*** 初始化Activity頁面控件*/private void initView() {audio_pause.setVisibility(View.GONE);pause_audio.setVisibility(View.GONE);seekbar_audio.setVisibility(View.GONE);audio_stop.setEnabled(false);play_audio.setEnabled(false);audio_delete.setEnabled(false);}/*** 綁定按鈕點擊事件*/private void setListener() {audio_record.setOnClickListener(this);audio_pause.setOnClickListener(this);audio_stop.setOnClickListener(this);play_audio.setOnClickListener(this);pause_audio.setOnClickListener(this);audio_delete.setOnClickListener(this);}@Overridepublic void onClick(View v) {if (!isCard) {toast("SDCard is Invalid");return;}switch (v.getId()) {case R.id.audio_record:audioRecord();toast("Audio Record is Start");break;case R.id.audio_pause:audioPause();toast("Audio Record is Pause");break;case R.id.audio_stop:audioStop();toast("Audio Record is Stop");break;case R.id.play_audio:playAudio();toast("Audio Play is Start");break;case R.id.pause_audio:pauseAudio();toast("Audio Play is Pause");break;case R.id.audio_delete:audioDelete();toast("Audio File is Deleted");break;}}//	開始錄製音頻文件private void audioRecord() {if (recordPath == null) {recordPath = sdCard + File.separator + System.currentTimeMillis() + recordFormat;audio_timer.setText("00:00:00");}isRecord = true;bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
//		使用裝飾者模式,簡潔Activity類中的代碼audioRecord = AudioRecordDecorated.play(audioRecord, audioSource,sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);
//		開啟一條線程,用於將音頻數據寫入SDCard中new Thread(new AudioRecordRunnable()).start();
//		開啟計時器setRecordTimer();
//		用於控制按鈕顯示、點擊操作referenceRecordView();}//	暫停錄製音頻文件private void audioPause() {isRecord = false;
//		釋放AudioRecord對象資源,其實不需要接收返回值,此處為保險起見audioRecord = AudioRecordDecorated.release(audioRecord);
//		刷新Activity控件referenceRecordView();}//	停止錄製音頻文件private void audioStop() {audioPause();
//		刷新Activity控件,重置一些數據resetRecordView();
//		開啟一條線程,用於將PCM格式音頻文件轉換成WAV音頻文件,用於在PC端上進行播放new Thread(new RecordConvertRunnable()).start();}//	開始播放private void playAudio() {isPlay = true;bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
//		封裝AudioTrack播放音頻文件的初始操作audioTrack = AudioTrackDecorated.play(audioTrack, sampleRateInHz,channelConfig, audioFormat, bufferSizeInBytes);
//		刷新Activity頁面referenceTrackView();File file = new File(audioPath);if (file.exists() && file.length() > 0) {
//			開啟一條線程,用於播放音頻文件new Thread(new AudioTrackRunnable(file)).start();} else {toast(audioPath + " Is Not Found");}}//	暫停播放音頻文件private void pauseAudio() {isPlay = false;
//		釋放AudioTrack對象資源audioTrack = AudioTrackDecorated.release(audioTrack);referenceTrackView();}//	刪除剛剛錄製的音頻文件private void audioDelete() {if (isPlay) {
//			如果在播放時,刪除文件,需要釋AudioTrack資源,直接調用暫停方法,省事pauseAudio();}File file = new File(audioPath);if (file.exists()) {file.delete();}
//		刷新頁面resetTrackView();}//	刷新頁面,控制控件點擊事件private void referenceRecordView() {if (isRecord) {audio_record.setVisibility(View.GONE);seekbar_audio.setVisibility(View.GONE);audio_pause.setVisibility(View.VISIBLE);audio_timer.setVisibility(View.VISIBLE);audio_stop.setEnabled(isRecord);play_audio.setEnabled(!isRecord);} else {audio_record.setVisibility(View.VISIBLE);audio_pause.setVisibility(View.GONE);if (timer != null) {timer.cancel();timer = null;}}}//	刷新頁面,控制控件點擊事件private void referenceTrackView() {if (isPlay) {play_audio.setVisibility(View.GONE);audio_timer.setVisibility(View.GONE);pause_audio.setVisibility(View.VISIBLE);seekbar_audio.setVisibility(View.VISIBLE);} else {play_audio.setVisibility(View.VISIBLE);pause_audio.setVisibility(View.GONE);}}//	重置頁面,控制頁面控制點擊事件private void resetRecordView() {hour = 0;minute = 0;second = 0;audio_stop.setEnabled(false);play_audio.setEnabled(true);audio_delete.setEnabled(true);audio_record.setVisibility(View.VISIBLE);audio_pause.setVisibility(View.GONE);audioPath = recordPath;recordPath = null;}//	重置頁面,控制頁面控制點擊事件private void resetTrackView() {audio_timer.setText("00:00:00");seekbar_audio.setVisibility(View.GONE);audio_timer.setVisibility(View.VISIBLE);play_audio.setEnabled(false);audio_delete.setEnabled(false);audioPath = null;}//	計時器private void setRecordTimer() {timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {if (isRecord) {second++;if (second >= 60) {second = 0;minute++;if (minute >= 60) {hour++;}}recordTimerHandler.sendEmptyMessage(what);}}}, 1000, 1000);}@Overrideprotected void onStop() {if (isRecord) {audioPause();} else if (isPlay) {pauseAudio();}super.onStop();}@Overrideprotected void onDestroy() {if (isRecord) {audioStop();} else if (isPlay) {pauseAudio();}super.onDestroy();}private void toast(String text) {Log.e("custom", text);Toast.makeText(this, text, Toast.LENGTH_LONG).show();}//	計時器Handler,用於顯示錄製音頻時間private Handler recordTimerHandler = new Handler() {public void handleMessage(Message msg) {if (msg.what == what ) {StringBuilder builder = new StringBuilder();builder.append(hour < 10 ? "0"+ hour : hour);builder.append(":");builder.append(minute < 10 ? "0"+ minute : minute);builder.append(":");builder.append(second < 10 ? "0"+ second : second);audio_timer.setText(builder.toString());}};};//	播放Handler,用於控制播放進度條private Handler audioPlayingHandler = new Handler() {public void handleMessage(Message msg) {if (msg.what == what) {seekbar_audio.setProgress(offset);}};};//	播放完畢Handler,用於調用暫停播放,釋放資源操作private Handler audioPlayOverHandler = new Handler() {public void handleMessage(Message msg) {if (msg.what == what) {seekbar_audio.setProgress(0);pauseAudio();}};};/*** 錄製音頻文件線程*/public class AudioRecordRunnable implements Runnable {@Overridepublic void run() {recordPath = recordFile(recordPath);}}/*** 將PCM格式音頻文件轉換成WAV格式音頻文件線程*/public class RecordConvertRunnable implements Runnable {@Overridepublic void run() {String targetPath = audioPath.replace(recordFormat, format);audioPath = AudioRecordDecorated.convertWAVFile(audioPath,targetPath, sampleRateInHz, bufferSizeInBytes, true);}}/*** 播放WAV格式音頻文件,同樣可以播放PCM格式音頻文件,不過PCM作為臨時文件在播放之前會被刪除*/public class AudioTrackRunnable implements Runnable {private File file;public AudioTrackRunnable(File file) {this.file = file;}@Overridepublic void run() {
//			RandomAccessFile可以很好的獲取文件的某個具體位置,用於多線程下載、音頻文件播放很適用RandomAccessFile in = null;try {
//				聲明只讀RandomAccessFile對象in = new RandomAccessFile(file, "r");
//				設置SeekBar的最大值seekbar_audio.setMax((int) in.getChannel().size());
//				設置文件的讀取位置in.seek(offset);byte[] audioData = new byte[1024];int i;while ((i = in.read(audioData)) != -1 && isPlay) {if (audioTrack != null && isPlay) {
//						記錄音頻文件的播放位置offset += i;audioTrack.write(audioData, 0, i);
//						使用Handler改變SeekBar的位置audioPlayingHandler.sendEmptyMessage(what);}
//					判斷是否播放完畢if (offset >= in.getChannel().size()) {offset = 0;
//						釋放AudioTrack資源的HandleraudioPlayOverHandler.sendEmptyMessage(what);}}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}/*** 將錄製的音頻文件持久化* @param path* @return*/public String recordFile(String path) {File file = new File(path);RandomAccessFile out = null;try {if (!file.exists()) {file.createNewFile();}out = new RandomAccessFile(file, "rw");out.seek(out.length());byte[] audioData = new byte[bufferSizeInBytes];while (isRecord) {int i = audioRecord.read(audioData, 0, bufferSizeInBytes);if (AudioRecord.ERROR_INVALID_OPERATION != i) {out.write(audioData, 0, i);}}} catch (IOException e) {e.printStackTrace();} finally {IOUtils.freeSource(out);}if (file.exists() && file.length() > 0) {return file.getAbsolutePath();}return null;}
}

/*** AudioRecord的裝飾類* @author Francis-ChinaFeng* @version 1.0 2013-8-30*/
public class AudioRecordDecorated {/*** 使用AudioRecord開始錄製PCM格式音頻文件* @param audioRecord	AudioRecord對象* @param audioSource	錄製音頻文件的硬件設備源* @param sampleRateInHz	錄製音頻文件的採樣率* @param channelConfig		錄製音頻文件的聲道* @param audioFormat		錄製音頻文件的編碼格式* @param bufferSizeInBytes	錄製音頻文件的緩存* @return*/public static AudioRecord play(AudioRecord audioRecord, int audioSource,int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes) {release(audioRecord);audioRecord = new AudioRecord(audioSource, sampleRateInHz,channelConfig, audioFormat, bufferSizeInBytes);audioRecord.startRecording();return audioRecord;}/*** 釋放AudioRecord資源* @param audioRecord* @return*/public static AudioRecord release(AudioRecord audioRecord) {if (audioRecord != null) {audioRecord.stop();audioRecord.release();audioRecord = null;}return audioRecord;}/*** 將PCM格式音頻文件轉換成WAV格式文件* @param source	PCM文件源路徑* @param target	WAV文件目標路徑* @param sampleRateInHz	* @param bufferSizeInBytes* @param bool		是否刪除PCM文件* @return*/public static String convertWAVFile(String source, String target,long sampleRateInHz, int bufferSizeInBytes, boolean bool) {File file = new File(source);if (file != null && file.exists() && file.isFile() && file.length() > 0) {File targetFile = convertWAVFile(source, target, sampleRateInHz, bufferSizeInBytes);if (targetFile != null && targetFile.exists() && targetFile.isFile()) {if (bool) {file.delete();}return targetFile.getAbsolutePath();}}return null;}/*** * @param source* @param target* @param sampleRateInHz* @param bufferSizeInBytes* @return*/private static File convertWAVFile(String source, String target,long sampleRateInHz, int bufferSizeInBytes) {BufferedInputStream in = null;BufferedOutputStream out = null;long longSampleRate = sampleRateInHz;int channels = 2;long byteRate = 16 * sampleRateInHz * channels / 8;byte[] buffer = new byte[bufferSizeInBytes];File targetFile = new File(target);try {if (!targetFile.exists()) {targetFile.createNewFile();}in = new BufferedInputStream(new FileInputStream(source));out = new BufferedOutputStream(new FileOutputStream(targetFile));long totalAudioLen = in.available();long totalDataLen = totalAudioLen + 36;convertWAVFile(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);int i;while ((i = in.read(buffer)) != -1) {out.write(buffer, 0, i);out.flush();}} catch (IOException e) {e.printStackTrace();} finally {IOUtils.freeSource(out, in);}if (targetFile != null && targetFile.exists() && targetFile.length() > 0) {System.out.println(targetFile.getAbsolutePath() +"---"+ targetFile.length());return targetFile;}return null;}/*** 在PCM文件之前添加44個字節,轉換成WAV格式文件* @param out* @param totalAudioLen* @param totalDataLen* @param longSampleRate* @param channels* @param byteRate* @throws IOException*/private static void convertWAVFile(BufferedOutputStream out,long totalAudioLen, long totalDataLen, long longSampleRate,int channels, long byteRate) throws IOException {byte[] header = new byte[44];header[0] = 'R';header[1] = 'I';header[2] = 'F';header[3] = 'F';header[4] = (byte) (totalDataLen & 0xff);header[5] = (byte) ((totalDataLen >> 8) & 0xff);header[6] = (byte) ((totalDataLen >> 16) & 0xff);header[7] = (byte) ((totalDataLen >> 24) & 0xff);header[8] = 'W';header[9] = 'A';header[10] = 'V';header[11] = 'E';header[12] = 'f';header[13] = 'm';header[14] = 't';header[15] = ' ';header[16] = 16;header[17] = 0;header[18] = 0;header[19] = 0;header[20] = 1;header[21] = 0;header[22] = (byte) channels;header[23] = 0;header[24] = (byte) (longSampleRate & 0xff);header[25] = (byte) ((longSampleRate >> 8) & 0xff);header[26] = (byte) ((longSampleRate >> 16) & 0xff);header[27] = (byte) ((longSampleRate >> 24) & 0xff);header[28] = (byte) (byteRate & 0xff);header[29] = (byte) ((byteRate >> 8) & 0xff);header[30] = (byte) ((byteRate >> 16) & 0xff);header[31] = (byte) ((byteRate >> 24) & 0xff);header[32] = (byte) (2 * 16 / 8);header[33] = 0;header[34] = 16;header[35] = 0;header[36] = 'd';header[37] = 'a';header[38] = 't';header[39] = 'a';header[40] = (byte) (totalAudioLen & 0xff);header[41] = (byte) ((totalAudioLen >> 8) & 0xff);header[42] = (byte) ((totalAudioLen >> 16) & 0xff);header[43] = (byte) ((totalAudioLen >> 24) & 0xff);out.write(header, 0, 44);}}

/*** * @author Francis-ChinaFeng* @version 1.0 2013-8-31*/
public class AudioTrackDecorated {/*** 使用AudioTrack播放PCM、WAV格式文件* @param audioTrack	* @param sampleRateInHz* @param channelConfig* @param audioFormat* @param bufferSizeInBytes* @return*/public static AudioTrack play(AudioTrack audioTrack, int sampleRateInHz,int channelConfig, int audioFormat, int bufferSizeInBytes) {release(audioTrack);int streamType = AudioManager.STREAM_MUSIC;int mode = AudioTrack.MODE_STREAM;audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig,audioFormat, bufferSizeInBytes, mode);audioTrack.play();return audioTrack;}/*** 釋放AudioTrack對象資源* @param audioTrack* @return*/public static AudioTrack release(AudioTrack audioTrack) {if (audioTrack != null) {audioTrack.stop();audioTrack.release();audioTrack = null;}return audioTrack;}}

整個錄製音頻操作也就差不多了,不過還有些內容是可以完善的:用戶拖動SeekBar操作、動態改變採樣率、PCM文件轉其他格式音頻文件的操作等


歡飲拍磚

这篇关于Android之音頻錄製-實例篇的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1028557

相关文章

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR

Android逆向(反调,脱壳,过ssl证书脚本)

文章目录 总结 基础Android基础工具 定位关键代码页面activity定位数据包参数定位堆栈追踪 编写反调脱壳好用的脚本过ssl证书校验抓包反调的脚本打印堆栈bilibili反调的脚本 总结 暑假做了两个月的Android逆向,记录一下自己学到的东西。对于app渗透有了一些思路。 这两个月主要做的是代码分析,对于分析完后的持久化等没有学习。主要是如何反编译源码,如何找到

android系统源码12 修改默认桌面壁纸--SRO方式

1、aosp12修改默认桌面壁纸 代码路径 :frameworks\base\core\res\res\drawable-nodpi 替换成自己的图片即可,不过需要覆盖所有目录下的图片。 由于是静态修改,则需要make一下,重新编译。 2、方法二Overlay方式 由于上述方法有很大缺点,修改多了之后容易遗忘自己修改哪些文件,为此我们采用另外一种方法,使用Overlay方式。