前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >列表、存储以及WebView 相关优化

列表、存储以及WebView 相关优化

作者头像
Yif
发布2020-04-23 17:40:27
6330
发布2020-04-23 17:40:27
举报
文章被收录于专栏:Android 进阶Android 进阶

列表优化

ListView 优化逻辑

  1. adapter中的getView方法中尽量少使用逻辑
  2. 尽最大可能避免GC
  3. 滑动的时候不载入图片
  4. ListViewscrollingCache和animateCache设置为false
  5. item的布局层级越少越好
  6. 使用ViewHolder
  7. 使用RecyclerView
  • adapter中的getView方法中尽量少使用逻辑

不要在你的getView()中写过多的逻辑代码,我们能够将这些代码放在别的地方。比如:

优化前的getView():

代码语言:javascript
复制
<br />@Override
 
public View getView(int position, View convertView, ViewGroup paramViewGroup) {
 
        Object current_event = mObjects.get(position);
 
        ViewHolder holder = null;
 
        if (convertView == null) {
 
                holder = new ViewHolder();
 
                convertView = inflater.inflate(R.layout.row_event, null);
 
                holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim);
 
                holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster);
 
                convertView.setTag(holder);
 
 
 
        } else {
 
                holder = (ViewHolder) convertView.getTag();
 
        }
 
 
 
       //在这里进行逻辑推断。这是有问题的 
 
        if (doesSomeComplexChecking()) {
 
                holder.ThreeDimention.setVisibility(View.VISIBLE);
 
        } else {
 
                holder.ThreeDimention.setVisibility(View.GONE); 
 
        }
 
 
 
        // 这是设置image的參数,每次getView方法运行时都会运行这段代码。这显然是有问题的
 
        RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight);
 
        holder.EventPoster.setLayoutParams(imageParams);
 
 
 
        return convertView;
 
}
 
 

优化后的getView():

代码语言:javascript
复制
<br />@Override
 
public View getView(int position, View convertView, ViewGroup paramViewGroup) {
 
    Object object = mObjects.get(position);
 
    ViewHolder holder = null;
 
 
 
    if (convertView == null) {
 
            holder = new ViewHolder();
 
            convertView = inflater.inflate(R.layout.row_event, null);
 
            holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim);
 
            holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster);
 
            //设置參数提到这里,仅仅有第一次的时候会运行,之后会复用 
 
            RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight);
 
            holder.EventPoster.setLayoutParams(imageParams);
 
            convertView.setTag(holder);
 
    } else {
 
            holder = (ViewHolder) convertView.getTag();
 
    }
 
 
 
    // 我们直接通过对象的getter方法取代刚才那些逻辑推断。那些逻辑推断放到别的地方去运行了
 
    holder.ThreeDimension.setVisibility(object.getVisibility());
 
 
 
    return convertView;
 
}
 
 
  • GC 垃圾回收器

当你创建了大量的对象的时候。GC就会频繁的运行。所以在getView()方法中不要创建非常多的对象。最好的优化是,不要在ViewHolder以外创建不论什么对象。假设你的你的log里面发现“GC has freed some memory”频繁出现的话。那你的程序肯定有问题了。

你能够检查一下:

  • item布局的层级是否太深
  • getView()方法中是否有大量对象存在
  • ListView的布局属性
    • 载入图片

假设你的ListView中须要显示从网络上下载的图片的话。我们不要在ListView滑动的时候载入图片,那样会使ListView变得卡顿,所以我们须要再监听器里面监听ListView的状态。假设滑动的时候,停止载入图片,假设没有滑动,则开始载入图片

代码语言:javascript
复制
<br />listView.setOnScrollListener(new OnScrollListener() {
 
 
 
            @Override
 
            public void onScrollStateChanged(AbsListView listView, int scrollState) {
 
                    //停止载入图片 
 
                    if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
 
                            imageLoader.stopProcessingQueue();
 
                    } else {
 
                    //開始载入图片
 
                            imageLoader.startProcessingQueue();
 
                    }
 
            }
 
 
 
            @Override
 
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
 
                    // TODO Auto-generated method stub
 
 
 
            }
 
    });
 
 
  • 将ListView的scrollingCache和animateCache设置为false

