那些年的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在线反混淆

相关推荐
Yeats_Liao7 分钟前
Spring 定时任务:@Scheduled 注解四大参数解析
android·java·spring
雾里看山2 小时前
【MySQL】 库的操作
android·数据库·笔记·mysql
水瓶丫头站住10 小时前
安卓APP如何适配不同的手机分辨率
android·智能手机
xvch11 小时前
Kotlin 2.1.0 入门教程(五)
android·kotlin
xvch15 小时前
Kotlin 2.1.0 入门教程(七)
android·kotlin
望风的懒蜗牛15 小时前
编译Android平台使用的FFmpeg库
android
浩宇软件开发15 小时前
Android开发,待办事项提醒App的设计与实现(个人中心页)
android·android studio·android开发
ac-er888816 小时前
Yii框架中的多语言支持:如何实现国际化
android·开发语言·php
苏金标17 小时前
The maximum compatible Gradle JVM version is 17.
android
zhangphil17 小时前
Android BitmapShader简洁实现马赛克,Kotlin(一)
android·kotlin