Fork me on GitHub

工作笔记之Android记录

工作笔记

工作笔记用于记录Android开发过程中遇到的疑难点和难以解决的点,特此记录。

使用Jetpack的registerForActivityResult来启动带结果的Activity的时候#

java.lang.IllegalStateException: LifecycleOwner XXXActivity is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
需要提前声明好,不能再调用处初始化ActivityResultLauncher。

//java 
ActivityResultLauncher<Void> myLauncher= registerForActivityResult(object:ActivityResultContract<Void?, Result?>() {
    override fun createIntent(context: Context, input: Void?) =
        Intent(context, XXXActivity ::class.java)

    override fun parseResult(resultCode: Int, intent: Intent?): Result? {
        return when (resultCode) {
            RESULT_OK -> intent?.getParcelableExtra("info")
            else -> null
        }
    }
}, result -> {
    //handle result 
});
myLauncher.launcher(null);
//kotlin
private val myLauncher= registerForActivityResult(object:ActivityResultContract<Void?, Result?>() {
    override fun createIntent(context: Context, input: Void?) =
        Intent(context, XXXActivity ::class.java)

    override fun parseResult(resultCode: Int, intent: Intent?): Result? {
        return when (resultCode) {
            RESULT_OK -> intent?.getParcelableExtra("info")
            else -> null
        }
    }
}) {
//handle result 
}
myLauncher.launcher(null);

Kotlin的版本关系#

kotlin和room的使用需要进行调整,如kotlin 1.7.10 room 2.5.0kotlin 1.6.20 room 2.4.2的组合
activty:1.5.1支持的API版本要支持到API31才行
lifecycle-livedata-ktx:2.5.0 需要API 33

添加View 到视图的顶层#

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
//代码中

WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(
 ViewGroup.LayoutParams.MATCH_PARENT,
 ViewGroup.LayoutParams.MATCH_PARENT,
 WindowManager.LayoutParams.TYPE_PHONE,
 WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
 PixelFormat.TRANSLUCENT);
mLayoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;

mWindowManager.addView(yourView, mLayoutParams);

//6.0以上的时候 需要申请权限
if(!Settings.canDrawOverlays(this)){
// ask for setting
 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
 Uri.parse("package:" + getPackageName()));
 startActivityForResult(intent, 101); }

和其他APP分享#

Intent intent = ShareCompat.IntentBuilder.from(getContext())
 .setType("application/pdf")
 .setStream(uri)
 .setChooserTitle("Choose bar")
 .createChooserIntent()
 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Context.startActivity(intent);

Intent.FLAG_GRANT_READ_URI_PERMISSION对于高版本需要申请临时访问文件的权限。

更新阿里云推送#

因为最近测试发现问题,需要更新Vivo的推送SDK。看到了阿里云推送的SDK日志方案,更新到3.7.7版本。
编译出现AAPT: error: unexpected element <queries> found in <manifest>. 显示是适配Android 11出现的问题,这个问题需要更新AGP和Gradle版本。
但是更新的时候也需要更新AGP和gradle版本,升级了之后又发现Tinker不兼容3.5.4,提示必须初始化 tinker才行。这个时候又去看Tinker的github,发现有需要更新tinker的support为1.2.3才行。
我一般情况下使用的是AGP 3.3.0 VS Gradle 4.10.1AGP 3.5.4 VS Gradle 5.6.4AGP 4.1.0 VS Gradle 6.5这几个组合。

文件不可见的时候#

在文件处理完成之后,调用以下的内容来动态更新文件

//方式一
MediaScannerConnection.scanFile(this, new String[] {file.getPath()}, null, null);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(file)));
//方式二
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(mCurrentPhotoPath);
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);

ViewFlipper#

ViewFlipper是一个简化的视图动画翻转容器,它可以在两个或多个视图之间切换。ViewSwitcher和这个类似,但是ViewSwitcher仅支持两个View的切换。

ViewFlipper viewFlipper;//声明容器
//可以通过for循环添加多个View
viewFlipper.addView(view);//添加视图
viewFlipper.setFlipInterval(1000);//播放周期1秒
viewFlipper.setAutoStart(true);自动开始播放

Native崩溃类型#

kernel发出#

  • SIGFPE:除数为零
  • SIGILL:无法识别的CPU指令
  • SIGSYS:无法识别的系统调用(system call)
  • SIGSEGV:错误的虚拟内存地址访问
  • SIGBUS:错误的物理设备地址访问

用户进程发出#

  • SIGABRT:调用abort()/kill()/tkill()/tgkill()自杀,或被其他进程通过kill()/tkill()/tgkill()他杀。
  • 因为栈溢出、虚拟内存地址耗尽、FD耗尽、Flash空间耗尽也会导致Native的调用singal handler无法正常使用。

