【坑】坑记录

本文记录了Android开发中的一些常见问题,包括LayoutInflater加载布局时宽高失效的原因、Service启动Activity失败的解决、启动时白屏黑屏的处理、FragmentStatePagerAdapter导致的数据重复加载、Activity的onCreate显示问题、Broadcast IntentFilter的优先级、onCreate更新UI的限制、ObjectAnimator对自定义Bean的支持、SharedPreference导致的UI阻塞以及Fragment Crash的情况分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、LayoutInflater加载布局导致宽高失效

现象:

使用LayoutInflater加载布局时,第二个参数root如果为null,通过再addView添加该布局,会导致布局内部设置的宽高失效;如果root不为空,而且attachToRoottrue,则不会失效

原因:

      if (root != null) {
            //1、如果root不为null,创建布局参数(因为给addView传的参数都是params)
            //所以不管attachToRoot是否为空,布局View都被设置了LayoutParams
            params = root.generateLayoutParams(attrs);
            if (!attachToRoot) {
                //标识1
                temp.setLayoutParams(params);
           }
        }
        if (root != null && attachToRoot) {
            //2、root不为空,attachToRoot为true:调用addView,内部也会调用
            //if (!checkLayoutParams(params)) {
            //    params = generateLayoutParams(params);
            //}
            //也就是将从外部传进来的params原封不动的设置给View,效果同标识1
            root.addView(temp, params);
        }
        if (root == null || !attachToRoot) {
            //如果root为空,则不会对view设置任何布局参数
            result = temp;
        }

1、root为空 或者attachToRoot 为 false:不会为View设置任何的布局参数,此时设置view宽高是无效的
2、root不为空 同时attachToRoot 为 false:会为View设置一个来自ViewGroupLayoutParams,属性为wrap_content,因此此时设置view宽高是生效的
3、root不为空 同时attachToRoot 为 true:调用addView,而addView内部也会为View设置一个布局参数(来自于设置进addViewparams,原封不动),因此此时设置view宽高是生效的

总结:

不论是在inflate时设置true,还是设置false然后手动addView,源码都是调用的addView,但是是实现不同的addViewrootnull的话就会导致paramsnulladdView会根据params方法决定要不要调用ViewGroup本身的generateDefaultLayoutParams,这个方法生成的布局是wrap_content的,也就能解释为什么root为空时布局文件的宽高不生效


2、通过Service启动Activity失败

现象

通过AIDL从客户端启动服务端的Activity失败

原因

Service调用startActivity调用的是ContextImplstartActivity,而Activity是自己重写了startActivity方法ContextImpl启动Activity必须要有Intent.FLAG_ACTIVITY_NEW_TASK

    @Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        final int targetSdkVersion = getApplicationInfo().targetSdkVersion;

        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && (targetSdkVersion < Build.VERSION_CODES.N
                        || targetSdkVersion >= Build.VERSION_CODES.P)
                && (options == null
                        || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                            + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                            + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }

3、部分手机启动时会有一闪而过的白屏或者黑屏

原因

windowDisablePreview属性代表窗口的预览动画

解决

windowDisablePreview禁用这个属性就可以了


4、使用FragmentStatePagerAdapter导致重新加载数据

现象

使用FragmentStatePagerAdapter导致ViewPager数量多内存不足时Fragment数据重新加载

原因

  • FragmentPagerAdapterdestroyItem调用的是detach,仅仅是在页面上让fragmentUI脱离ActivityUI,但是fragment仍然保存在内存里,并不会回收内存。
  • FragmentStatePagerAdapterdestroyItem调用的是remove,会remove之前加载的fragment从而将内存释放掉。
	//--------------FragmentPagerAdapter-------------------//

    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        this.mCurTransaction.detach((Fragment)object);
    }

	
	//--------------FragmentStatePagerAdapter-------------------//
	
	    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment)object;
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        while(this.mSavedState.size() <= position) {
            this.mSavedState.add((Object)null);
        }

        this.mSavedState.set(position, fragment.isAdded() ? this.mFragmentManager.saveFragmentInstanceState(fragment) : null);
        this.mFragments.set(position, (Object)null);
        this.mCurTransaction.remove(fragment);
    }

