
كيفية برمجة تطبيق تسجيل الصوت على Android Studio (Java)
في هذا المقال المفصل، سنستكشف خطوات برمجة تطبيق مسجل صوتي متقدم
لنظام Android باستخدام Android Studio ولغة Java.
سيركز التطبيق Voice Recorder على توفير ميزات تسجيل عالية الجودة،
وخيارات تنظيم فعالة للتسجيلات المحفوظة محليًا، وأدوات تحرير صوت أساسية على
نظام Android 7 (Nougat) والإصدارات الأحدث للاستفادة من واجهات
برمجة التطبيقات الحديثة وتحقيق أداء أفضل.
خطوات برمجة تطبيق تسجيل الصوت
سيتكون التطبيق من عدة أنشطة (Activities) وأجزاء (Fragments) للتعامل مع وظائفه المختلفة:
- نشاط التسجيل (RecordingActivity): واجهة لبدء وإيقاف التسجيل الصوتي.
- نشاط قائمة التسجيلات (RecordingsListActivity/Fragment):
عرض قائمة بالتسجيلات المحفوظة مع خيارات التشغيل والتنظيم والتحرير.
- نشاط التحرير (EditingActivity): واجهة لتحرير التسجيل الصوتي
المحدد (قص، دمج بسيط، تعديل مستوى الصوت).
- خدمة التسجيل (RecordingService - اختياري): لتشغيل التسجيل في الخلفية.
- فئات إدارة الصوت (AudioHelper/RecordingManager):
للتعامل مع عمليات تسجيل وتشغيل الصوت وحفظ الملفات.
- فئات إدارة البيانات (StorageManager/DatabaseHelper):
لتخزين معلومات التسجيلات (الاسم، التاريخ، المدة، العلامات).
الخطوة 1: تصميم واجهات المستخدم (Layouts)
- activity_recording.xml: أزرار لبدء/إيقاف التسجيل، مؤشر للوقت المنقضي :
<!-- end list -->
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...>
<TextView android:id="@+id/timerTextView" ... />
<Button android:id="@+id/recordButton" android:text="Record" ... />
<Button android:id="@+id/stopButton" android:text="Stop" android:enabled="false" ... />
</LinearLayout>
--
- item_recording.xml: تخطيط لعرض كل تسجيل في القائمة
(اسم، تاريخ، مدة، أزرار تشغيل/تحرير) :
<!-- end list -->
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...>
<TextView android:id="@+id/nameTextView" ... />
<TextView android:id="@+id/dateTextView" ... />
<TextView android:id="@+id/durationTextView" ... />
<ImageView android:id="@+id/playButton" ... />
<ImageView android:id="@+id/editButton" ... />
</LinearLayout>
--
- activity_editing.xml: عناصر تحكم للتشغيل، ومؤشر للموقع الحالي،
وأزرار للتحرير (قص، ضبط مستوى الصوت) :
<!-- end list -->
XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...>
<SeekBar android:id="@+id/seekBar" ... />
<Button android:id="@+id/cutButton" android:text="Cut" ... />
<Button android:id="@+id/volumeButton" android:text="Volume" ... />
</LinearLayout>
--
- fragment_recordings_list.xml: حاوية لعرض قائمة التسجيلات
باستخدام RecyclerView :
<!-- end list -->
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recordingsRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
--
الخطوة 2: برمجة نشاط التسجيل (RecordingActivity)
التعامل مع أذونات تسجيل الصوت (Manifest.permission.RECORD_AUDIO).
استخدام MediaRecorder لبدء وإيقاف التسجيل وحفظ الملف.
تحديث مؤشر الوقت أثناء التسجيل :
<!-- end list -->
Java
public class RecordingActivity extends AppCompatActivity { private TextView timerTextView; private Button recordButton; private Button stopButton; private MediaRecorder mediaRecorder; private String outputFile; private Timer timer; private long startTime = 0;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recording); timerTextView = findViewById(R.id.timerTextView); recordButton = findViewById(R.id/recordButton); stopButton = findViewById(R.id/stopButton);
recordButton.setOnClickListener(v -> startRecording()); stopButton.setOnClickListener(v -> stopRecording());
// طلب أذونات التسجيل ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100); }
private void startRecording() { outputFile = getExternalCacheDir().getAbsolutePath() + "/recording_" + System.currentTimeMillis() + ".3gp"; mediaRecorder = new MediaRecorder(); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mediaRecorder.setOutputFile(outputFile);
try { mediaRecorder.prepare(); mediaRecorder.start(); startTime = System.currentTimeMillis(); startTimer(); recordButton.setEnabled(false); stopButton.setEnabled(true); Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "Recording failed!", Toast.LENGTH_SHORT).show(); } }
private void stopRecording() { if (mediaRecorder != null) { mediaRecorder.stop(); mediaRecorder.release(); mediaRecorder = null; stopTimer(); recordButton.setEnabled(true); stopButton.setEnabled(false); Toast.makeText(this, "Recording stopped!", Toast.LENGTH_SHORT).show(); // حفظ معلومات التسجيل في قاعدة البيانات أو ملف saveRecordingInfo(outputFile); } }
private void startTimer() { timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { long elapsedTime = System.currentTimeMillis() - startTime; long minutes = (elapsedTime / 1000) / 60; long seconds = (elapsedTime / 1000) % 60; runOnUiThread(() -> timerTextView.setText(String.format("%02d:%02d", minutes, seconds))); } }, 0, 1000); }
private void stopTimer() { if (timer != null) { timer.cancel(); timer = null; } }
private void saveRecordingInfo(String filePath) { // منطق حفظ معلومات التسجيل (اسم الملف، التاريخ، المدة) // يمكنك استخدام SQLite أو SharedPreferences }}
--
الخطوة 3: برمجة قائمة التسجيلات (RecordingsListActivity/Fragment)
استخدام RecyclerView لعرض قائمة التسجيلات من قاعدة البيانات أو ملف.
إنشاء Adapter لعرض بيانات التسجيل.
التعامل مع النقر على عناصر القائمة لتشغيل أو تحرير التسجيل.
استخدام MediaPlayer لتشغيل الصوت.
<!-- end list -->
Java
public class RecordingsListFragment extends Fragment { private RecyclerView recordingsRecyclerView; private RecordingAdapter adapter; private List<Recording> recordingList; private MediaPlayer mediaPlayer;
@Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_recordings_list, container, false); recordingsRecyclerView = view.findViewById(R.id.recordingsRecyclerView); recordingsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recordingList = getRecordingsFromStorage(); // استرجاع التسجيلات من قاعدة البيانات أو ملف adapter = new RecordingAdapter(recordingList, this::onRecordingClicked, this::onEditClicked); recordingsRecyclerView.setAdapter(adapter); return view; }
private List<Recording> getRecordingsFromStorage() { // منطق استرجاع معلومات التسجيل من قاعدة البيانات أو ملف return new ArrayList<>(); }
private void onRecordingClicked(Recording recording) { playRecording(recording.getFilePath()); }
private void onEditClicked(Recording recording) { Intent intent = new Intent(getContext(), EditingActivity.class); intent.putExtra("filePath", recording.getFilePath()); startActivity(intent); }
private void playRecording(String filePath) { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(filePath); mediaPlayer.prepare(); mediaPlayer.start(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(getContext(), "Failed to play!", Toast.LENGTH_SHORT).show(); } }
// فئة Recording و RecordingAdapter هنا}
--
الخطوة 4: برمجة نشاط التحرير (EditingActivity)
عرض شريط تمرير (SeekBar) لتتبع تقدم التشغيل.
أزرار للقص (يتطلب معالجة ملف الصوت)، وضبط مستوى الصوت (AudioManager).
قد تحتاج إلى استخدام مكتبات خارجية لمعالجة الصوت بشكل متقدم
(مثل MPAndroidChart لعرض الموجة الصوتية).
<!-- end list -->
Java
public class EditingActivity extends AppCompatActivity { private SeekBar seekBar; private MediaPlayer mediaPlayer; private String filePath;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_editing); seekBar = findViewById(R.id.seekBar); filePath = getIntent().getStringExtra("filePath"); playRecording(filePath);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser && mediaPlayer != null) { mediaPlayer.seekTo(progress); } }
@Override public void onStartTrackingTouch(SeekBar seekBar) { }
@Override public void onStopTrackingTouch(SeekBar seekBar) { } }); }
private void playRecording(String filePath) { mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(filePath); mediaPlayer.prepare(); seekBar.setMax(mediaPlayer.getDuration()); mediaPlayer.start(); updateSeekBar(); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "Failed to play!", Toast.LENGTH_SHORT).show(); } }
private void updateSeekBar() { new Handler().postDelayed(new Runnable() { @Override public void run() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { seekBar.setProgress(mediaPlayer.getCurrentPosition()); updateSeekBar(); } } }, 100); }
// منطق وظائف التحرير (قص، ضبط مستوى الصوت) هنا - قد يتطلب مكتبات خارجية}
--
الخطوة 5: إدارة البيانات (قاعدة بيانات SQLite)
إنشاء قاعدة بيانات لتخزين معلومات التسجيلات (اسم الملف، المسار، التاريخ، المدة، العلامات).
إنشاء فئة DatabaseHelper للتعامل مع عمليات قاعدة البيانات (إنشاء، إدراج، استعلام).
<!-- end list -->
Java
public class DatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = "recordings.db"; private static final int DATABASE_VERSION = 1; public static final String TABLE_NAME = "recordings"; public static final String COLUMN_ID = "_id"; public static final String COLUMN_NAME = "name"; public static final String COLUMN_FILE_PATH = "file_path"; public static final String COLUMN_DATE = "date"; public static final String COLUMN_DURATION = "duration";
private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + TABLE_NAME + " (" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + COLUMN_NAME + " TEXT," + COLUMN_FILE_PATH + " TEXT UNIQUE," + COLUMN_DATE + " INTEGER," + COLUMN_DURATION + " INTEGER)";
private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + TABLE_NAME;
public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); }
@Override public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); }
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(SQL_DELETE_ENTRIES); onCreate(db); }
// طرق لإدراج واسترجاع معلومات التسجيل}
--
الخطوة 6: ميزات متقدمة (اختياري)
أ. السماح للمستخدم بإضافة علامات يدوية (Manual Tagging):
هذه الميزة تتضمن تصميم واجهة مستخدم للسماح للمستخدم بإضافة علامات نصية أو فئات للتسجيلات.
واجهة المستخدم: أضف حقل إدخال نص (EditText) وزر "إضافة علامة"
في تخطيط تفاصيل التسجيل أو في شاشة التحرير (activity_editing.xml أو تخطيط مخصص).
<!-- end list -->
XML
<EditText android:id="@+id/tagEditText" android:layout_width="0dp" android:layout_height="wrap_content" android:hint="أضف علامة" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/seekBar" app:layout_constraintEnd_toStartOf="@+id/addTagButton" />
<Button android:id="@+id/addTagButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="إضافة" app:layout_constraintTop_toBottomOf="@+id/seekBar" app:layout_constraintEnd_toEndOf="parent" />
<TextView android:id="@+id/tagsTextView" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:text="العلامات: " app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tagEditText" app:layout_constraintEnd_toEndOf="parent" />
--
* التعامل مع الإدخال: في نشاط التحرير (EditingActivity) أو نشاط تفاصيل التسجيل،
استمع إلى نقرات زر "إضافة علامة" واسترد النص من EditText.
<!-- end list -->
Java
public class EditingActivity extends AppCompatActivity { // ... عناصر واجهة المستخدم الأخرى private EditText tagEditText; private Button addTagButton; private TextView tagsTextView; private List<String> tags = new ArrayList<>();
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_editing); // ... تهيئة عناصر واجهة المستخدم الأخرى tagEditText = findViewById(R.id.tagEditText); addTagButton = findViewById(R.id.addTagButton); tagsTextView = findViewById(R.id.tagsTextView);
addTagButton.setOnClickListener(v -> { String tag = tagEditText.getText().toString().trim(); if (!tag.isEmpty()) { tags.add(tag); updateTagsTextView(); tagEditText.setText(""); // مسح حقل الإدخال saveTagsToStorage(tags); // حفظ العلامات في قاعدة البيانات أو ملف } });
// استرجاع العلامات عند تحميل النشاط loadTagsFromStorage(); updateTagsTextView(); }
private void updateTagsTextView() { StringBuilder sb = new StringBuilder("العلامات: "); for (String tag : tags) { sb.append(tag).append(", "); } tagsTextView.setText(sb.toString()); }
private void saveTagsToStorage(List<String> currentTags) { // منطق حفظ العلامات المرتبطة بالتسجيل في قاعدة البيانات أو ملف // قد تحتاج إلى إضافة عمود جديد في جدول التسجيلات أو إنشاء جدول منفصل للعلامات }
private void loadTagsFromStorage() { // منطق استرجاع العلامات المرتبطة بالتسجيل من قاعدة البيانات أو ملف }
// ... بقية الكود}
--
ب. تحرير متقدم (قص بسيط):
يتطلب قص الصوت التعامل مع ملفات الصوت وتقسيمها.
يمكنك استخدام مكتبات خارجية لهذا الغرض، ولكن سأوضح لك فكرة بسيطة
تعتمد على إنشاء ملف جديد لجزء محدد.
Java
import android.media.MediaExtractor;import android.media.MediaFormat;import android.media.MediaMuxer;import android.os.Environment;
import java.io.File;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;import java.nio.file.Paths;import java.nio.file.StandardOpenOption;
public class AudioEditor {
public static File cutAudio(String inputPath, long startTimeUs, long endTimeUs) throws IOException { File inputFile = new File(inputPath); File outputFile = new File(Environment.getExternalCacheDir(), "cut_recording_" + System.currentTimeMillis() + ".3gp");
MediaExtractor extractor = new MediaExtractor(); MediaMuxer muxer = new MediaMuxer(outputFile.getAbsolutePath(), MediaMuxer.OutputFormat.THREE_GPP);
try { extractor.setDataSource(inputFile.getAbsolutePath()); int audioTrackIndex = -1; MediaFormat inputFormat = null;
for (int i = 0; i < extractor.getTrackCount(); i++) { MediaFormat format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); if (mime.startsWith("audio/")) { audioTrackIndex = i; inputFormat = format; muxer.addTrack(inputFormat); break; } }
if (audioTrackIndex == -1) { throw new IOException("No audio track found in " + inputPath); }
extractor.selectTrack(audioTrackIndex);
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024); // حجم مؤقت MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
extractor.seekTo(startTimeUs, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
while (true) { int sampleSize = extractor.readSampleData(buffer, 0); if (sampleSize < 0) { break; }
long currentTimestampUs = extractor.getSampleTime(); if (currentTimestampUs > endTimeUs) { break; }
bufferInfo.presentationTimeUs = currentTimestampUs - startTimeUs; bufferInfo.flags = extractor.getSampleFlags(); bufferInfo.size = sampleSize;
muxer.writeSampleData(0, buffer, bufferInfo); extractor.advance(); }
muxer.stop(); muxer.release(); extractor.release();
return outputFile;
} catch (IOException e) { muxer.release(); extractor.release(); throw e; } }}
--
* الاستخدام في EditingActivity: أضف زر "قص" واسترد نطاق
القص من واجهة المستخدم (مثل استخدام اثنين من SeekBar
لتحديد نقطتي البداية والنهاية). قم بتشغيل AudioEditor.cutAudio في
مؤشر ترابط منفصل لتجنب تجميد واجهة المستخدم.
<!-- end list -->
Java
public class EditingActivity extends AppCompatActivity { // ... عناصر واجهة المستخدم الأخرى private Button cutButton; private SeekBar startCutSeekBar; private SeekBar endCutSeekBar; private String filePath;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_editing); // ... تهيئة عناصر واجهة المستخدم الأخرى cutButton = findViewById(R.id.cutButton); startCutSeekBar = findViewById(R.id.startCutSeekBar); endCutSeekBar = findViewById(R.id.endCutSeekBar); filePath = getIntent().getStringExtra("filePath");
cutButton.setOnClickListener(v -> { long startTimeUs = startCutSeekBar.getProgress() * 1000L; // تحويل إلى ميكروثانية long endTimeUs = endCutSeekBar.getProgress() * 1000L; new Thread(() -> { try { File cutFile = AudioEditor.cutAudio(filePath, startTimeUs, endTimeUs); // التعامل مع الملف الناتج (تشغيله، حفظ مساره) runOnUiThread(() -> Toast.makeText(EditingActivity.this, "تم القص!", Toast.LENGTH_SHORT).show()); } catch (IOException e) { e.printStackTrace(); runOnUiThread(() -> Toast.makeText(EditingActivity.this, "فشل القص!", Toast.LENGTH_SHORT).show()); } }).start(); });
// تهيئة SeekBar لنطاق القص بناءً على مدة التسجيل }
// ... بقية الكود}
--
ج. التسجيل في الخلفية (Using Service):
للتسجيل في الخلفية، ستحتاج إلى إنشاء Service وتشغيله عند بدء التسجيل.
إنشاء RecordingService:
<!-- end list -->
Java
import android.app.Notification;import android.app.NotificationChannel;import android.app.NotificationManager;import android.app.Service;import android.content.Intent;import android.media.MediaRecorder;import android.os.Build;import android.os.IBinder;import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import java.io.IOException;
public class RecordingService extends Service { private static final String CHANNEL_ID = "RecordingServiceChannel"; private MediaRecorder mediaRecorder; private String outputFile;
@Override public void onCreate() { super.onCreate(); createNotificationChannel(); }
@Override public int onStartCommand(Intent intent, int flags, int startId) { outputFile = getExternalCacheDir().getAbsolutePath() + "/background_recording_" + System.currentTimeMillis() + ".3gp"; mediaRecorder = new MediaRecorder(); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); mediaRecorder.setOutputFile(outputFile);
try { mediaRecorder.prepare(); mediaRecorder.start();
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("تسجيل في الخلفية") .setContentText("التطبيق يقوم بالتسجيل في الخلفية") .setSmallIcon(android.R.drawable.ic_media_mic) .build();
startForeground(1, notification);
Toast.makeText(this, "بدأ التسجيل في الخلفية!", Toast.LENGTH_SHORT).show();
} catch (IOException e) { e.printStackTrace(); stopForeground(true); stopSelf(); Toast.makeText(this, "فشل بدء التسجيل في الخلفية!", Toast.LENGTH_SHORT).show(); }
return START_STICKY; // إذا تم إيقاف الخدمة بواسطة النظام، حاول إعادة تشغيلها }
@Override public void onDestroy() { if (mediaRecorder != null) { mediaRecorder.stop(); mediaRecorder.release(); mediaRecorder = null; Toast.makeText(this, "توقف التسجيل في الخلفية!", Toast.LENGTH_SHORT).show(); // حفظ معلومات التسجيل } stopForeground(true); stopSelf(); }
@Override public IBinder onBind(Intent intent) { return null; }
private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel serviceChannel = new NotificationChannel( CHANNEL_ID, "Recording Service Channel", NotificationManager.IMPORTANCE_DEFAULT ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(serviceChannel); } }}
--
* بدء وإيقاف الخدمة من RecordingActivity:
<!-- end list -->
Java
public class RecordingActivity extends AppCompatActivity {
// ... عناصر واجهة المستخدم الأخرى
private void startBackgroundRecording() {
Intent serviceIntent = new Intent(this, RecordingService.class);
startService(serviceIntent);
recordButton.setEnabled(false);
stopButton.setEnabled(true);
}
private void stopBackgroundRecording() {
Intent serviceIntent = new Intent(this, RecordingService.class);
stopService(serviceIntent);
recordButton.setEnabled(true);
stopButton.setEnabled(false);
}
// ... بقية الكود
}
--
* ملاحظات هامة (مفصلة):
الأذونات: تأكد من إضافة الأذونات التالية في AndroidManifest.xml:
<!-- end list -->
XML
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
وفي وقت التشغيل، اطلب `RECORD_AUDIO` و `WRITE_EXTERNAL_STORAGE` باستخدام `ActivityCompat.requestPermissions()`.
--
- إدارة دورة حياة التطبيق: في أنشطتك وخدماتك، قم بتحرير موارد
MediaRecorder و MediaPlayer في طرق مثل onStop() أو
onDestroy() لمنع تسرب الذاكرة والمشاكل الأخرى. على سبيل المثال:
<!-- end list -->
Java
@Override
protected void onStop() {
super.onStop();
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
if (mediaRecorder != null) {
mediaRecorder.release();
mediaRecorder = null;
}
}
--
- التعامل مع الأخطاء: استخدم كتل try-catch حول العمليات التي
قد تفشل (مثل mediaRecorder.prepare(), mediaPlayer.prepare(),
عمليات قراءة/كتابة الملفات). قم بعرض رسائل خطأ للمستخدم بشكل مناسب.
- واجهة مستخدم سلسة (Threading): أي عمليات تستغرق وقتًا طويلاً
(مثل قص الصوت، تحليل الصوت المحتمل) يجب تشغيلها في مؤشرات
ترابط خلفية (باستخدام AsyncTask أو ExecutorService أو
Thread مباشرة مع Handler لتحديث واجهة المستخدم).
يؤدي تنفيذ العمليات الطويلة على مؤشر ترابط واجهة المستخدم الرئيسي إلى تجميد التطبيق.
تذكر أن التحرير المتقدم وتحليل الصوت التلقائي يمكن أن يكونا معقدين
ويتطلبان استخدام مكتبات خارجية متخصصة أو معرفة متقدمة بمعالجة الإشارات الصوتية.
ابدأ بالميزات الأسهل وقم بتوسيع التطبيق تدريجيًا.
* الخلاصة :
برمجة تطبيق مسجل صوتي متقدم بميزات تنظيم وتحرير محلية هو
مشروع كبير يتطلب فهمًا جيدًا لبرمجة Android والتعامل مع الصوت.
يتضمن هذا المقال الهيكل الأساسي والأجزاء الرئيسية من الأكواد لكل خطوة.
ستحتاج إلى تطوير المزيد من التعليمات البرمجية لتنفيذ جميع الوظائف بشكل كامل،
خاصة ميزات التحرير المتقدمة والتنظيم التلقائي.
ابدأ بالأساسيات وقم ببناء الميزات تدريجيًا ، حظًا موفقًا في مشروعك!