打包#

编译过程提示:Illegal class file: Class a is missing a super type. Class file version 53.Caused by: com.android.tools.r8.CompilationFailedException: Compilation failed to complete

app的build.gradle文件中
 minifyEnabled true
 multiDexEnabled true
混淆配置文件中 
-ignorewarnings
项目的Application中
 继承MultiDexApplication

gradle.properties
  android.enableR8 = true

密码规则#

英文字母大写、小写,数字及特殊符号构成,必须满足3种类型,8-20位

^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_]+$)(?![a-z0-9]+$)(?![a-z\\W_]+$)(?![0-9\\W_]+$)[a-zA-Z0-9\\W_]{8,20}$

Android常用#

    //应用商店
     //应用宝
    public static final String MARKET_APPLICATION_OF_TREASURE = "com.tencent.android.qqdownloader";
     //魅族应用商店
    public static final String MARKET_FLY_ME = "com.flyme.meizu.store";
     //华为应用商店
    public static final String MARKET_HUA_WEI = "com.huawei.appmarket";
    //酷派应用商店
    public static final String MARKET_COOLPAD = "com.yulong.android.coolmart";
     //oppo应用商店
    public static final String MARKET_OPPO = "com.oppo.markey";
     //vivo应用商店
    public static final String MARKET_VIVO = "com.bbk.appstore";
     //三星应用商店
    public static final String MARKET_SAMSUNG = "com.sec.android.app.samsungapps";
     //小米应用商店
    public static final String MARKET_XIAO_MI = "com.xiaomi.market";
     //百度手机助手
    public static final String MARKET_BAIDU = "com.baidu.appserch";
    //地图包名
     //百度地图
    public static final String MAP_BAIDU = "com.baidu.BaiduMap";
     //腾讯地图
    public static final String MAP_TENCENT = "com.tencent.map";
    //高德地图
    public static final String MAP_GAODE = "com.autonavi.minimap";

打包出现异常#

java.lang.NoSuchMethodError: No static method asAttributeSet(Lorg/xmlpull/v1/a;)Landroid/util/AttributeSet; in class Landroid/util/Xml; or its super classes (declaration of 'android.util.Xml' appears in /system/framework/framework.jar)

原因:xml解析异常;解决方案是在混淆文件中配置该类,防止被混淆了之后找不到。

-dontwarn org.xmlpull.v1.XmlPullParser
-dontwarn org.xmlpull.v1.XmlSerializer
-keep class org.xmlpull.v1.* {*;}

As安装Apk出现问题#

../build/outputs/apk/app-debug.apk does not exist on disk.
解决方案:

  • clean projects
  • rebuild Projects
  • Sync Projects with Gradle Files
  • Invalidate and restart
  • 清楚Android的配置 重新安装配置即可

键盘和布局问题#

  1. 方法一:在项目的AndroidManifest.xml文件中界面对应的里加 android:windowSoftInputMode="adjustPan|stateHidden"
  2. 方法二:在你的Activity中的oncreate中setContentView之前写上这个代码getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
  3. 方法三:把顶级的layout替换成ScrollView,或者说在顶级的Layout上面再加一层ScrollView的封装。这样就会把软键盘和输入框一起滚动了,软键盘会一直处于底部。

实用的API#

  • SystemClock.elapsedRealtime() 设备开机到现在的时间
  • System.currentTimeMillis() 当前时间,和系统时间有关联
  • Caused by: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake 网络连接出现问题,更换网络

提示Apache的类找不到#

java.lang.ClassNotFoundException:Didn't find class "org.apache.http.util.ByteArrayBuffer"

原因是使用了apache.http中的ByteArrayBuffer,但是Android高版本已经不使用apache.http,因此将ByteArrayBuffer 替换成ByteArrayOutputStream即可。

安装Apk#

//安装Apk意图
    private static Intent installApkIntent(Context context, File file) {
        if (file == null) return null;
        if (!file.exists() && !file.isFile()) return null;
        Intent intent = new Intent(Intent.ACTION_VIEW);
        String type = "application/vnd.android.package-archive";
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
        }
        Uri uri = AppPathUtil.getFileToUri(context, file);
        context.getApplicationContext().grantUriPermission(context.getPackageName(), uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(uri, type);
        return intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }
