在开发过程中,遇到过各种各样的代码兼容问题,有些问题真的是让人头大。
这里做一个记录整理,各位网友有更多的兼容问题,欢迎留言,我好补充下来,给更多的网友做一个汇总,谢谢!
该博客持续更新!
1. android Q(10.0)无法获取到剪贴板的内容
官方链接:https://developer.android.com/about/versions/10/privacy/changes?hl=zh-cn
国内大家都会用搜狗、QQ这类第三方输入法,所以默认输入法就不用想了。目前正处于焦点的应用,经测试,在android 10.0以上的手机,从第三方应用复制回来之后,也会走onResume方法,但是直接在onResume方法获取剪贴板内容,就会得到空。那么按照官方的说明,我们应该是要获取应用的焦点,遗憾的是,搜遍文档,也没有看到系统提供的获得焦点的监听,所以这里建议大家在onResume方法的最后,做一个延迟加载来获取剪贴板的内容,至于延迟的时间,取决于布局的复杂程度(毕竟要布局加载完成,才能理解为应用获得了焦点),我这里是将时间设置为1秒。
Handler().postDelayed({
getClipboardContent() //在这里写了个方法获取剪贴板的内容
},1000)
2.android 8.0 以上版本开启service闪退
官方链接:https://developer.android.com/about/versions/oreo/android-8.0-changes.html#back-all
崩溃日志如下:
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground() at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:2220) at
android.os.Handler.dispatchMessage(Handler.java:109) at
android.os.Looper.loop(Looper.java:166) at
android.app.ActivityThread.main(ActivityThread.java:7555) at
java.lang.reflect.Method.invoke(Native Method) at
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469) at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)
解决办法:
//启动服务的时候判断一下版本
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
startForegroundService(Intent(this,StepService::class.java))
}else {
startService(Intent(this, StepService::class.java))
}
class StepService : Service() {
override fun onCreate() {
super.onCreate()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
//这里判断一下版本,8.0以后的,要创建一个通知
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
createNotificationChannel()
}
return super.onStartCommand(intent, flags, startId)
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(){
var mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
var id = "这里填包名"
var name = "xxx"
var description = "xxxxxx"
var importance = NotificationManager.IMPORTANCE_DEFAULT
var mChannel = NotificationChannel(id,name,importance)
mChannel.description = description
mNotificationManager.createNotificationChannel(mChannel)
var intent = Intent(this, WebActivity::class.java) //这里是点击这个通知去哪里
intent.putExtra("url", Contans.walkMoney)
intent.putExtra("id","")
intent.putExtra("icon","")
intent.putExtra("name","")
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
var pi = PendingIntent.getActivity(this,0,intent,0)
var notification = Notification.Builder(this).setContentTitle("xxx")
.setContentText("xxxxxx").setSmallIcon(R.mipmap.ic_launcher).setChannelId(id)
.setContentIntent(pi).setAutoCancel(true)
.build()
startForeground(1,notification)
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}
NOTICE: 经实际测试,并非所有8.0以上的手机都会闪退,即部分8.0以上的手机,不调用startForegroundService 也不会闪退; 同时做了上述修正代码之后,在一些8.0以上的手机,也并不会出现通知,猜测和国内厂商修改android源码有关
3.剪贴板ClipData闪退
崩溃日志:
java.lang.SecurityException: tp.defen.guard from uid 10935 not allowed to perform READ_CLIPBOARD
at android.os.Parcel.createException(Parcel.java:2087) at
android.os.Parcel.readException(Parcel.java:2055) at
android.os.Parcel.readException(Parcel.java:2003) at
android.content.IClipboard$Stub$Proxy.setPrimaryClip(IClipboard.java:293) at
android.content.ClipboardManager.setPrimaryClip(ClipboardManager.java:106) at
//这里因为隐私问题,屏蔽了对应的包名的崩溃日志
android.view.View.performClick(View.java:7275) at
android.view.View.performClickInternal(View.java:7227) at
android.view.View.access$3800(View.java:829) at
android.view.View$PerformClick.run(View.java:27920) at
android.os.Handler.handleCallback(Handler.java:883) at
android.os.Handler.dispatchMessage(Handler.java:100) at
android.os.Looper.loop(Looper.java:238) at
android.app.ActivityThread.main(ActivityThread.j$
根据崩溃日志,我去查了一下 tp.defen.guard这个app,名称叫 “强力杀毒卫士”,这个app可以禁止应用使用剪贴板,所以,无奈,只能在代码中加入try catch解决问题
try {
val mClipData: ClipData = ClipData.newPlainText("Label", "")
cm.primaryClip = mClipData
}catch (e:Exception){}
4.java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
崩溃日志
Error
06-04 10:17:14.414
18538
AndroidRuntime
java.lang.RuntimeException: Unable to start activity ComponentInfo{cn.binfenli.quanyika/cn.binfenli.quanyika.SplashActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
Error
06-04 10:17:14.414
18538
AndroidRuntime
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2884)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2945)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at android.app.ActivityThread.-wrap12(ActivityThread.java)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1655)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at android.os.Handler.dispatchMessage(Handler.java:102)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at android.os.Looper.loop(Looper.java:154)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at android.app.ActivityThread.main(ActivityThread.java:6457)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at java.lang.reflect.Method.invoke(Native Method)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1000)
Error
06-04 10:17:14.414
18538
AndroidRuntime
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:890)
Found activity ActivityRecord{26729e8 u0 (这里是包名)/.(崩溃的activity名) t445 f} in proc activity list using null instead of expected ProcessRecord{bcb3d65 18538:(包名)/u0a955}
这个崩溃日志真是让人头痛,没有任何有用的信息,最下面一行的崩溃信息,是打开APP的启动页,也就是一打开APP就崩溃了,而且没有指向任何一行代码。在更下面的日志里面,找到一行指向的代码,是空指针,指向的是自定义的BaseActivity的OnCreate方法的
super.onCreate(savedInstanceState)这行代码
我简直吐了,这行代码怎么可能会引发空指针呢。
这里特别说明一下,笔者遇到这个问题,来自于第三方平台的崩溃监测,崩溃的手机:魅族
型号:meizu 16x , android版本:7.1.1
由于公司没有这台手机,云真机也没有这台手机,所以先找了一些android版本7.1.1的手机测试,测试了小米、华为、oppo、vivo的android 7.1.1的手机,均正常。最后终于在腾讯的wetest找到一台型号叫meizu 15的android 7.1.1的手机,果然,打开就闪退,只能说:垃圾魅族!!!
最后没办法呀,只能从头找原因
1.先是写了个testactivity,不再继承BaseActivity,而是继承AppCompatActivity,界面还是使用当时闪退的启动页的xml,打开,闪退,说明并不是BaseActivity的问题
2. 重新写一个xml,里面就一个textview,显示hello world,testactivity用这个布局,打开,闪退,说明也不是启动页的布局的问题
3.去除AndroidManifest.xml里面application的android:name,不再使用自定义的Application,打开,正常!!!
找到了原因,就开始在Application里面找原因,最后终于找到
companion object{
private var instances = MyApplication()
}
居然是这行代码导致的错误,难道是不允许在这里赋值一个对象?于是改成这样
private var instances:MyApplication? = null
在Application的OnCreate方法里面赋值
override fun onCreate() {
super.onCreate()
instances=this
}
问题解决,该问题目前笔者所遭遇的,只出现在魅族手机,android 7.1.1的系统
5.java.lang.RuntimeException: Using WebView from more than one process at once with the same data directory is not supported.
错误日志
java.lang.RuntimeException: Using WebView from more than one process at once with the same
data directory is not supported. https://crbug.com/558377 at
org.chromium.android_webview.AwBrowserProcess.b(PG:11) at D5.m(PG:33) at C5.run(PG:2) at
org.chromium.base.task.TaskRunnerImpl.g(PG:11) at Xs.run(Unknown Source:2) at
android.os.Handler.handleCallback(Handler.java:883) at
android.os.Handler.dispatchMessage(Handler.java:100) at
android.os.Looper.loop(Looper.java:238) at
android.app.ActivityThread.main(ActivityThread.java:7827) at
java.lang.reflect.Method.invoke(Native Method) at
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:995)
这个错误发生在代码设置的targetSdkVersion >=28并且手机是9.0以上的机器,是官方android P(9.0)的行为变更,不允许多进程使用同一目录webview
解决办法:在app的application类OnCreate方法中加入代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val processName = getProcessName()
if (!"应用包名".equals(processName)) {
WebView.setDataDirectorySuffix(processName)
}
}
6.部分手机(android 10.0以上系统)无法拉起微信小程序
在测试的过程中,被反馈部分手机无法拉起微信小程序,网上找资料的时候,找到如下资料:
华为Android 10手机微信小程序无法调起的问题解决办法 - 程序员大本营
说的有两点问题:
1. 检查微信的悬浮窗权限是否开启
2. 检查微信是否有显示在其他应用上层的权限
经检查,拉不起微信小程序的手机,的确是没有开启相应的权限(不是每台手机都有上述两个权限,笔者的华为mate30是只有2的权限),开启权限之后,是能正常拉起小程序了。但这不能成为解决方案,因为我们不能引导用户去做这个操作,于是经过测试,在拉起小程序的地方,先拉起微信,就可以了。
try {
if(api!!.isWXAppInstalled){ //这个api是微信注册的返回,对象是IWXAPI
api!!.openWXApp() //判断是否有安装微信,有的话先拉起微信
}
//然后再拉起小程序
var obj = JSONObject(json)
var req = WXLaunchMiniProgram.Req()
req.userName = obj.optString("userName")
req.path = obj.optString("path")
req.miniprogramType = WXLaunchMiniProgram.Req.MINIPTOGRAM_TYPE_RELEASE
api!!.sendReq(req)
}catch (e:Exception){}
7.微信小程序左上角的返回按键或者自定义按钮调用返回事件,无法返回至APP,并且导致APP主进程被杀死
在第6点,APP拉起小程序之后,在对接到某个第三方的小程序时,发现对方的小程序和其它小程序不一样。它自定义了头部导航,左上角不是回到首页的图标,而是返回按键,在点击该按键时,无反应,但神奇的发现APP的进程不见了。
解决方案:AndroidManifest.xml文件中,找到 WXEntryActivity,添加启动模式
android:launchMode="singleTask"
其它任何模式都不行,必须是singleTask,具体原因暂不清楚
8.android12的机器,内部版本更新时提示解析包出现异常
这个很坑爹哈,一开始以为是代码的问题,网上各种搜,发现代码写法基本都一致的,没啥问题,代码如下:
val file = File(filePath)
val intent = Intent(Intent.ACTION_VIEW)
// 由于没有在Activity环境下启动Activity,设置下面的标签
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
if (Build.VERSION.SDK_INT >= 24) { //判读版本是否在7.0以上
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addCategory(Intent.CATEGORY_DEFAULT)
val contentUri = FileProvider.getUriForFile(context,
context.packageName + ".provider", file)
intent.setDataAndType(contentUri, "application/vnd.android.package-archive")
} else {
intent.setDataAndType(Uri.parse("file://$filePath"), "application/vnd.android.package-archive")
}
context.startActivity(intent)
权限和provider的配置都是有加的:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
file_paths的配置也都是OK的,这个网上的各种配置还有些许不同,我这一股脑的全部都给复制了过来
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--为了适配所有路径可以设置 path = "." -->
<external-path name="external_files" path="." />
<external-path name="tt_external_root" path="." />
<external-path name="tt_external_download" path="Download" />
<external-files-path name="tt_external_files_download" path="Download" />
<files-path name="tt_internal_file_download" path="Download" />
<cache-path name="tt_internal_cache_download" path="Download" />
<external-path
name="app_update_external"
path="/" />
<external-cache-path
name="app_update_cache"
path="/" />
</paths>
到此,我就死活找不出原因来了,最后通过各方排查,发现了区别,是有一台华为手机,系统版本是鸿蒙3.0.0,但实际内部基于的安卓版本是API 31,也就是android 12,就是这台机器会出现这个问题,其它相同的鸿蒙3.0.0的系统,API版本不是31的都没问题。最后终于找出原因:
var DOWNLOAD_FOLDER_NAME = Environment.DIRECTORY_DOWNLOADS
/** 就是这行代码导致的,配置的下载目录是用的手机默认的download目录,
* 解析出来的真实路径是:/storage/emulated/0/Download,而
* android 12不允许读取非应用内部的目录了,最后代码改成下面这个:
*/
var DOWNLOAD_FOLDER_NAME = KBaseApplication.getInstances().externalCacheDir?.path
/** 这里的KBaseApplication是我自定义的application,
* 各位看官如果是在activity里面是可以直接用externalCacheDir的,
* 或者你只要传递了context,也可以用context.externalCacheDir。
* 这个目录读取出来的真实路径是:/storage/emulated/0/Android/data/包名/cache,
* 它就可以在android 12正常读取了
*/
另外,我使用的是系统的 DownloadManager来进行下载的,如果你也是使用这个的话,那么还有一行代码要修改一下:
val request = DownloadManager.Request(
Uri.parse(DOWN_APK_URL))
//这里要使用setDestinationInExternalFilesDir,
//如果是使用setDestinationInExternalPublicDir的话,
//会报错: java.lang.IllegalStateException: Not one of standard directories
request.setDestinationInExternalFilesDir(this,DOWNLOAD_FOLDER_NAME,
File.separator+DOWNLOAD_FILE_NAME)