学习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 }
好了