//Android O的时候需要申请安装权限
    /**
     * 跳转到设置-允许安装未知来源-页面
     * 注意这个是8.0新API
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public static void startInstallPermissionSettingActivity(Activity mActivity, String appId) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setData(Uri.parse("package:" + appId));
        mActivity.startActivity(intent);
    }
//两个整合在一起
  /**
     * App 安装
     *
     * @param activity 上下文
     * @param filePath 文件路径
     * @return 返回true 请开启安装未知应用来源的权限!或开启安装  false代表需要重新尝试
     */
    public static boolean installApk(Activity activity, String filePath) {
        if (null == filePath) {
            return false;
        }
        return installApk(activity, new File(filePath));
    }

    /**
     * App 安装
     *
     * @param activity 上下文
     * @param file     文件路径
     * @return 返回true 请开启安装未知应用来源的权限!或开启安装  false代表需要重新尝试
     */
    public static boolean installApk(Activity activity, File file) {
        if (null == file) {
            return false;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (!activity.getPackageManager().canRequestPackageInstalls()) {
                startInstallPermissionSettingActivity(activity, activity.getPackageName());
                Toaster.info(activity, "请开启安装未知应用来源的权限!");
                return true;
            }
        }
        Intent intent = installApkIntent(activity, file);
        if (null != intent) {
            activity.startActivity(intent);
            return true;
        }
        return false;
    }

AS打开项目的时候提示branch 142#

 Uninitialized object exists on backward branch 142

出现这个的原因是项目的JDK版本和AS的编译版本冲突,多次条件AS的编译版本即可。

AS运行包含NDK的项目#

A problem occurred configuring project ':app'. > java.lang.NullPointerException (no error message)

原因是AS查找本地NDK-Bundle出现问题,找不到NDK的配置导致。
在项目的配置中查找NDK的配置,如果不行可以进行手动配置,比如我的配置是

sdk.dir=D\:\\sdk
ndk.dir=D\:\\sdk\\ndk\\21.1.6352462

App的周期性弹窗#

最近的项目要求用户在发起过问题之后就会发送每隔15秒的一个弹窗入口来引导用户付款。能打开这个功能的前提时用户点击了咨询的入口,然后我这边就要打开这个功能。
这个需要现在回想起来有一个点没有确认清除,是15秒的倒计时的循环弹出还是只在主界面进行弹出。如果按照15的间隔开始弹出的话,我设计的是通过监控ActivityLifeCallback来触发弹窗,这个初衷导致我在后面出现了不少的坑。因为功能的入口在一个网页的界面,通过JS来告诉我啥时候启动。但是如果在这个网页中启动过了之后的话,在下一个界面中如果用户点击了支付的弹窗和支付完成的话是不能再次出现弹窗的,不然用户就会收到频繁的弹窗提示。我一开始就把路给走窄啦,其实只需要在JS告诉我启动了弹窗的功能之后,我就在这里开始循环触发弹窗就行啦,结果导致我周五和周一这两天都在弄这个逻辑,一方面是我把路走窄啦,一方面是开始设计的初衷在我的思维惯性里已经是最好的啦,其他的方案对我来说都不优雅。

ActivityLoopUtil {
  uiHandler;//初始化
 public static void startLoop(){
    if(剩余次数>0){
     //开启  
     uiHandler. sendMessageDelayed(CODE,1000*10);
  }
 }
public void dispatchMessage(Message msg) {
    if(CODE==msg.what){
      //判断当前是否满足弹窗的条件,不满足的话 再次发送延迟消息
      //满足的话就弹出,并在弹出的界面吧次数减一  
  }
}
 public static void sopLoop(){
    uiHandler.removeMessage(CODE);
 }

HTTP的Content-Type问题#

在一次和同事关于接口的问题中,发现了怎么使用都不行,对比了请求的数据之后发现,原来是后台设置了Content-Type中consumes是application/json;charset=Utf-8,而我这边传值的是application/json; charset=Utf-8两者中间就多了一个空格,因为这个问题我调试了很久,还一直以为是我网络库的问题,我问后台为什么要这么设置,不能修改吗?后台说不能,定死了。那我就很奇怪啦,怎么会有这种的类型呢,难倒我平时的设置不对吗?这次问题排查多亏了抓包,这个在PostMan中也没有发现这个问题,因为我在PostMan也是输入的和后台不一样。所以这个东西就很奇怪。通过抓包我还帮助IOS的一个同事解决请求总是失败的问题。熟悉抓包工具确实能解决前后台的小细节问题。反证后面的常用了抓包来解决问题,快速定位问题和解决问题才是作为一个开发人员的目标,至于你说你要当老板的话,也是要追求效率的。两个一点也不冲突,解决问题的能力一方面靠经验,一方面也考个人脑袋的灵活性。

作者:kevin2022

出处:https://www.cnblogs.com/kevin2022/p/15736090.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

你可以在这里自定义其他内容

posted @   KevinAt2022  阅读(495)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu