那些年的Android开发经验记录

Android Studio 新版Logcat 从惊艳到放弃

AS总算更新了这个logcat了,原来的logcat真是使用起来贼难受,动不动过滤就失效,或者日志不打印,新版的logcat初步使用下来,那是贼舒服,先上一张界面图



  • 一眼看去,可以看到主要改变有三点:

一个是多窗口支持,可以同时打开多个logcat窗口,这对于分析繁多复杂的日志十分有帮助,并且每个窗口又可以复制出多个子窗口,对于分析对比当前窗口不同位置的日志是非常有帮助的;

一个是过滤方式,改成了类似于Github里的搜索过滤语法,可以多个语法组合进行过滤,自定义更强更灵活了;

一个是打印格式,优化了多行日志的打印,日志排版也更清新,也支持了颜色高亮。并且打印信息也可以自定义是否显示

接下来就详细介绍一下这个过滤语法

过滤语法

package:mine 默认语法,官方原版解释:PIDs for the local app project,不太理解这个解释,不过根据使用,应该就是当前logcat窗口所在项目的log

package:my-package-ID 也就是根据包名过滤

tag:my-tag 根据tag进行过滤

level:ERROR 根据日志等级进行过滤

-tag:exclude-this-tag 在前面加了个'-',排除指定的TAG

tag~:regex-tag 在后面加了个'~',支持正则过滤

-tag~:exclude-this-regex-tag 这个自然就是支持正则过滤排除指定TAG

-package:exclude-this-package 同上

package~:regex-package 同上

-package~:exclude-this-regex-package 同上

总结

更新的内容挺好的,不过该有的问题依然存在,还是时不时的不显示logcat,需要重新插拔一下手机才行

后续

放弃了,日志更新不及时,删除也不及时,有些日志还没捕获到,比原来的logcat还垃圾,基本问题没解决,上层功能做的再花俏有什么卵用

退回旧版

Settings-> Experimental -> [x] Enable new Logcat tool window 取消选择 -> 重启 Android Studio

Android P以上隐藏API(非SDK接口)反射限制解决方案

相关限制说明参考:针对非 SDK 接口的限制

FreeReflection github

RestrictionBypass github

  • Java层双重反射 (Android P)
  • native层双重反射 (通过AttachCurrentThread隐藏未知调用者) (Android Q+) (将被修复)

AndroidHiddenApiBypass github

  • 借用Unsafe重写反射代码(Android P+)

总结

目前本人仅使用了FreeReflection,不过由于时常报native层闪退异常已弃用(猜测是ClassLoader置空方案造成的,双重反射应该只在Android P生效,native层修改Flag未用到),其他暂未使用,不做评价,不过理论上比较看好AndroidHiddenApiBypass,纯Java,兼容性肯定是最好的,由于Unsafe涉及到native层访问,恐怕不太好封,限制访问部分内存区域?还是直接限制访问反射相关结构体?RestrictionBypass的native层双重反射应该也容易被检测修复

Android 10以上无法隐藏应用图标问题探究及解决方案

Android 10之前的隐藏方式:

java 复制代码
con.getPackageManager().setComponentEnabledSetting(new ComponentName(con,activityAliasName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);

Android 10之后,该隐藏图标方式失效,隐藏成功后依然会在桌面上保留其图标,点击后会跳转到应用详情页。具体查看官方API更新说明:getActivityList

那这就很难受了,在我看来,目前设计的并不太合理,能不能隐藏应该由用户决定,而不是强制不允许隐藏,不过目前官方也设置了控制开关,可以修改系统设置来去掉该限制,只不过需要adb或能修改系统设置的APP或ROOT权限才能修改。接下来就是具体分析了。

具体分析

  • getActivityList
java 复制代码
//frameworks/base/core/java/android/content/pm/LauncherApps.java
    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
        logErrorForInvalidProfileAccess(user);
        try {
            return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
                    packageName, user), user);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }
  • getLauncherActivities
