我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android性能优化之ViewPagers + Fragment缓存优化

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android性能优化之ViewPagers + Fragment缓存优化

前言

大家看标题,可能会有点儿懵,什么是ViewPagers,因为在很久之前,我们使用的都是ViewPager,但是现在更多的是在用ViewPager2,因此用ViewPagers(ViewPager、ViewPager2)来代替两者,主要介绍两者的区别。

ViewPagers嵌套Fragment架构,在我们常用的App中随处可见,抖音的首页、各大电商app首页(淘宝、京东、拼多多)等,通过左右滑动切换Tab;但因为ViewPager的预加载机制存在,

我们先看下ViewPager的源码:

public void setOffscreenPageLimit(int limit) {
    if (limit < DEFAULT_OFFSCREEN_PAGES) {
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
                + DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    }
    if (limit != mOffscreenPageLimit) {
        mOffscreenPageLimit = limit;
        populate();
    }
}

当我们设置offscreenPageLimit(离屏加载)的数值时,我们可以看到limit的值是有限制,不能小于DEFAULT_OFFSCREEN_PAGES

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

那么就意味着ViewPager默认支持预加载,我们看下面这张图

如果红色区域默认为首页,根据ViewPager默认预加载的阈值,那么左右两边的页面同样也会被加载,如果有网络请求,也就是说,我们没有打开左边的页面,它已经默认进行了网络请求,这种体验是非常差的,因为会在暗地里消耗流量。

理想情况下,我们需要的是打开某个页面的时候才去加载,这里就需要通过懒加载的方式优化。

1 ViewPager懒加载优化

1.1 ViewPager的缓存机制

很多时候,我们在使用Fragment的时候,发现打开过的页面再回来,页面没有重建刷新,很多人觉得是Fragment是有缓存的,其实并不是Fragment有缓存,而是ViewPager具备缓存能力;

如果有小伙伴使用过单Activity + 多Fragment架构的时候就会发现,打开过的页面再次返回的时候,Fragment会被重建,所以两种架构都有利弊,关键看我们怎么选择,下面我们看下ViewPager的缓存机制。

public void setAdapter(@Nullable PagerAdapter adapter) {
    if (mAdapter != null) {
        ①
        mAdapter.setViewPagerObserver(null);
        mAdapter.startUpdate(this);
        for (int i = 0; i < mItems.size(); i++) {
            final ItemInfo ii = mItems.get(i);
            mAdapter.destroyItem(this, ii.position, ii.object);
        }
        mAdapter.finishUpdate(this);
        mItems.clear();
        removeNonDecorViews();
        mCurItem = 0;
        scrollTo(0, 0);
    }
    ②
    final PagerAdapter oldAdapter = mAdapter;
    mAdapter = adapter;
    mExpectedAdapterCount = 0;

    ③
    if (mAdapter != null) {
        if (mObserver == null) {
            mObserver = new PagerObserver();
        }
        mAdapter.setViewPagerObserver(mObserver);
        mPopulatePending = false;
        final boolean wasFirstLayout = mFirstLayout;
        mFirstLayout = true;
        mExpectedAdapterCount = mAdapter.getCount();
        if (mRestoredCurItem >= 0) {
            mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
            setCurrentItemInternal(mRestoredCurItem, false, true);
            mRestoredCurItem = -1;
            mRestoredAdapterState = null;
            mRestoredClassLoader = null;
        } else if (!wasFirstLayout) {
            ④
            populate();
        } else {
            ⑤
            requestLayout();
        }
    }

    // Dispatch the change to any listeners
    if (mAdapterChangeListeners != null && !mAdapterChangeListeners.isEmpty()) {
        for (int i = 0, count = mAdapterChangeListeners.size(); i < count; i++) {
            mAdapterChangeListeners.get(i).onAdapterChanged(this, oldAdapter, adapter);
        }
    }
}

核心方法就是setAdapter,像RecyclerView一样,因为会有缓存,所以当页面滑动的时候,如果缓存中存在页面,那么就会从缓存中取,如果没有,就需要去创建新的页面,所以我们先来关注一下PagerAdapter

public abstract class PagerAdapter {
    private final DataSetObservable mObservable = new DataSetObservable();
    private DataSetObserver mViewPagerObserver;

    public static final int POSITION_UNCHANGED = -1;
    public static final int POSITION_NONE = -2;

    public abstract int getCount();
    //开始更新
    public void startUpdate(@NonNull ViewGroup container) {
        startUpdate((View) container);
    }
    //初始化页面
    @NonNull
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        return instantiateItem((View) container, position);
    }
    //销毁页面
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        destroyItem((View) container, position, object);
    }
    //结束刷新
    public void finishUpdate(@NonNull ViewGroup container) {
        finishUpdate((View) container);
    }
}

