原来所谓的开机启动就是在zygote启动之后,孵化出来的进程啊,我杀了几次都会重新启动。
这个Service一直在响,打开应用管理器,发现是这个Service。
//TODO nexus的截图。
先用apktool初步解压出apk的内容
apktool解压出来的是smali文件,这里我们暂时不关注,先关注它解析后的AndroidManifest.xml
,毕竟这里面列举出了所有可能会用到的组件(如果不列出某组件却要用到的话,应用就会崩溃)。
在AndroidManifest.xml
文件中找这个Service。
上Jadx
Jadx主要是反编译dex文件的,而扔给它一个apk文件也是可以的,但是会占用很大的内存,因为还需要解析一些res目录下的图片等资源文件。
所以在上Jadx之前,先用zip等解压缩工具直接将apk解压出来,得到了两个dex文件。class.dex
和class2.dex
,这是由于方法数达到了上限65535而导致的分包现象,即不得不用两个dex来装所有编译后的class文件。参考:http://www.wjdiankong.cn/android%E5%85%B3%E4%BA%8Edex%E6%8B%86%E5%88%86multidex%E6%8A%80%E6%9C%AF%E8%AF%A6%E8%A7%A3/
然后分别将这两个dex放到Jadx中,并使用--deobf
参数进行反混淆(个人比较喜欢使用,对于一些类名方法名等被混淆的代码使用反混淆之后可以方便查找然后重命名)
有两种方式,一种是命令行方式:
方法一
# 使用jadx将两个dex文件反编译然后放到当前的jadx目录下(若没有则创建),并将标准输出和标准错误重定向到/dev/null,即不显示输出和错误信息。
$ jadx class.dex -d ./jadx --deobf 1>/dev/null 2>/dev/null
$ jadx class.dex -d ./jadx --deobf 1>/dev/null 2>/dev/null
然后反编译后的java源码就导出到./jadx目录下了。
方法二
另外一种方式是
$ jadx-gui class.dex
然后记得按下反混淆的选项。等左下角的进度条完成之后,点击save as gradle project。
找到其全名为com.weico.international.service.WeicoNewMsgPullService.java
先找到它的onCreate()
方法,毕竟这个方法会在首次启动改Service比调用的。
registerScreenNotify()
查看registerScreenNotify()
的定义,将三个Intent
android.intent.action.SCREEN_ON
android.intent.action.SCREEN_OFF
android.intent.action.USER_PRESENT
# 这三个Intent是用来获取用户的交互状态的。
# 参考:
# https://developer.android.com/reference/android/content/Intent.html </br>
注意 SCREEN_ON和SCREEN_OFF是无法在AndroidManifest.xml中注册的,必须用Java代码手动注册
加入到mReceiver中,并注册mReceiver这个广播接收者
而mReceiver是是一个自定义的BroadcastReceiver
从代码中看,作者认为只要匹配到action为android.intent.action.USER_PRESENT
时,才将mUserIsScreenLock
设置为false。
每个用户隔一段时间重新开始使用手机时,首先按电源键点亮屏幕,紧接着解锁。android.intent.action.USER_PRESENT就是解锁时发出的intent.
注意这个action只能有系统发出,是在用户唤醒机器的时候才会发出这种action.
参考:
http://blog.csdn.net/heikefangxian23/article/details/46888645
然后通过isProcessFrontground()
方法判断进程名为com.weico.international
的进程是否处于UI前台。
// com.weico.international.service.WeicoNewMsgPullService.java
private boolean isProcessFrontground() {
return ActivityUtils.isForeAppProcess(this);
}
// com/weico/international/utility/ActivityUtils.java
public static boolean isForeAppProcess(Context context) {
return isForeAppProcess(context, context.getApplicationInfo().processName);
}
public static boolean isForeAppProcess() {
return isForeAppProcess(WApplication.cContext, "com.weico.international");
}
private static boolean isForeAppProcess(Context context, String processName) {
List<RunningAppProcessInfo> runningTaskList = ((ActivityManager) context.getSystemService("activity")).getRunningAppProcesses();
if (runningTaskList == null || runningTaskList.isEmpty()) {
return false;
}
for (RunningAppProcessInfo appProcessInfo : runningTaskList) {
if (processName.contentEquals(appProcessInfo.processName)) {
// importance为100其实就是处于进程处于前台UI界面,正在与用户交互。参考:https://developer.android.com/reference/android/app/ActivityManager.RunningAppProcessInfo.html#IMPORTANCE_FOREGROUND
if (appProcessInfo.importance == 100) {
return true;
}
return false;
}
}
return false;
}
即当用户处于weico界面前台时,则清除(cancel)掉这四种通知。
// com/weico/international/model/sina/UnreadMsg.java
public class UnreadMsg extends BaseType {
...
public static final int NEW_AT_ME_COMMENT_IDENTIFIER = 10000005;
public static final int NEW_AT_ME_IDENTIFIER = 10000001;
public static final int NEW_COMMENT_IDENTIFIER = 10000002;
public static final int NEW_DM_IDENTIFIER = 10000003;
public static final int NEW_FOLLOWER_IDENTIFIER = 10000004;
然后就是执行一个每10s fetch一下未读消息的Runnable了。
看一下这个
而这个mToStopRunnable
变量只在onDestroy()
方法中设置为true,从而结束Ruannble的运行。
// com.weico.international.service.WeicoNewMsgPullService.java
...
public void onDestroy() {
super.onDestroy();
// 注销广播
unregisterReceiver(this.mReceiver);
// 停止Runnable
this.mToStopRunnable = true;
// shutdown掉ExecutorService es好习惯
this.es.shutdown();
}
src/main/java/com/weico/international/service/WeicoNewMsgPullService.java
private void fetchUnreadCount() {
Account mAccount = AccountsStore.getCurAccount(); // 这里命名为什么要加m?
if (mAccount != null) {
this.mUnreadMsgApi = new UnreadMsgAPI(mAccount.curAccessToken());
this.mUnreadMsgApi.getUnreadMsg(mAccount.getUser().getIdstr(), new C04802());
return;
}
compareUnreadCount(new UnreadMsg());
stopSelf(); // 会执行onDestroy()
}
src/main/java/com/weico/international/manager/accounts/AccountsStore.java
public static Account getCurAccount() {
if (curAccount != null || getAccountSize() == 0) {
return curAccount;
}
return (Account) accountList.get(0);
}