java 复制代码
//frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.java
@Override
public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(
        String callingPackage, String packageName, UserHandle user) throws RemoteException {
    //查询包含ACTION_MAIN和CATEGORY_LAUNCHER的activity
    ParceledListSlice<LauncherActivityInfoInternal> launcherActivities =
            queryActivitiesForUser(callingPackage,
                    new Intent(Intent.ACTION_MAIN)
                            .addCategory(Intent.CATEGORY_LAUNCHER)
                            .setPackage(packageName),
                    user);
    //检测系统设置(系统设置控制开关控制是否走新版限制逻辑)
    if (Settings.Global.getInt(mContext.getContentResolver(),
            Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
        return launcherActivities;
    }
    //没有找到入口activity
    if (launcherActivities == null) {
        // Cannot access profile, so we don't even return any hidden apps.
        return null;
    }

    final int callingUid = injectBinderCallingUid();
    final long ident = injectClearCallingIdentity();
    try {
        //托管配置文件可以隐藏APP
        if (mUm.getUserInfo(user.getIdentifier()).isManagedProfile()) {
            // Managed profile should not show hidden apps
            return launcherActivities;
        }
        //设备管理员可以隐藏APP
        if (mDpm.getDeviceOwnerComponentOnAnyUser() != null) {
            // Device owner devices should not show hidden apps
            return launcherActivities;
        }

        final ArrayList<LauncherActivityInfoInternal> result = new ArrayList<>(
                launcherActivities.getList());
        if (packageName != null) {
            // If this hidden app should not be shown, return the original list.
            // Otherwise, inject hidden activity that forwards user to app details page.
            //可能是如果大于0,表示有有效的启动入口,则不用注入隐藏的APP详情页入口
            if (result.size() > 0) {
                return launcherActivities;
            }
            final ApplicationInfo appInfo = mPackageManagerInternal.getApplicationInfo(
                    packageName, /* flags= */ 0, callingUid, user.getIdentifier());
            //根据APP信息进行判断是否注入隐藏APP详情页入口
            if (shouldShowSyntheticActivity(user, appInfo)) {
                LauncherActivityInfoInternal info = getHiddenAppActivityInfo(packageName,
                        callingUid, user);
                if (info != null) {
                    result.add(info);
                }
            }
            return new ParceledListSlice<>(result);
        }
        final HashSet<String> visiblePackages = new HashSet<>();
        for (LauncherActivityInfoInternal info : result) {
            visiblePackages.add(info.getActivityInfo().packageName);
        }
        final List<ApplicationInfo> installedPackages =
                mPackageManagerInternal.getInstalledApplications(/* flags= */ 0,
                        user.getIdentifier(), callingUid);
        for (ApplicationInfo applicationInfo : installedPackages) {
            if (!visiblePackages.contains(applicationInfo.packageName)) {
                if (!shouldShowSyntheticActivity(user, applicationInfo)) {
                    continue;
                }
                LauncherActivityInfoInternal info = getHiddenAppActivityInfo(
                        applicationInfo.packageName, callingUid, user);
                if (info != null) {
                    result.add(info);
                }
            }
        }
        return new ParceledListSlice<>(result);
    } finally {
        injectRestoreCallingIdentity(ident);
    }
}
  • shouldShowSyntheticActivity
java 复制代码
//frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.java
private boolean shouldShowSyntheticActivity(UserHandle user, ApplicationInfo appInfo) {
    //没有app信息,是系统APP或者更新的系统APP,则不添加隐藏详情页入口
    if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
        return false;
    }
    //是管理员APP也不用添加该入口
    if (isManagedProfileAdmin(user, appInfo.packageName)) {
        return false;
    }
    final AndroidPackage pkg = mPackageManagerInternal.getPackage(appInfo.packageName);
    if (pkg == null) {
        // Should not happen, but we shouldn't be failing if it does
        return false;
    }
    // If app does not have any default enabled launcher activity or any permissions,
    // the app can legitimately have no icon so we do not show the synthetic activity.
    return requestsPermissions(pkg) && hasDefaultEnableLauncherActivity(
            appInfo.packageName);
}

