zoukankan      html  css  js  c++  java
  • 学习Android过程中遇到的问题及解决方法——电话监听

    也许有时你会有这样一个需求:通电话时有一个重要的事需要记下来或者和一个陌生人特别是大骗子通话时,这是就想如果能把通话录下来就方便多了。(这才是我写这个代码的目的!!!)

    在此过程中,犯了一个很大的错误。对电话状态还不熟悉就开始编程,使得我就算编写正确也出现各种bug。

    先将代码列出来,供大家参考,然后解释错误和相关知识。

    activity_main.xml:

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:tools="http://schemas.android.com/tools"
     4     android:id="@+id/container"
     5     android:layout_width="match_parent"
     6     android:layout_height="match_parent"
     7     android:orientation="vertical"
     8     tools:context="com.lgqchinese.homework3.MainActivity" >
     9 
    10     <Button
    11         android:layout_width="wrap_content"
    12         android:layout_height="wrap_content"
    13         android:text="开启服务"
    14         android:id="@+id/btn_startService"
    15         />
    16 
    17     <Button
    18         android:layout_width="wrap_content"
    19         android:layout_height="wrap_content"
    20         android:text="关闭服务"
    21         android:id="@+id/btn_stopService"
    22         />
    23 
    24 </LinearLayout>

    MainAvtivity.java:

    /**
     * 在这里启动服务
     */
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    
    public class MainActivity extends Activity implements OnClickListener{
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button btn_startService = (Button) findViewById(R.id.btn_startService);
            Button btn_stopService = (Button) findViewById(R.id.btn_stopService);
    
            //监听按钮点击
            btn_startService.setOnClickListener(this);
            btn_stopService.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
    
            Intent service = new Intent(getApplicationContext(), MyService.class);
    
            switch (v.getId()) {
                case R.id.btn_startService:
                    startService(service);
    
    //            bindService(service, conn, flags  为了调用服务里面方法
                    break;
                case R.id.btn_stopService:
                    stopService(service);
                    break;
    
            }
    
        }
    
    
    }

    MyServier.java:

    package com.lgqchinese.homework3;
    
    import android.app.Service;
    import android.content.Intent;
    import android.media.MediaRecorder;
    import android.os.IBinder;
    import android.telephony.PhoneStateListener;
    import android.telephony.TelephonyManager;
    
    import java.io.IOException;
    
    public class MyService extends Service {
        private MediaRecorder recorder;
        public MyService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.
            return null;
        }
        @Override
        public void onCreate() {
            // TODO Auto-generated method stub
            System.out.println("onCreate");
    
            // 监听电话状态
            TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    
            tm.listen(new myListen(), PhoneStateListener.LISTEN_CALL_STATE);
    
            super.onCreate();
        }
    
    
    
    
        public class myListen extends PhoneStateListener {
    
    
            @Override
            public void onCallStateChanged(int state, String incomingNumber) {
                // TODO Auto-generated method stub
                /**
                 * Callback invoked when device call state changes.
                 *
                 * @see TelephonyManager#CALL_STATE_IDLE
                 * @see TelephonyManager#CALL_STATE_RINGING
                 * @see TelephonyManager#CALL_STATE_OFFHOOK
                 */
                switch (state) {
                    case TelephonyManager.CALL_STATE_IDLE: // 空闲状态
                        System.out.println("CALL_STATE_IDLE: //空闲状态");
    
                        if (recorder != null) {
                            recorder.stop();
                            recorder.reset(); // You can reuse the object by going back to// setAudioSource() step
                            recorder.release(); // Now the object cannot be reused
    
                        }
    
    
                        break;
                    case TelephonyManager.CALL_STATE_RINGING: // 响铃状态
                        System.out.println("CALL_STATE_RINGING: //响铃状态");
    
                        recorder = new MediaRecorder();
                        //音频来源
                        System.out.println("声音来源");
                        recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
                        //音频的输出格式
                        recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);  //3gp
                        System.out.println("输出格式");
                        //设置音频编码格式
                        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                        System.out.println("编码格式");
                        //音频输出文件地址
                        recorder.setOutputFile("/mnt/sdcard/luyin.3gp");
                        System.out.println("文件路径");
                        //准备录音
    
                        try {
                            recorder.prepare();
                            System.out.println("正在准备");
                        } catch (IllegalStateException e) {
                            // TODO Auto-generated catch block
                            System.out.println("IllegalStateException异常");
                            e.printStackTrace();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                            System.out.println("IOException异常");
                        }
    
    
    
    
                        break;
                    case TelephonyManager.CALL_STATE_OFFHOOK: // 接听状态
                        System.out.println("CALL_STATE_OFFHOOK: //接听状态");
                        //开始录音
                        recorder.start(); // Recording is now started
                        break;
    
                }
    
                super.onCallStateChanged(state, incomingNumber);
            }
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            // TODO Auto-generated method stub
            System.out.println("onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            // TODO Auto-generated method stub
            System.out.println("onDestroy");
            super.onDestroy();
        }
    }

    Ok,代码完成。现在就让我们拔打电话来试试吧。(此时很多读过相关文档的小白一定都在笑话我吧)

    咦~~,出现这么个问题:

    为啥不行!为啥不行!为啥不行!开始调试:

    找到了这个:

    即在

    //开始录音
    recorder.start(); // Recording is now started

    出现问题,但里面的system.out也执行了呀

    然后你还会觉得是start()的问题吗?——会!

    应该会发现在响铃状态里面的语句都没执行。所以在start()前面的一些必备方法并没有执行,导致录音失败。

    我把响铃状态里面的语句拿到空闲状态,再拨打电话,成功了。。。。。不是真正的成功,虽然可以录音。

    此时,就在此时,我还没有阅读有关状态的知识。

    我进入CALL_STATE_RINGING所在类,发现里面全部标红:

    然后。。我。。把sdk卸载重装一边——(此时智商已经下线了,明明是Android Studio还没加载完)

    Ok。Ok,趁这空吃个晚饭,回来后想到了错误:

    经过阅读发现,响铃状态,即CALL_STATE_RINGING并不是打电话的状态,那就是接电话的状态了。

    敲了半天,不,敲了一天,原来是接电话。结果如下:

    由于没有理解电话状态代码的含义,明明对的代码还在不断的调试。以后要避免这种低级的错误。

    下面需要记录下状态码的含义:

    电话状态:

    状态码所在类是TelephonyManager:

    CALL_STATE_IDLE 无任何状态时

    CALL_STATE_OFFHOOK 接起电话时

    CALL_STATE_RINGING 电话进来时

    数据活动状态:

    DATA_ACTIVITY_IN 数据连接状态:活动,正在接受数据

    DATA_ACTIVITY_OUT 数据连接状态:活动,正在发送数据

    DATA_ACTIVITY_INOUT 数据连接状态:活动,正在接受和发送数据

    DATA_ACTIVITY_NONE 数据连接状态:活动,但无数据发送和接受

    数据连接状态:

    DATA_CONNECTED 数据连接状态:已连接

    DATA_CONNECTING 数据连接状态:正在连接

    DATA_DISCONNECTED 数据连接状态:断开

    DATA_SUSPENDED 数据连接状态:暂停

    网络类型:

    NETWORK_TYPE_CDMA 网络类型为CDMA

    NETWORK_TYPE_EDGE 网络类型为EDGE

    NETWORK_TYPE_EVDO_0 网络类型为EVDO0

    NETWORK_TYPE_EVDO_A 网络类型为EVDOA

    NETWORK_TYPE_GPRS 网络类型为GPRS

    NETWORK_TYPE_HSDPA 网络类型为HSDPA

    NETWORK_TYPE_HSPA 网络类型为HSPA

    NETWORK_TYPE_HSUPA 网络类型为HSUPA

    NETWORK_TYPE_UMTS 网络类型为UMTS

    在中国,联通的3G为UMTS或HSDPA,移动和联通的2G为GPRS或EGDE,电信的2G为CDMA,电信的3G为EVDO

    移动终端的类型:

    PHONE_TYPE_CDMA 手机制式为CDMA,电信

    PHONE_TYPE_GSM 手机制式为GSM,移动和联通

    PHONE_TYPE_NONE 手机制式未知

    移动终端:

    SIM_STATE_ABSENT SIM卡未找到

    SIM_STATE_NETWORK_LOCKED SIM卡网络被锁定,需要Network PIN解锁

    SIM_STATE_PIN_REQUIRED SIM卡PIN被锁定,需要User PIN解锁

    SIM_STATE_PUK_REQUIRED SIM卡PUK被锁定,需要User PUK解锁

    SIM_STATE_READY SIM卡可用 * SIM_STATE_UNKNOWN SIM卡未知

    打电话的功能以后再写。网上很多一起写了,而且都很好。我写一遍主要也是提高自己的水平。

    这就完了,得做点什么。我说过了,这才是我的目的。

    新建一个类:StartPhoneReceiver.java用来进行开机自启 ,按一下开启服务多不方便呀

    package com.lgqchinese.homework3;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    
    public class StartPhoneReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            // TODO: This method is called when the BroadcastReceiver is receiving
            // an Intent broadcast.
            //开启服务
    
            Intent service = new Intent(context, MyService.class);
    
            context.startService(service);
        }
    }

    在清单文件里注册一下:

    <receiver
        android:name=".StartPhoneReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>

    那你会想看到这个多余的图标吗,在MainActivity.java中加上两行黄底代码:

    1 @Override
    2     protected void onCreate(Bundle savedInstanceState) {
    3         super.onCreate(savedInstanceState);
    4         setContentView(R.layout.activity_main);
    5         PackageManager p = getPackageManager();
    6       p.setComponentEnabledSetting(getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    7 
    8         ………………………………
    9 }


    好了

    昔日我曾苍老,如今风华正茂(ง •̀_•́)ง
  • 相关阅读:
    开源一个常用的小软件的源码——系统数据库服务管理软件
    MySql Windws 下自动备份脚本
    ubuntu-14.04-server配置Jexus --安装步骤记录
    Jumony快速抓取网页 --- Jumony使用笔记--icode
    视频教程--ASP.NET MVC 使用 Petapoco 微型ORM框架+NpgSql驱动连接 PostgreSQL数据库
    收录.NET跨平台及跨数据库的博文...
    ASP.NET MVC 使用 Petapoco 微型ORM框架+NpgSql驱动连接 PostgreSQL数据库
    Windbg程序调试系列
    QCY蓝牙耳机 左右两只耳机配对 方法
    wpf 的 Window或UserControl绑定自己后台属性
  • 原文地址:https://www.cnblogs.com/lgqrlchinese/p/9940441.html
Copyright © 2011-2022 走看看