总结

  • 需要加载的页面较少且每个页面的数据相对变化较少的时候应当使用FragmentPagerAdapter
  • 需要加载的页面较多,并且每个页面的数据量比较大或者数据经常变化,占用内存较多的时候应当使用FragmentStatePagerAdapter

5、继承了Activity并重写了onCreate发现不显示

原因

onCreate有两个方法:

  • onCreate(Bundle savedInstanceState)(这是我们平常用的)
  • onCreate(Bundle savedInstanceState, PersistableBundle persistentState)

总结

PersistableBundle:用于保存异常状况下关闭时Activity中的数据(手机由于过热,没电或者第三方定制Rom由于卡顿而异常关机的情况),他实际上是一种数据持久化的Activity(摘自网络)
还有另外两个生命周期也更新了这个方法:

  • public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState)
  • public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState)

使用:
android:persistableMode="persistAcrossReboots"


6、Broadcast中IntentFilter优先级

现象

只要设置了setPriority,用sendBroadcast也可以让优先级生效(有序),但是不能通过abort去截断,要截断只能用sendOrderedBroadcast

总结

所以sendBroadcastsendOrderedBroadcast的本质区别是能否中断广播的传输


7、onCreate不能更新UI

现象

onCreate更新ui不生效

原因

invalidaterequestLayout最终都会调用checkThread去检查当前线程是不是创建ViewRootImpl的线程
ViewRootImpl的创建是在ActivityThread # handleResumeActivity里面的wm.addView,也就是在onResume后才会添加WindowmakeVisible

1、引申出另外一个问题:子线程能不能更新UI?
我感觉应该是可以的,只要在子线程addView,创建了ViewRootImpl就可以

2、引申第二个问题:为什么checkThread不会报错
因为在invalidateInternal里面只有p!=null才会调用p.invalidateChild,所以根本没有调用到ViewRootImpl里面去

3、开启硬件加速后,onDescendantInvalidated为什么可以注释掉checkThread,因为RenderThread不是主线程而是渲染线程?


8、ObjectAnimator可以使用自定义Bean修改属性

现象

定义一个JavaBean,可以通过ObjectAnimator去修改属性(虽然知道ObjectAnimator继承自ValueAnimator,但一直以为只有ValueAnimator才可以)

	//property放具体属性
	ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(JavaBean,"property",0,1);

9、SharedPreference导致UI线程阻塞

之前用sp的时候发现有些时候有点卡顿,后来读源码才知道原来是因为getXXX的时候,如果sp还没读取完整个xml文件的话,是会执行wait

    SharedPreferencesImpl(File file, int mode) {
        mFile = file;
        mBackupFile = makeBackupFile(file);
        mMode = mode;
        mLoaded = false;
        mMap = null;
        mThrowable = null;
        //主要就是在这里,开启了一个线程去读取文件
        startLoadFromDisk();
    }

    @UnsupportedAppUsage
    private void startLoadFromDisk() {
        synchronized (mLock) {
            mLoaded = false;
        }
        new Thread("SharedPreferencesImpl-load") {
            public void run() {
                loadFromDisk();
            }
        }.start();
    }

	@Override
    @Nullable
    public String getString(String key, @Nullable String defValue) {
        synchronized (mLock) {
            awaitLoadedLocked();
            String v = (String)mMap.get(key);
            return v != null ? v : defValue;
        }
    }

	    @GuardedBy("mLock")
    private void awaitLoadedLocked() {
        while (!mLoaded) {
            try {
            	//在这里执行了wait
                mLock.wait();
            } catch (InterruptedException unused) {
            }
        }
        if (mThrowable != null) {
            throw new IllegalStateException(mThrowable);
        }
    }

10、Fragment Crash

Fragment如果自己实现了带参构造,一定要有默认的空构造,否则会crash(instantiate)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值