根据以上源码,可以发现该机制有个全局设置开关,并且有多种情况可以不受该机制限制。因此总结出如下解决方案:

  • 若是你的APP有adb权限或ROOT权限,可以直接修改这个全局开关,修改命令如下:
bash 复制代码
adb shell settings put global show_hidden_icon_apps_enabled 0
  • 或者你的APP有设备管理员权限
  • 或者你的APP是系统APP
  • 或者你的APP没有任何默认启动入口
  • 或者你的APP没有请求任何权限
  • 或者你的APP涉及到Managed profile(这个具体不太清楚怎么用)
  • 或者你的APP是Xposed模块,可以直接注入系统服务拦截相关API,或者注入可以修改系统设置的APP(比如:设置)直接修改这个开关
  • 或者你是系统源码开发者,可以直接修改该处代码即可
  • 其它更多还未探索出的方式(比如利用某些逻辑漏洞)...

参考

Android Studio gradle 没依赖成功 但不报错 只有调用时会报红

可以单独运行一下依赖任务,就会出现错误信息了

Empty SMPTE 2094-40 data 报错问题

搜索源码定位到打印日志位置:SharedMetadata#151

临时解决方案

借用AS 新版Logcat 过滤语法进行过滤即可,过滤语法如下

-tag:gralloc4 

即排除掉tag为gralloc4的日志

【远程USB】VirtualHere 指定IP(Android 远程开发优雅连接调试设备)

一般我们Android远程调试一般直接是adb connect ip:port,但是呢,这个很不方便,你还需要配置wifi USB调试,但是远程USB就不一样了,连接远程设备就像连接本地设备一样操作,直接adb device就可以看见设备了,不用什么adb connect和wifi USB调试。

而远程USB工具,当然就推荐VirtualHere了,VirtualHere使用起来真香,局域网内,直接安装运行就行了,不需要任何操作(当然了,试用版同一时间仅限一台设备,不过对于个人使用,这足够了,需要时再切换即可,反正又不绑定设备),但互联网的话就不能这么操作了,毕竟互联网这么多IP,它怎么知道是哪个对不对,所以需要我们手动指定IP,正文开始...


服务端若是在服务器运行的,那就不用动了,若是在本地运行的话,就需要做内网穿透了,这里推荐FRP,简单方便开源,具体这里就不展开讲了;

然后在客户端中,在USB Hubs上右键菜单选择Specify Hubs...

然后点击add添加,在新窗口输入你的ip和port即可

之后等待USB hubs下出现新设备即可

若是出现问题,可以通过

bash 复制代码
telnet ip port

命令,看看是否可以正常连接,若不能正常连接,检查防火墙是否拦截了

Frida使用技巧

命令行

Frida命令

  • 指定IP和Port:-H 127.0.0.1:27042
  • 强制重启应用:-f
  • 重启后直接运行应用:--no-pause
  • 注入指定包名(必须):com.xxxxx.xxx
  • 注入指定进程ID(与上面二选一):pid
  • 调试js代码:--debug --runtime=v8

具体参考Here,其中代码方式开启调试测试不太管用,具体待分析

  • 指定要加载的js文件路径(必须):-l path_to_js

接入Google Admob

官方Demo

Here

填坑

  • 1.The ad request was successful, but no ad was returned due to lack of ad inventory
    参考Here

1.未关联应用商店应用,在应用设置里添加应用ID,也就是包名,且在google play,或者其支持的应用商店上架

2.填充率低,采用中介模式,具体参考Here

2.1.查看国内支持的广告源,参考Here

  • 2.需要上车才能显示Ad

参考

App支付功能实现及踩坑

支付宝支付实现

1.正规支付实现

前提条件:

