这次是一个循环拨打电话的需求,直到打通为止,否则就一直循环播打,这个问题的难点在于如何获取到对方接通电话的状态结束循环(此处说的非offhook状态,因为offhook只是摘机状态,无法判断对方是否接起电话),经过上网查阅资料,去电接通状态是获取不到的,可是怎么结束循环呢,这个问题困扰了我好长时间,后来有一次看到一个电话订票抢票软件,可以输入一个号码,一直循环拨打,直到打通为止,这不是跟我的需求一样吗!所以抱着学习的态度,同时也抱着试一试的态度,进行了反编译,查看了一下它的实现方式。所以,我们先来看一下如何反编译查看源码吧。
我在上篇文章中写了反编译apk获取资源文件,大家可以点击查看,本文主要介绍使用dex2jar反编译查看源码,反编译工具包也可以从中下载,下面是以8684这个电话订票抢票软件为例的。
使用dex2jar反编译获取源码
1. 首先,我们先将dex2jar及jd-gui下载并解压,dex2jar是用来反编译的,我们要用到d2j-dex2jar.bat这个文件,如果你是linux或mac系统的话就要用d2j-dex2jar.sh这个文件,jd-gui是用来查看反编译后的jar包的,这个在下面会说到,解压后如下图:
2.获取要反编译的apk中的dex文件,我们可以将demo.apk修改后缀名为.zip文件,并使用解压工具将其中的.dex文件取出,放到dex2jar解压后的目录,如下图所示:
3.在此目录下打开命令行,并执行 d2j-dex2jar classes.dex 指令,执行结束后,没有任何报错,则会在文件夹中得到classes-dex2jar.jar文件,如图:
4.classes-dex2jar.jar文件,我们是无法查看的,需要jd-gui工具来查看,打开jd-gui,并将此jar文件拖入打开的对话框中,即可查看源码:
自此,我们就获得了demo.apk中的源代码,但是一些资源id并不是以R.id.button的形式体现的,而是以真正的数字id体现,如上图中:this.comm.halt(getText(2131165188)),还需要注意的是,现在向市场上传apk时有时需要强制给apk加壳,这样我们这种反编译的方式是获取不到源码的,需要先进行脱壳,而且如果代码经过混淆以后,类名等都是以a,b,c等没有意义的字母直接表示的,造成阅读的难度,至于如何脱壳,再次不做赘述,以后再做研究。下面我们介绍下反编译出来的源码中停止循环的逻辑。
从反编译的源码中获取停止循环的逻辑
首先将应用安装到手机上,并定位到具有该功能的界面,使用hierarchyviewer.bat可以查看当前打开的是哪个activity,通过点击按钮的文字获取对应的id,并在activity中得到按钮调用方法的位置,一般简单的应用之间看onclick方法就能定位到,而且有的可以直接通过包名或者类名得到,比如本例中,直接通过包名qiangpiao就可以得到(是的,就是你没跑了):
可以看到,这些类名都是有意义的,所以大大降低了阅读源码的难度,通过分析,我最终将判断结束循环拨打电话的源码定位到了PhoneCallService这个类,经过分析发现,原来这里结束循环的做法是每次挂断电话以后,判断一下通话的数据库中的通话时长是否大于0(额,就这么简单吗。。。),因为打通之后,而对方没有接听电话的话,运营商会自动挂断电话,或者我们可以手动挂断电话,此时,通话时长为0,如果对方接听了电话,则通话时长大于0,核心代码及:
private boolean LastCallSucceed() {
if (!this.isCall)
return false;
String[] arrayOfString = { "number", "duration" };
Cursor localCursor = getContentResolver().query(CallLog.Calls.CONTENT_URI, arrayOfString, null, null, "date DESC"); //获取number跟duration列的cursor游标
return (localCursor.moveToFirst()) && (localCursor.getInt(1) > 0); //定位到最后一条通话,并返回duration的时间是否大于0
}
看到这里,算是解决了困扰了我好久的问题,结合这个我写了一个可以循环拨打电话的demo,逻辑也很简单,贴一下主要逻辑代码,写一个service,在onCreate()中,初始化要循环的电话,并监听电话的状态:
public void onCreate() {
mPhoneNumList.add("xxxxxxxxxxx");//TODO本demo需要自己添加测试号码
mPhoneNumList.add("xxxxxxxxxxx");
tm= (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(newMyPhoneStateListener(),PhoneStateListener.LISTEN_CALL_STATE); //监听电话状态
super.onCreate();
}
PhoneStateListener类:
classMyPhoneStateListenerextendsPhoneStateListener {
@Override
public voidonCallStateChanged(intstate,String incomingNumber) {
switch(state) {
caseTelephonyManager.CALL_STATE_IDLE:
mHandler.sendEmptyMessageDelayed(0,3000); //当挂断电话时,发送一个延时消息,调用拨打电话的逻辑,实现循环拨打电话
break;
caseTelephonyManager.CALL_STATE_OFFHOOK:
break;
caseTelephonyManager.CALL_STATE_RINGING:
break;
}
}
}
Handler的调用逻辑,接受到延时消息后,判断之后调用逻辑:
privateHandlermHandler=newHandler() {
@Override
public voidhandleMessage(Message msg) {
if(!isLastCallSucceed() &&mIsLoopCall) { //通过mIsLoopCall标记来过滤普通电话的挂断状态
startPhoneCall();
}else{
mIsLoopCall=false;
}
}
};
在MainActivity中只是简单进行Service的绑定及调用循环拨打电话的操作,这里就不贴了,具体的代码已经上传github,有需要的朋友可以down下来瞅一下,如果对您有所帮助,还望star哈,3Q!
这里是传送门:github地址