PagerAdapter是一个抽象类,那么这些方法肯定是具体实现类实现,如果我们在使用ViewPager嵌套Fragment的时候,使用的是FragmentPageAdapter

接着回到setAdapter方法中:

  • ①:有一个全局变量 mAdapter,如果是第一个加载进来,那么mAdapter是空的,走到②
  • ②:这里就是将我们传入的adapter给mAdapter赋值
  • ③:这个时候mAdapter不为空,这里需要关注几个参数:
wasFirstLayout = true
mRestoredCurItem = -1

所以这里直接走到⑤,调用requestLayout方法,会执行到onMeasure,在这个方法中,会执行populate方法(这个大家自己去爬楼)

populate干了什么呢?代码太多了就不贴出来了,直接上图:

如果是默认缓存(mOffscreenPageLimit = 1),那么在mItems就会缓存3个Fragment

private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();

当页面滑动时,page2成为了当前页,那么ViewPager的populate做了什么操作呢?

  • (1)首先page3会被预加载,这个时候调用了PagerAdapter的instantiateItem方法新建页面,并放在mItems集合中,并且设置为不可见的状态(setUserVisibleHint(false)),所有缓存中不可见的页面同理(2)page1就会从缓存中移除,调用了PagerAdapter的destroyItem方法,curPage会成为mItems中第一个缓存对象;
  • (3)将page2设置为当前展示的Fragment

因此populate干的主要工作就是,随着页面的滑动,将Page从缓存中移除销毁,或者将新页面新建加入缓存中。

1.2 ViewPager懒加载方案

如上所述,ViewPager默认就是开启预加载的,而且默认最多能够缓存3个Fragment页面,那么为了避免流量的消耗,需要我们针对预加载这种情况进行页面懒加载,只有当页面可见的时候,才能加载数据。

class MainLazyLoadAdapter(
    fragmentManager: FragmentManager,
    val fragments:MutableList<Fragment>
) : FragmentPagerAdapter(fragmentManager) {
    override fun getCount(): Int {
        return fragments.size
    }

    override fun getItem(position: Int): Fragment {
        return fragments[position]
    }
}
class LazyFragment(val index:Int) : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.e("TAG","LazyFragment $index onCreate")
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.e("TAG","LazyFragment $index onCreateView")
        return inflater.inflate(R.layout.fragment_lazy, container, false)
    }

}
val fragments = mutableListOf<Fragment>()
for (index in 0..5) {
    fragments.add(LazyFragment(index))
}
vp_lazy_load.adapter = MainLazyLoadAdapter(supportFragmentManager, fragments)

首先我们先看默认预加载状态,验证之前源码中的原理:

//第一次进来
2022-08-28 13:41:15.759 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreate
2022-08-28 13:41:15.760 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreateView
2022-08-28 13:41:15.783 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreate
2022-08-28 13:41:15.784 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreateView

我们看到第一次进来,第二个Fragment被加载进来,然后右滑,第三个Fragment被加载

2022-08-28 13:41:15.759 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreate
2022-08-28 13:41:15.760 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreateView
2022-08-28 13:41:15.783 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreate
2022-08-28 13:41:15.784 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreateView
2022-08-28 13:48:45.248 12677-12677/com.lay.image_process E/TAG: LazyFragment 2 onCreate
2022-08-28 13:48:45.250 12677-12677/com.lay.image_process E/TAG: LazyFragment 2 onCreateView

当我们滑到第4个Fragment的时候,左滑回到第3个Fragment,发现并没有重建是因为缓存的原因,因为滑到第4个Fragment的时候,第2个Fragment已经被销毁了,再回到第3个Fragment的时候,第2个Fragment被重建,走了onCreateView方法

2022-08-28 13:41:15.759 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreate
2022-08-28 13:41:15.760 12677-12677/com.lay.image_process E/TAG: LazyFragment 0 onCreateView
2022-08-28 13:41:15.783 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreate
2022-08-28 13:41:15.784 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreateView
2022-08-28 13:48:45.248 12677-12677/com.lay.image_process E/TAG: LazyFragment 2 onCreate
2022-08-28 13:48:45.250 12677-12677/com.lay.image_process E/TAG: LazyFragment 2 onCreateView
2022-08-28 13:50:00.439 12677-12677/com.lay.image_process E/TAG: LazyFragment 3 onCreate
2022-08-28 13:50:00.440 12677-12677/com.lay.image_process E/TAG: LazyFragment 3 onCreateView
2022-08-28 13:50:01.344 12677-12677/com.lay.image_process E/TAG: LazyFragment 4 onCreate
2022-08-28 13:50:01.345 12677-12677/com.lay.image_process E/TAG: LazyFragment 4 onCreateView
2022-08-28 13:50:03.315 12677-12677/com.lay.image_process E/TAG: LazyFragment 1 onCreateView

