Android奇葩问题全纪录-(一)
1. android使用fragment制作的tab页,当主界面切换到后台,在系统内存不足的情况下,主界面被系统强制回收后又被重新create造成fragment重叠。
主界面的tab切换使用了fragment切换来实现,因此主activity继承自v7的AppCompatActivity,而AppCompatActivity继承自FragmentActivity
我们看下FragmentActivity中的onSaveInstanceState方法:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Parcelable p = mFragments.saveAllState(); if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); } if (mPendingFragmentActivityResults.size() > 0) { outState.putInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG,mNextCandidateRequestIndex); int[] requestCodes = new int[mPendingFragmentActivityResults.size()]; String[] fragmentWhos = new String[mPendingFragmentActivityResults.size()]; for (int i = 0; i < mPendingFragmentActivityResults.size(); i++) { requestCodes[i] = mPendingFragmentActivityResults.keyAt(i); fragmentWhos[i] = mPendingFragmentActivityResults.valueAt(i); } outState.putIntArray(ALLOCATED_REQUEST_INDICIES_TAG, requestCodes); outState.putStringArray(REQUEST_FRAGMENT_WHO_TAG, fragmentWhos); } }
在代码中有这么一段
Parcelable p = mFragments.saveAllState(); if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); }
FragmentActivity在onSaveInstanceState中保存了当前界面所有fragment的状态,这就导致了主界面activity在销毁后fragment被onSaveInstanceState保存了下来,在主界面Activity重新回到前台被系统重新创建的时候,原有的fragment的视图状态在FragmentActivity中被还原了回来,同时主界面Activity的oncreate方法又被重新执行了,于是导致了在已有fragment基础上重复创建了fragment。
下面是几种解决方案:
1.在oncreate中判断oncreate方法中传递过来的savedInstanceState是否为null,如果为null执行完整的fragment tab初始化工作,否则不执行初始化,思路代码如下:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState != null) { //界面正常情况下create时的逻辑 initTab(); } else { //界面在内存不足情况下被强制回收后重新create的逻辑 }}
2.这种方法属于懒人方法的一种,在oncreate中在super.oncreate执行前将saveInstance中的fragment状态全部置空,这样在Activity被重新创建的时候原有的fragment都不会被恢复状态,而是按照Activity的正常create执行:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState != null) { savedInstanceState.putParcelable("android:support:fragments", null); } super.onCreate(savedInstanceState);}
3.同样是懒人方法,重写onSaveInstanceState方法,注释对父saveInstanceState的调用,不过这么做会有很多副作用,系统自己实现的onSaveInstanceState就完全失去作用了,所以并不太推荐大家这么去做,参考代码:
@Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {// super.onSaveInstanceState(outState, outPersistentState); }
2. App打包成apk后在部分手机上安装,安装完成后直接点击系统安装界面上的打开按钮后点击home键到home页,再点击启动图标,应用重复启动(该问题只出现在第一次安装app的时候,之后退出app再重新运行,点击home,再点击启动图标不会出现该问题)
解决方案,在启动页增加代码判断,如果启动的Activity不是当前任务的根Activity则直接finish掉,代码如下:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { setTheme(R.style.AppTheme_NoActionBar); super.onCreate(savedInstanceState); //判断根Activity代码 if (!isTaskRoot()) { finish(); return; } }
3. 应用调用系统相机后,返回应用数据丢失,应用崩溃等
在手机上调用系统相机的时候,有很大的几率会导致内存不足从而调用相机的app或者app的调用相机的Activity界面被强制回收,所以调用相机的Activity重写onSaveInstanceState是非常必要的,在onSaveInstanceState方法中将局部变量保存起来,同时在onRestoreInstanceState方法中重新获取这些局部变量并做必要的逻辑处理。
4. 在部分老旧机型上,调用系统相机报Activity Not Found 异常
出现这种错误一般两种情况:
- 部分设备没有自带摄像头
- 部分设备没有sd卡
为了处理这两种情况,在启动相机的时候做了简单的判断:
private void showCamera() { String status = Environment.getExternalStorageState(); if (status.equals(Environment.MEDIA_MOUNTED)) { try { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); mTakePhotoUri = FileUtils.getOutputMediaFileUri(FileUtils.MEDIA_TYPE_IMAGE); mFilePath = mTakePhotoUri.getPath(); intent.putExtra(MediaStore.EXTRA_OUTPUT, mTakePhotoUri); startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE); } catch (ActivityNotFoundException e) { showShortToast(R.string.error_no_camera); } } else { showShortToast(R.string.error_no_sdcard); } }
5. 在Android 4.1等设备上使用EventBus报aused by: java.lang.ClassNotFoundException: Didn’t find class “android.os.PersistableBundle” on path: DexPathList
造成这个错误的原因一般是无意中重写了onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)这个方法,检查下自己的代码,使用onSaveInstanceState(Bundle outState)来处理你的代码逻辑
6. 引入图片框架fresco后,出现is 32-bit instead of 64-bit的错误
因为项目中引用了其他第三方的so库,但是却没有arm64-v8目录的so文件,而fresco的库文件中却创建了arm64-v8的目录
按照Android查找so文件的规则,Android设备会优先查找对应的cpu架构目录,如果查找不到对应的目录,会到armeabi目录下兼容方式调用so文件,但是如果能查找到对应的目录,则只会调用该目录下的so库,如果已有的so库没有对应目录下的so文件,那么就会因为找不到对应目录so库从而造成错误。
那么我的项目并没有创建arm64-v8的目录,为什么会出错呢,经过检查后发现,原来我们的fresco库为了兼容性,默认创建了对应的arm64-v8的库目录,而我的项目中其他的so库并没有对应的arm64-v8架构的,所以导致app在安装到arm64位设备上的时候会到arm64-v8的so目录下去查找,从而报了is 32-bit instead of 64-bit的错误。
解决方案:
- 为项目已经引用的so库添加对应arm64-v8架构的so库。
在gradle的defaultConfig中设置
ndk { // 设置支持的 SO 库构架,注意这里要根据你的实际情况来设置 abiFilters 'armeabi' , 'x86'}
更多相关文章
- android suspend and resume
- 细读《深入理解 Android(安卓)内核设计思想》(一)进程间通信与同步
- Android相机启动crash错误排查
- android自定义字体和程序启动时的加载页面
- 如何让自己写的apk获得系统权限
- Android应用获取系统属性
- Android(安卓)类似美团的选择城市界面
- 【 Android(安卓)10 四大组件 】系列 -- Service 的 “ 启动流程
- android使用AIDL跨进程通信(IPC)