我的电话录音机服务一直在崩溃

时间:2018-05-01 17:47:12

标签: java android kotlin

我在我的应用中使用以下代码进行通话记录,但是一旦“'服务”出现问题,它就会崩溃。被称为!

BroadcastReceiver用于检测呼叫,并启动AudioService是:

class PhoneStateReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {

        val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
        val incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)

        when(state){
            TelephonyManager.EXTRA_STATE_RINGING -> {
                            Toast.makeText(context, "Ringing $incomingNumber", Toast.LENGTH_LONG).show()
                            }
            TelephonyManager.EXTRA_STATE_OFFHOOK -> {
                            Toast.makeText(context, "On Call $incomingNumber", Toast.LENGTH_LONG).show()
                            context.startService(Intent(context, AudioService::class.java))
                            }
            TelephonyManager.EXTRA_STATE_IDLE -> {
                        Toast.makeText(context, "IDLE", Toast.LENGTH_LONG).show()
                        context.stopService(Intent(context, AudioService::class.java))
                         }
        }
    }
}

用于记录通话的AudioService,但不断崩溃:

class AudioService : Service(), MediaRecorder.OnInfoListener {

    lateinit var context: Context
    private var mRecorder: MediaRecorder? = null
    //setting maximum file size to be recorded
    private val Audio_MAX_FILE_SIZE: Long = 1000000//1Mb

    private var mOutputFile: File? = null
    private var mStartTime: Long = 0

    private val outputFile: File
        get() {
            val dateFormat = SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US)
            return File(context.filesDir, //Environment.getExternalStorageDirectory(Environment.DIRECTORY_DOWNLOADS)
                  //  .absolutePath.toString()
                    "RECORDING_"  // "/Voice Recorder/RECORDING_"
                    + dateFormat.format(Date())
                    + ".m4a")
        }

    override fun onInfo(mr: MediaRecorder?, what: Int, extra: Int) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
            stopRecording(true)
        }
    }

    override fun onBind(intent: Intent?): IBinder? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
        return null
    }

    override fun onCreate() {
        context = this
      //  Toast.makeText(context,"created", Toast.LENGTH_LONG).show()
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Toast.makeText(context,"started recording", Toast.LENGTH_LONG).show()

        mRecorder = MediaRecorder()
        mRecorder!!.setOnInfoListener(this)

        mRecorder!!.apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setMaxFileSize(Audio_MAX_FILE_SIZE)
            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)

            setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC)
            setAudioEncodingBitRate(48000)

            setAudioSamplingRate(16000)
        }
        mOutputFile = outputFile
        mOutputFile!!.parentFile.mkdirs()
        mRecorder!!.setOutputFile(mOutputFile!!.absolutePath)

        try {
            mRecorder!!.apply {
                prepare()
                start()
            }

            mStartTime = SystemClock.elapsedRealtime()
        } catch (e: IOException) {
        }

        return Service.START_STICKY
    }

    private fun stopRecording(saveFile: Boolean) {
        Toast.makeText(context,"stopped recording ", Toast.LENGTH_LONG).show()
        mRecorder!!.apply {
            stop()
            release()
        }
        mRecorder = null
        mStartTime = 0
        if (!saveFile && mOutputFile != null) {
            mOutputFile!!.delete()
        }
        // to stop the service by itself
        stopSelf()

    }

    override fun onDestroy() {
        super.onDestroy()
        stopRecording(true)
    }
}

AndroidManifest我添加了以下内容:

    <receiver android:name=".broadcasts.PhoneStateReceiver">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
            <action android:name="android.intent.action.READ_PHONE_STATE" />
        </intent-filter>
    </receiver>

    <service android:name=".Services.AudioService" />

我已经在MainActivity

中授予了android.Manifest.permission.READ_PHONE_STATE所需的运行时权限

更新

我在调试器中遇到了这个错误:

  

java.lang.RuntimeException:setAudioSource failed

所以,参考this我添加了Manifest.permission.RECORD_AUDIO的运行时权限,但现在又出现了另一个错误,即:

  

java.lang.RuntimeException:start failed。

2 个答案:

答案 0 :(得分:1)

需要添加以下权限:

uses-permission android:name="android.permission.RECORD_AUDIO"

此外,这是一个危险的&#34;权限,因此必须由用户直接授予: https://developer.android.com/training/permissions/requesting

答案 1 :(得分:1)

我发现原因是由于Android O API的变化。关于前台服务,并通知用户媒体录制。

我的Manifest是:

<receiver android:name=".broadcasts.PhoneStateReceiver">
    <intent-filter>
        <action android:name="android.intent.action.PHONE_STATE" />
        <action android:name="android.intent.action.READ_PHONE_STATE" />
    </intent-filter>
</receiver>
<service android:name=".Services.AudioService"
    android:enabled="true"
    android:exported="true"/>

我的Broadcast Receiver是:

class PhoneStateReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {

        prefs = context.getSharedPreferences(PREFS_FILENAME, 0)
        val record_calls = prefs!!.getBoolean("recordCalls", false)
        val service = Intent(context, AudioService::class.java)

        val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
        val incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)

        when(state){
            TelephonyManager.EXTRA_STATE_RINGING -> {
                      //      Toast.makeText(context, "Ringing $incomingNumber", Toast.LENGTH_LONG).show()
                            }
            TelephonyManager.EXTRA_STATE_OFFHOOK -> {
               // Toast.makeText(context, "IS_SERVICE_RUNNING $IS_SERVICE_RUNNING", Toast.LENGTH_LONG).show()
                            if (record_calls) {
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                                    context.startForegroundService(service)
                                } else context.startService(service)
                                IS_SERVICE_RUNNING = true
                                }
            }
            TelephonyManager.EXTRA_STATE_IDLE -> if (IS_SERVICE_RUNNING) context.stopService(service)
            else -> Toast.makeText(context, "ERROR", Toast.LENGTH_LONG).show()
        }
    }
}