首先我们先看下,Adapter重建Fragment的时候的核心代码

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

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
            mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
        } else {
            //关键代码
            fragment.setUserVisibleHint(false);
        }
    }

    return fragment;
}

我们可以看到,当前Fragment如果被创建但是没有在当前页面展示的时候,调用了fragment.setUserVisibleHint(false),也就是说setUserVisibleHint能够监听当前Fragment是否可见

所以我们对Fragment进行改造:

class LazyFragment(val index:Int) : Fragment() {

    //判断当前页面是否可见
    private var isShow = false
    //判断页面是否创建成功
    private var isViewCreated = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.e("TAG","LazyFragment $index onCreate")
    }
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.e("TAG","LazyFragment $index onCreateView")
        return inflater.inflate(R.layout.fragment_lazy, container, false)
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        isViewCreated = true
        lazyLoad()
    }
    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        Log.e("TAG","LazyFragment $index isVisibleToUser $isVisibleToUser")
        if(isVisibleToUser){
            isShow = true
            //才有资格去懒加载
            lazyLoad()
        }else{
            isShow = false
        }
    }
    private fun lazyLoad() {
        if(isViewCreated && isShow){
            Log.e("TAG","Fragment $index loadData")
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        isViewCreated = false
        isShow = false
    }
}

如果按照之前的方式,当调用onViewCreated方法的时候,我们就会加载数据;做了懒加载处理之后,重写了setUserVisibleHint方法,当前页面可见的时候,才有资格去加载数据,这样即便创建了Fragment,但是如果不可见就不会加载数据

2022-08-28 14:06:29.776 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 isVisibleToUser false
2022-08-28 14:06:29.776 25904-25904/com.lay.image_process E/TAG: LazyFragment 1 isVisibleToUser false
2022-08-28 14:06:29.776 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 isVisibleToUser true
2022-08-28 14:06:29.782 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 onCreate
2022-08-28 14:06:29.783 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 onCreateView
2022-08-28 14:06:29.796 25904-25904/com.lay.image_process E/TAG: Fragment 0 loadData
2022-08-28 14:06:29.805 25904-25904/com.lay.image_process E/TAG: LazyFragment 1 onCreate
2022-08-28 14:06:29.805 25904-25904/com.lay.image_process E/TAG: LazyFragment 1 onCreateView
2022-08-28 14:06:59.395 25904-25904/com.lay.image_process E/TAG: LazyFragment 2 isVisibleToUser false
2022-08-28 14:06:59.396 25904-25904/com.lay.image_process E/TAG: LazyFragment 0 isVisibleToUser false
2022-08-28 14:06:59.396 25904-25904/com.lay.image_process E/TAG: LazyFragment 1 isVisibleToUser true
2022-08-28 14:06:59.396 25904-25904/com.lay.image_process E/TAG: Fragment 1 loadData
2022-08-28 14:06:59.399 25904-25904/com.lay.image_process E/TAG: LazyFragment 2 onCreate
2022-08-28 14:06:59.400 25904-25904/com.lay.image_process E/TAG: LazyFragment 2 onCreateView

通过日志我们可以看到,当首次进入的时候,虽然Fragment 1 被创建了,但是并没有加载数据。

这里有个问题,既然可见之后就能加载数据,那么我只在setUserVisibleHint的时候,判断是否可见来去加载数据?

其实是不可以的,通过日志我们能够发现,setUserVisibleHint是早于onCreate方法调用的,也就是说在页面还没有创建时,去加载数据有可能导致页面元素找不到发生空指针异常。

2 ViewPager2与ViewPager的区别

上一小节,我们介绍了ViewPager的加载机制和缓存机制,那么我们把整套页面搬过来,唯一发生变化的就是将ViewPager转换为ViewPager2

class MainLazyLoadAdapter2(
    activity: FragmentActivity,
    val fragments: MutableList<Fragment>
) : FragmentStateAdapter(activity) {
    override fun getItemCount(): Int {
        return fragments.size
    }
    override fun createFragment(position: Int): Fragment {
        return fragments[position]
    }

}