scrollingCache: scrollingCache本质上是drawing cache,你能够让一个View将他自己的drawing保存在cache中(保存为一个bitmap),这样下次再显示View的时候就不用重画了,而是从cache中取出。默认情况下drawing cahce是禁用的。由于它太耗内存了,可是它确实比重画来的更加平滑。

而在ListView中,scrollingCache是默认开启的,我们能够手动将它关闭。

animateCache: ListView默认开启了animateCache,这会消耗大量的内存,因此会频繁调用GC,我们能够手动将它关闭掉

优化前的ListView

代码语言:javascript
复制
<br />&lt;ListView
 
        android:id="@android:id/list"
 
        android:layout_width="match_parent"
 
        android:layout_height="wrap_content"
 
        android:cacheColorHint="#00000000"
 
        android:divider="@color/list_background_color"
 
        android:dividerHeight="0dp"
 
        android:listSelector="#00000000"
 
        android:smoothScrollbar="true"
 
        android:visibility="gone" /&gt; 
 
 

优化后的ListView

代码语言:javascript
复制
<br />&lt;ListView
 
        android:id="@android:id/list"
 
        android:layout_width="match_parent"
 
        android:layout_height="wrap_content"
 
        android:divider="@color/list_background_color"
 
        android:dividerHeight="0dp"
 
        android:listSelector="#00000000"
 
        android:scrollingCache="false"
 
        android:animationCache="false"
 
        android:smoothScrollbar="true"
 
        android:visibility="gone" /&gt;
 
 
  • 降低item的布局的深度

我们应该尽量降低item布局深度,由于当滑动ListView的时候,这回直接导致測量与绘制,因此会浪费大量的时间。所以我们应该将一些不必要的布局嵌套关系去掉。降低item布局深度

  • 使用ViewHolder

这个大家应该非常熟悉了,可是不要小看这个ViewHolder,它能够大大提高我们ListView的性能

针对RecyclerView做了哪些优化

  1. onBindViewHolder

这个方法含义应该都知道是绑定数据,并且是在UI线程,所以要尽量在这个方法中少做一些业务处理

  1. 数据优化

采用android Support 包下的DIffUtil集合工具类结合RV分页加载会更加友好,节省性能

  1. item优化

减少item的View的层级,(pps:当然推荐把一个item自定义成一个View,如果有能力的话),如果item的高度固定的话可以设置setHasFixedSize(true),避免requestLayout浪费资源

  1. 使用RecycledViewPool

RecycledViewPool是对item进行缓存的,item相同的不同RV可以才使用这种方式进行性能提升

  1. Prefetch预取

这是在RV25.1.0及以上添加的新功能

  1. 资源回收

通过重写RecyclerView.onViewRecycled(holder)来合理的回收资源。

  1. 不要什么情况都用adapter.notifyDataSetChanged(),小范围修改可以试试adapter.notifyItemChanged(position)或者adapter.notifyItemRangeChanged(positionStart,itemcount)

TextView 优化

原因:面对复杂文本性能不佳 方案: - BoringLayout 单行 - StaticLayout 多行 - DynamicLayout 可编辑展示文本 - 展示类StaticLayout即可,性能优于DynamicLayout - 异步创建StaticLayout

代码语言:javascript
复制
public class CustomTextView extends View {
 
    private String mText = "我是StaticLayout显示出来的文本";
    private TextPaint mTextPaint;
    private StaticLayout mStaticLayout;
 
    public CustomTextView(Context context) {
        super(context);
        initLabelView();
    }
 
    public CustomTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initLabelView();
    }
 
    private void initLabelView() {
        mTextPaint = new TextPaint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
        mTextPaint.setColor(Color.BLACK);
        final int width = (int) mTextPaint.measureText(mText);
 
        //异步创建StaticLayout
        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                mStaticLayout = new StaticLayout(mText, mTextPaint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0, false);
                postInvalidate();
            }
        });
    }
 
 
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(mStaticLayout != null){
            canvas.save();
            canvas.translate(getPaddingLeft(), getPaddingTop());
            mStaticLayout.draw(canvas);
            canvas.restore();
        }
    }
}
 

字符串拼接优化