1. 企业或个体工商户可申请,且账号通过支付宝实名认证审核
2. 需提供真实有效的营业执照,且支付宝账户名称需与营业执照主体一致;
3. 提供APP名称或产品说明文档,开发者与支付宝账户名称不一致需提供开发合作协议。
(您可以提供产品介绍文档或者手机demo .apk .doc .docx .pdf格式,内容要求包含产品说明、接口使用场景、资费说明等(文件大小不超过5M)如果已经在appstore、googleplay、豌豆荚上架,可不提供文档demo,直接提供应用名称)

现在企业和个体工商户的办理,某宝应该都可以搜到,我咨询了卖家,说是需要对公账户,

对公账户具体意思呢,卖家就说是上面第二个条件.

还是不太明白,我又上网百度了下:

看来百度,也百度不到什么,试试用谷歌吧

终于让我找到了,看来还是谷歌大法好,百度真是越做越垃圾了!

看来要对公账号,就等于要申请一个公司啊,那还是算了,想起那一大堆文件和流程就害怕!只能等待以后有一定资金了直接某宝找人代办吧

2.非正规实现

1.还是使用个人支付账号,然后服务端后台(或个人电脑端)不断监听付款记录,当匹配到特定备注的支付记录时就进行相应处理!

2.使用第三方代理支付,就是把支付交给已经得到支付宝认证的第三方支付商,这样的话的确是最方便的,不做任何处理就可以得到支付宝的正规支付功能,缺点就是肯定会收手续费了,当然收手续费很正常,毕竟人家提供了服务嘛,另一个缺点就是这个服务商是否可信,毕竟人家后台怎么处理你根本不知道,同样的这种服务商都一个共同的最大的缺点---不稳定,就是一个开发者的App支付若是被投诉,肯定会影响到这个服务商,在支付宝那边就相当于这个服务商被投诉,若投诉多了,那这个服务商也就挂了!

微信支付实现

1.正规实现

同样需要企业认证,其开发者认证也不是很容易

仅认证个开发者就要300元,还仅是一年的!

还是需要企业,算了,放弃!

2.非正规实现

还在研究中...

参考

1.支付宝收款流程

DcseTBS获取设备ID原理

这款工具的就不说了,这不是文章的重点,今天我们要研究一下他的获取设备唯一ID的原理。

其实它获取设备唯一ID的方法跟别人的没啥区别,

实现代码:

TelephoneManager tm=TelephoneManager.getSystemService(Context.TELEPHONE_SERVICE);
tm.getDeviceId();

代码非常的简单,所以破解它也非常的简单,用xposed模块hook即可修改,或者直接反编译修改也可以。

但重点不在这里,而是破解之后的情况,它的每个设备ID不充值是仅能使用一天的,所以我破解它的方法是让他每次返回随机设备ID,这样不就可以破解了,可是事实不是这样的,并没有破解(其实后来才知道已经破解了),每次打开显示错误,真是让我纠结了半天,以为他是用了反射或者通过其他方式获取了设备ID,或者把设备ID存储在了内存卡,通过我的验证,最后发现都没有,这就让我懵逼了,难道使用了我不知到的方法,就在我准备放弃的时候,我想到了一个被我忽略的地方,那就是我的网络IP地址,并且发现有时候错误的信息并不一样,到最后我终于发现了问题的关键点

原来是当它获取到新的设备ID时,并不会第一时间就出现试用信息,而是出现错误信息,并让你重启,正常在没破解的情况下,在第二次进入时就会出现试用信息了,但是当我们把它破解之后,每次获取的都是随机的设备ID,也就是当第二次重启时,又获取的是新的设备ID,这是又需要再重启一次,可是再获取一次又是新的设备ID,这样造成的结果就是永远不会出现试用信息!

既然明白了问题,那破解也就简单了,只要在每次过了试用期后,再返回一个新的设备ID即可!

真是一个非常巧妙地防止被破解的方法!

破解后的截图

不要频繁重置ID,一天一次即可,设置了新的ID后直接点击"明天再提示",就会等到明天这个时候会自动提示更换新ID,这时候再设置一个新ID即可。