ViewPager2的适配器使用的是FragmentStateAdapter,因为FragmentStateAdapter继承了RecyclerView.Adapter,因此支持了横向滑动和竖向滑动

val fragments = mutableListOf<Fragment>()
for (index in 0..5) {
    fragments.add(LazyFragment(index))
}
vp_lazy_load = findViewById(R.id.vp_lazy_load)
vp_lazy_load.adapter = MainLazyLoadAdapter2(this, fragments)

用同样的方式设置了适配器,我们看下日志输出,就会发现,咦?怎么跟ViewPager不一样了

2022-08-28 14:47:11.790 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreate
2022-08-28 14:47:11.792 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreateView

刚进来的时候,只有Fragment 1 加载了页面,并没有新建缓存页面,当我滑动到下一页的时候,也只有下一页的页面进行了重建

2022-08-28 14:47:11.790 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreate
2022-08-28 14:47:11.792 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreateView
2022-08-28 14:47:13.948 15514-15514/com.lay.image_process E/TAG: LazyFragment 1 onCreate
2022-08-28 14:47:13.948 15514-15514/com.lay.image_process E/TAG: LazyFragment 1 onCreateView

ViewPager2没有预加载机制吗?这里我们就需要看源码了,直接奔向setOffscreenPageLimit方法,我们看到跟ViewPager的setOffscreenPageLimit方法是不一样的

public void setOffscreenPageLimit(@OffscreenPageLimit int limit) {
    if (limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT) {
        throw new IllegalArgumentException(
                "Offscreen page limit must be OFFSCREEN_PAGE_LIMIT_DEFAULT or a number > 0");
    }
    mOffscreenPageLimit = limit;
    // Trigger layout so prefetch happens through getExtraLayoutSize()
    mRecyclerView.requestLayout();
}
public static final int OFFSCREEN_PAGE_LIMIT_DEFAULT = -1;

这里的判断条件 limit < 1 && limit != OFFSCREEN_PAGE_LIMIT_DEFAULT,有一个数值能够通过,就是-1,这就意味着,ViewPager2默认是不支持预加载的

但是ViewPager2的缓存策略还是存在,因为继承了RecyclerView的Adapter,所以缓存复用机制是跟RecyclerView一致的,默认mViewCaches缓存池的大小是3

2022-08-28 15:30:00.579 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreate
2022-08-28 15:30:00.579 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreateView
2022-08-28 15:30:03.883 15514-15514/com.lay.image_process E/TAG: LazyFragment 1 onCreate
2022-08-28 15:30:03.884 15514-15514/com.lay.image_process E/TAG: LazyFragment 1 onCreateView
2022-08-28 15:30:05.064 15514-15514/com.lay.image_process E/TAG: LazyFragment 2 onCreate
2022-08-28 15:30:05.064 15514-15514/com.lay.image_process E/TAG: LazyFragment 2 onCreateView
2022-08-28 15:30:08.997 15514-15514/com.lay.image_process E/TAG: LazyFragment 3 onCreate
2022-08-28 15:30:08.997 15514-15514/com.lay.image_process E/TAG: LazyFragment 3 onCreateView
2022-08-28 15:30:20.005 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreate
2022-08-28 15:30:20.005 15514-15514/com.lay.image_process E/TAG: LazyFragment 0 onCreateView

当我们滑动到第4个Fragment的时候,注意这里跟ViewPager不一样的是,ViewPager的缓存是缓存当前页的左右两边,但是ViewPager2就是RecyclerView的缓存机制,顺序缓存;

当滑动到第4个Fragment的时候,因为缓存池大小为3,因此LazyFragment 0 就会从缓存池中移除,当再次滑动到LazyFragment 0的时候,就会重建!

所以当我们还在思考如何针对ViewPager的预加载机制做懒加载操作时,请将项目中的ViewPager迁移至ViewPager2

附录:

当你的项目中还在使用ViewPager时,建议使用当前这个懒加载框架

abstract class BaseLazyFragment<VM : ViewModel, VB : ViewBinding> : Fragment() {
    private lateinit var viewModel: VM
    private lateinit var binding: VB

    private var isShow = false
    private var isViewCreated = false
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel = getViewModelInstance()
        binding = getLayoutInflate(layoutInflater)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return binding.root
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initView()
        isViewCreated = true
        lazyLoad()
    }
    override fun setUserVisibleHint(isVisibleToUser: Boolean) {
        super.setUserVisibleHint(isVisibleToUser)
        if (isVisibleToUser) {
            isShow = true
            lazyLoad()
        } else {
            isShow = false
        }
    }
    override fun onDestroy() {
        super.onDestroy()
        isShow = false
        isViewCreated = false
    }
    private fun lazyLoad() {
        if (isShow && isViewCreated) {
            initData()
        }
    }
    open fun initData() {}
    open fun initView() {}
    abstract fun getViewModelInstance(): VM
    abstract fun getLayoutInflate(layoutInflater: LayoutInflater): VB
}