代码语言:javascript
复制
   // 以下代码是为了演示字符串的拼接
        String msgOld = newsItem.title + newsItem.targetId;// 原有方式
 
        StringBuilder builder = new StringBuilder();
        builder.append(newsItem.title)
                .append(newsItem.targetId);// 建议使用方式,不要小看这点优化
        String msgNew = builder.toString();
 

存储优化

常规方案

  • 确保IO操作发送在非主线程
  • HOOK或者AOP辅助

SharedPreferences相关

  • 跨进程不安全。
  • 加载缓慢:初始化加载整个文件
  • 全量写入:每次改动都需要整体写入
  • 卡顿:补偿策略导致,由于提供了异步落盘的 apply 机制,在崩溃或者其他一些异常情况可能会导致数据丢失。
基于缺点改进
  1. 建议不要存储较大数据或者较多数据到SharedPreferences中;
  2. 频繁修改的数据修改后统一提交,而不是修改过后马上提交;
  3. 在跨进程通讯中不去使用SharedPreferences
  4. 键值对不宜过多;
使用腾讯MMKV 来替换SharedPreferences
  • mmp和文件锁保证数据的完整性
  • 增量写入,使用Protocol Buffer
  • 支持从SharedPreferences 迁移

引入方式,导包:implementation 'com.tencent:mmkv:1.0.17' 使用

代码语言:javascript
复制
//在Application中进行初始化
MMKV.initialize(MyApplication.this);
//key值是times,值为100
MMKV.defaultMMKV().encode("times",100);
//获取key值对应的value值
int times = MMKV.defaultMMKV().decodeInt("times");
 

WebView 异常监控

监控屏幕是否白屏,白屏则webview有问题 确认白屏,所有像素一直则认为白屏

腾讯开源的轻量级框架VasSonic:轻量级高性能Hybrid框架 https://github.com/Tencent/VasSonic/wiki

WebView 白屏检测代码示例

代码语言:javascript
复制
<br />public class TestBlankActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.testblank);
        final TextView textView = findViewById(R.id.tv_testblank);
        textView.postDelayed(new Runnable() {
            @Override
            public void run() {
                boolean isBlank = BlankDetect.isBlank(textView);
                LogUtils.i("isBlank "+ isBlank);
            }
        },2000);
 
    }
}
/**
 * WebView白屏检测
 */
public class BlankDetect {
 
    /**
     * 判断Bitmap是否都是一个颜色
     * @param bitmap
     * @return
     */
    public static boolean isBlank(View view) {
        Bitmap bitmap = getBitmapFromView(view);
 
        if (bitmap == null) {
            return true;
        }
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        if (width &gt; 0 &amp;&amp; height &gt; 0) {
            int originPix = bitmap.getPixel(0, 0);
            int[] target = new int[width];
            Arrays.fill(target, originPix);
            int[] source = new int[width];
            boolean isWhiteScreen = true;
            for (int col = 0; col &lt; height; col++) {
                bitmap.getPixels(source, 0, width, 0, col, width, 1);
                //拿每个像素对比第一个像素点,如果不一样说明没有出现白屏,否则出现白屏
                if (!Arrays.equals(target, source)) {
                    //不是白屏
                    isWhiteScreen = false;
                    break;
                }
            }
            return isWhiteScreen;
        }
        return false;
    }
 
    /**
     * 从View获取转换到的Bitmap
     * @param view
     * @return
     */
    private static Bitmap getBitmapFromView(View view){
        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        if (Build.VERSION.SDK_INT &gt;= 11) {
            view.measure(View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(view.getHeight(), View.MeasureSpec.EXACTLY));
            view.layout((int) view.getX(), (int) view.getY(), (int) view.getX() + view.getMeasuredWidth(), (int) view.getY() + view.getMeasuredHeight());
        } else {
            view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
            view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        }
        view.draw(canvas);
        return bitmap;
    }
}
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    android:orientation="horizontal"&gt;
 
    &lt;TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/tv_testblank"/&gt;
 
&lt;/LinearLayout&gt;
 
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020年4月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 列表优化
    • ListView 优化逻辑
      • 针对RecyclerView做了哪些优化
      • TextView 优化
      • 字符串拼接优化
      • 存储优化
        • 常规方案
          • SharedPreferences相关
            • 基于缺点改进
            • 使用腾讯MMKV 来替换SharedPreferences
        • WebView 异常监控
          • WebView 白屏检测代码示例
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档