AndroidManifest.xml报多根标签等错误,录音应用点击按钮崩溃求助
解决Android录音应用Manifest错误与录制崩溃问题
先处理AndroidManifest.xml的标签错误
虽然你觉得Manifest结构规范,但AS提示的Multiple root tags和Top level element is not completed肯定是XML结构有问题,按以下步骤排查:
- 检查是否有多余的根标签:整个Manifest只能有一个
<manifest>根标签,不能在它外面加任何其他标签(比如不小心把<uses-permission>写在了<manifest>外面) - 检查标签闭合:确认所有标签都有对应的闭合标签,比如
<activity>必须有</activity>,<intent-filter>不能漏了</intent-filter> - 排查注释错误:如果有XML注释,确保
<!--和-->是成对出现的,半拉注释会破坏XML结构 - 可以把Manifest代码复制到XML校验工具里(本地或离线的),快速定位结构错误
给你一个标准的录音应用Manifest模板,对比检查你的代码:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.your_recorder"> <!-- 录音必需权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- Android 10以下需要存储权限保存录音文件 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.RecorderApp"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
再解决录制按钮崩溃问题
Manifest的结构错误可能导致组件注册异常,加上录音权限处理不当,都会引发崩溃,按以下步骤排查:
- 权限问题:Android 6.0(API 23)以上,录音权限属于危险权限,必须动态申请,不能只在Manifest里声明。如果没申请权限就调用录制逻辑,会直接崩溃
- MediaRecorder初始化错误:比如输出文件路径不存在、未正确设置音频源/编码器,都会抛出IO异常导致崩溃
- 查看崩溃日志:打开Android Studio底部的Logcat,选择你的应用包名,过滤
Error级别,点击录制按钮后,查看具体的异常信息(比如SecurityException是权限问题,NullPointerException是空指针)
给你一段基础的MainActivity录制逻辑示例,包含权限申请:
import android.Manifest; import android.content.pm.PackageManager; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.widget.Button; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import java.io.IOException; public class MainActivity extends AppCompatActivity { private static final int RECORD_PERMISSION_REQUEST = 1; private MediaRecorder mediaRecorder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button recordBtn = findViewById(R.id.btn_record); recordBtn.setOnClickListener(v -> checkRecordPermission()); } private void checkRecordPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, RECORD_PERMISSION_REQUEST); } else { startRecording(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == RECORD_PERMISSION_REQUEST) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startRecording(); } else { Toast.makeText(this, "需要授予录音权限才能录制", Toast.LENGTH_SHORT).show(); } } } private void startRecording() { mediaRecorder = new MediaRecorder(); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); // 用应用私有目录存储录音,无需存储权限(Android 10+) String outputPath = getExternalFilesDir(Environment.DIRECTORY_AUDIO_RECORDINGS) + "/recording.3gp"; mediaRecorder.setOutputFile(outputPath); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); try { mediaRecorder.prepare(); mediaRecorder.start(); Toast.makeText(this, "正在录制...", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "录制失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); releaseRecorder(); } } private void releaseRecorder() { if (mediaRecorder != null) { try { mediaRecorder.stop(); } catch (RuntimeException e) { // 处理未开始录制就停止的情况 } mediaRecorder.release(); mediaRecorder = null; } } @Override protected void onDestroy() { super.onDestroy(); releaseRecorder(); } }
内容的提问来源于stack exchange,提问作者Dinesen
相关产品推荐
相关产品推荐

