工作笔记
工作笔记用于记录Android开发过程中遇到的疑难点和难以解决的点,特此记录。
Kotlin的版本关系
kotlin和room的使用需要进行调整,如kotlin 1.7.10 room 2.5.0
和kotlin 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.1
、AGP 3.5.4 VS Gradle 5.6.4
、AGP 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的配置 重新安装配置即可
键盘和布局问题
- 方法一:在项目的AndroidManifest.xml文件中界面对应的
里加 android:windowSoftInputMode="adjustPan|stateHidden" - 方法二:在你的Activity中的oncreate中setContentView之前写上这个代码getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
- 方法三:把顶级的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的一个同事解决请求总是失败的问题。熟悉抓包工具确实能解决前后台的小细节问题。反证后面的常用了抓包来解决问题,快速定位问题和解决问题才是作为一个开发人员的目标,至于你说你要当老板的话,也是要追求效率的。两个一点也不冲突,解决问题的能力一方面靠经验,一方面也考个人脑袋的灵活性。