破解后的APK

看有这多人要,我就直接发出来吧!

下载地址:https://download.csdn.net/download/qq_26914291/10383983

【注】该Apk仅供研究测试用,用作它用造成的一切损失一概与本人无关 !

总结

1.破解遇到问题,一定要仔细观察错误的信息,或许原因就藏在那些之中!

2.其实往往看似复杂的东西其实很简单!

代码混淆

初级混淆

使用proGuard进行代码混淆

1.参考教程:Android开发者指南(9) ------ ProGuard

2.混淆模板:Android proguard-rules.pro 混淆模板

3.[Android 代码混淆 选项说明(https://blog.csdn.net/jjwwmlp456/article/details/44977721)

几个可以增加混淆强度的命令:

#将所有类放到跟目录下
-flattenpackagehierarchy

#使用唯一的类成员名称
-useuniqueclassmembernames

几个可以增加混淆强度的字典

1.https://github.com/ihbing/AndroidObfuseDictionary

几个使用时需要注意的命令:

1.-assumenosideeffects使用时一定不要加-dontoptimize才起作用

# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
-assumenosideeffects class android.util.Log {
   public static int v(...);
   public static int i(...);
   public static int w(...);
   public static int d(...);
   public static int e(...);
}

[注]

1.eclipse只有将项目导出时才会进行混淆

2.1 proguard.config=proguard-project.txt//采用相对路径配置,直接读取项目根目录下proguard-project.txt文件

2.2 proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt//采用绝对路径

使用proGuard-GUI进行手动混淆

使用AndResGuard进行资源混淆

参考教程:AndResGuard集成笔记

增加7z压缩

参考教程:Android项目集成AndResGuard资源混淆两种方式

参考教程:https://github.com/shwenzhang/AndResGuard/blob/master/doc/how_to_work.zh-cn.md

注意点

1.如何打开Gradle中的Task任务?

进阶混淆

使用StringFog对字符串Base64加密

参考教程:https://github.com/MegatronKing/StringFog

将字符串混淆成byte字节码

如下图

参考教程:https://github.com/ihbing/obfuseSmaliText

高阶混淆

修改proGuard工具的混淆字符

[注]AndroidStudio的自身proGuard混淆工具目录D:\Program Files\Android\Android Studio\gradle\m2repository\net\sf\proguard\proguard-base\D:\Program Files\Android\Android Studio\gradle\m2repository\net\proguard-base\

用无效代码对抗jadx&jd-gui

 //添加无效代码,对抗jadx & jd-gui
        boolean qwerty21345hjdnjd = false;
        while (qwerty21345hjdnjd) {
            switch (1) {
                case 1:
                    while (qwerty21345hjdnjd) {
                        try {
                            Throwable throwable=new Throwable();
                            Throwable cause = throwable.getCause();
                        } catch (NullPointerException e) {
                        } finally {
                        }
                    }
                    break;
            }
        }

参考

1.Android资源混淆工具

2.proGuard混淆笔记

3.Android代码混淆技术总结(一)

4.apk在线反混淆

相关推荐
晨曦_子画21 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
孤客网络科技工作室43 分钟前
AJAX 全面教程:从基础到高级
android·ajax·okhttp
Mr Lee_2 小时前
android 配置鼠标右键快捷对apk进行反编译
android
顾北川_野2 小时前
Android CALL关于电话音频和紧急电话设置和获取
android·音视频
&岁月不待人&3 小时前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
Winston Wood5 小时前
Android Parcelable和Serializable的区别与联系
android·序列化
清风徐来辽5 小时前
Android 项目模型配置管理
android
帅得不敢出门5 小时前
Gradle命令编译Android Studio工程项目并签名
android·ide·android studio·gradlew
problc6 小时前
Flutter中文字体设置指南:打造个性化的应用体验
android·javascript·flutter
帅得不敢出门16 小时前
安卓设备adb执行AT指令控制电话卡
android·adb·sim卡·at指令·电话卡