我的Service是:

var IS_SERVICE_RUNNING = false

class AudioService : Service(), MediaRecorder.OnInfoListener {

    lateinit var context: Context
    private var mRecorder: MediaRecorder? = null
    //setting maximum file size to be recorded
    private val Audio_MAX_FILE_SIZE: Long = 1000000//1Mb

    private var mOutputFile: File? = null
    private var mStartTime: Long = 0

    private val outputFile: File
        get() {
            val dateFormat = SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.US)
            return File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), //  Environment.DIRECTORY_DOCUMENTS, //    context.filesDir, //
                    //  .absolutePath.toString()
                    "call_"  // "/Voice Recorder/RECORDING_"
                            + dateFormat.format(Date())
                            + ".m4a")
        }

    override fun onInfo(mr: MediaRecorder?, what: Int, extra: Int) {
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
             stopRecording(true)
        }
    }

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        context = this
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Toast.makeText(context,"started recording", Toast.LENGTH_LONG).show()

                val intent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
    val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
    val nManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    val notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                Notification.Builder(context, NotificationService.CHANNEL_ID)
            } else {
                Notification.Builder(context)
            }.apply {
                setContentIntent(pendingIntent)
                setSmallIcon(R.drawable.ic_error_black_24dp)

           setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
                setAutoCancel(true)

         setContentTitle(resources.getString(R.string.recording_title))
                setStyle(Notification.BigTextStyle()                          .bigText(resources.getString(R.string.recording_body)))

          setContentText(resources.getString(R.string.recording_body))
            }.build()


    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
        startForeground(1, notification);
    } else {
        nManager.notify(1, notification)
    }

        mRecorder = MediaRecorder().apply {
            // reset()
        }
        mRecorder!!.setOnInfoListener(this)

        mOutputFile = outputFile
        mOutputFile!!.parentFile.mkdirs()

        mRecorder = MediaRecorder()
        mRecorder!!.apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            //  setMaxFileSize(Audio_MAX_FILE_SIZE)
            setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
            // setOutputFile(mFileName)
            setAudioEncoder(MediaRecorder.AudioEncoder.HE_AAC)
            setAudioEncodingBitRate(48000)
            setAudioSamplingRate(16000)
            setOutputFile(mOutputFile!!.absolutePath)
        }

        try {
            mRecorder!!.prepare()
            mRecorder!!.start()
        } catch (ise: IllegalStateException) {
            Toast.makeText(context,"Error 1 $ise ", Toast.LENGTH_LONG).show()
        } catch (ioe: IOException) {
            Toast.makeText(context,"Error 2 $ioe ", Toast.LENGTH_LONG).show()
        }

        return Service.START_STICKY
    }

    private fun stopRecording(saveFile: Boolean) {
        Toast.makeText(context,"stopped recording ", Toast.LENGTH_LONG).show()

        mRecorder!!.apply {
            stop()
            reset()
            release()
        }
        mRecorder = null
        mStartTime = 0
        if (!saveFile && mOutputFile != null) {
            mOutputFile!!.delete()
        }
        // to stop the service by itself
        stopSelf()

    }

    override fun onDestroy() {
        super.onDestroy()
      //  Toast.makeText(context,"service destroyed ", Toast.LENGTH_LONG).show()
        stopRecording(true)
    }
}

我的NotificationUtility是:

@RequiresApi(Build.VERSION_CODES.O)
class NotificationUtils(base: Context) : ContextWrapper(base) {

    val nManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    init {
        createChannels()
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createChannels() {
        val myChannel = NotificationChannel(CHANNEL_ID,
                CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT).apply {
            enableLights(true)
            enableVibration(true)
            lightColor = Color.GREEN
            lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        }

        nManager.createNotificationChannel(myChannel)
    }

    companion object {
        const val CHANNEL_ID = "my.CHANNEL_ID"
        const val CHANNEL_NAME = "my.Notification"
    }
}

对于运行时权限,我创建了Context Extensions

fun Context.toast(message: CharSequence) =
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()

fun Context.arePermissionsGranted(permissions: Array<String>): Boolean {
    permissions.forEach { it ->
        if(ActivityCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED)
            return false
    }
    return true
}

    fun Context.isPermissionGranted(permission: String) =
            ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED

    fun Context.batchRequestPermissions(permissions: Array<String>, requestId: Int) =
            ActivityCompat.requestPermissions(this as Activity, permissions, requestId)

    fun Context.requestPermission(permission: String, requestId: Int) =
            ActivityCompat.requestPermissions(this as Activity, arrayOf(permission), requestId)

MainActivity我&#t; ve:

val CALL_PERMISSIONS =
        arrayOf(READ_PHONE_STATE, RECORD_AUDIO, WRITE_EXTERNAL_STORAGE)
const val CALL_RECORD_PERMISSION_REQUEST_ALL = 10

class MainActivity : AppCompatActivity() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)  NotificationUtils(this)

val self = this as Context

        val dialog = AlertDialog.Builder(this).apply {
            setTitle(R.string.permissions_required)
            setIcon(R.drawable.ic_done_all_black_24dp)
            setMessage(R.string.grant_permissions_required)

            setPositiveButton("Confirm", { dialog, i ->
                self.batchRequestPermissions(CALL_PERMISSIONS, CALL_RECORD_PERMISSION_REQUEST_ALL)
            })
        }
}