使用方式:

class LazyFragment(val index:Int) : BaseLazyFragment<MainVM,FragmentLazy2Binding>() {
    override fun initData() {
        super.initData()
        Log.e("TAG","LazyFragment $index initData -- ")
    }
    override fun getViewModelInstance(): MainVM {
        return MainVM()
    }
    override fun getLayoutInflate(layoutInflater: LayoutInflater): FragmentLazy2Binding {
        return FragmentLazy2Binding.inflate(layoutInflater)
    }
}

到此这篇关于Android性能优化之ViewPagers + Fragment缓存优化的文章就介绍到这了,更多相关Android 缓存优化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android性能优化之ViewPagers + Fragment缓存优化

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android内存优化之内存缓存

什么是缓存?缓存技术原理是把用户访问的所有对象看作一个全集,经过算法标记哪些是用户经常访问的对象,把这些对象放到一个集合里,这个集合是全集一个子集,下一次用户再访问的时候会先从这个子集集合中查找用户要访问的对象如果找到直接返回这个对象,如果
2022-06-06

Android性能优化之内存优化的示例分析

这篇文章主要介绍Android性能优化之内存优化的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1、Android内存管理机制1.1 Java内存分配模型先上一张JVM将内存划分区域的图程序计数器:存储当前线
2023-06-15

Android性能优化之内存泄漏

前言对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我们可以借助LeakCanary、MAT等工具来检测应用程序是否存在内存泄漏,MAT是一款强
2022-06-06

Android性能优化之弱网优化详解

这篇文章主要为大家介绍了Android性能优化之弱网优化示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Android性能优化之Bitmap图片优化详解

前言 在Android开发过程中,Bitmap往往会给开发者带来一些困扰,因为对Bitmap操作不慎,就容易造成OOM(Java.lang.OutofMemoryError - 内存溢出),本篇博客,我们将一起探讨Bitmap的性能优化。
2022-06-06

android内存优化之图片优化

对图片本身进行操作。尽量不要使用setImageBitmap、setImageResource、BitmapFactory.decodeResource来设置一张大图,因为这些方法在完成decode后,最终都是通过java层的createB
2022-06-06

详解Android性能优化之内存泄漏

综述 内存泄漏(memory leak)是指由于疏忽或错误造成程序未能释放已经不再使用的内存。那么在Android中,当一个对象持有Activity的引用,如果该对象不能被系统回收,那么当这个Activity不再使用时,这个Activity
2022-06-06

Golang函数性能优化之存储分配优化

为了提高 go 函数性能,存储分配优化至关重要。通过预分配缓冲区、使用切片和使用对象池等技术,可以有效减少内存分配带来的开销。以读取大文件为例,预分配文件行缓冲区可以显著优化性能,因为它减少了频繁的内存分配。Go 语言函数性能优化:存储分配
Golang函数性能优化之存储分配优化
2024-04-16

Golang函数性能优化之内存对齐优化

内存对齐优化通过将数据对齐到特定地址来提高程序性能。它减少缓存未命中、优化 simd 指令,具体步骤如下:使用 alignof 函数获取类型的最小对齐值。使用 unsafe.pointer 类型分配对齐的内存。将数据结构强制转换为对齐类型。
Golang函数性能优化之内存对齐优化
2024-04-17

Golang函数性能优化之缓存技术应用

函数性能优化中,缓存技术可以减少函数执行时间,通过在快速存储中存储经常访问的数据。golang 中可使用 "sync/map" 和 "bigcache" 缓存库:"sync/map" 适用于小数据缓存,提供快速访问。"bigcache" 适
Golang函数性能优化之缓存技术应用
2024-04-17

Android性能优化之运算篇

运算篇1) Intro to Compute and Memory ProblemsAndroid中的Java代码会需要经过编译优化再执行的过程。代码的不同写法会影响到Java编译器的优化效率。例如for循环的不同写法会对编译器优化这段代码
2022-06-06

Android性能优化(一)启动优化

以前做手机的时候,我非常重视app的性能优化。其实一直以来,在工作中我总会去强调性能优化的重要性。但是,很多时候,由于一些外界因素,我们对app的一些性能指标不会那么重视。但是,性能优化依然是做好一个产品的重中之重。试想一下,如果用户费了很
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录