1. android性能优化总结
常用的Android性能优化方法:
一、布局优化:
1)尽量减少布局文件的层级。
层级少了,绘制的工作量也就少了,性能自然提高。
2)布局重用 <include标签>
3)按需加载:使用ViewStub,它继承自View,一种轻量级控件,本身不参与任何的布局和绘制过程。他的layout参数里添加一个替换的布局文件,当它通过setVisibility或者inflate方法加载后,它就会被内部布局替换掉。
二、绘制优化:
基于onDraw会被调用多次,该方法内要避免两类操作:
1)创建新的局部对象,导致大量垃圾对象的产生,从而导致频繁的gc,降低程序的执行效率。
2)不要做耗时操作,抢CPU时间片,造成绘制很卡不流畅。
三、内存泄漏优化:
1)静态变量导致内存泄漏 比较明显
2)单例模式导致的内存泄漏 单例无法被垃圾回收,它持有的任何对象的引用都会导致该对象不会被gc。
3)属性动画导致内存泄漏 无限循环动画,在activity中播放,但是onDestroy时没有停止的话,动画会一直播放下去,view被动画持有,activity又被view持有,导致activity无法被回收。
四、响应速度优化:
1)避免在主线程做耗时操作 包括四大组件,因为四大组件都是运行在主线程的。
2)把一些创建大量对象等的初始化工作放在页面回到前台之后,而不应该放到创建的时候。
五、ListView的优化:
1)使用convertView,走listView子View回收的一套:RecycleBin 机制
主要是维护了两个数组,一个是mActiveViews,当前可见的view,一个是mScrapViews,当前不可见的view。当触摸ListView并向上滑动时,ListView上部的一些OnScreen的View位置上移,并移除了ListView的屏幕范围,此时这些OnScreen的View就变得不可见了,不可见的View叫做OffScreen的View,即这些View已经不在屏幕可见范围内了,也可以叫做ScrapView,Scrap表示废弃的意思,ScrapView的意思是这些OffScreen的View不再处于可以交互的Active状态了。ListView会把那些ScrapView(即OffScreen的View)删除,这样就不用绘制这些本来就不可见的View了,同时,ListView会把这些删除的ScrapView放入到RecycleBin中存起来,就像把暂时无用的资源放到回收站一样。
当ListView的底部需要显示新的View的时候,会从RecycleBin中取出一个ScrapView,将其作为convertView参数传递给Adapter的getView方法,从而达到View复用的目的,这样就不必在Adapter的getView方法中执行LayoutInflater.inflate()方法了。
RecycleBin中有两个重要的View数组,分别是mActiveViews和mScrapViews。这两个数组中所存储的View都是用来复用的,只不过mActiveViews中存储的是OnScreen的View,这些View很有可能被直接复用;而mScrapViews中存储的是OffScreen的View,这些View主要是用来间接复用的。
2)使用ViewHolder避免重复地findViewById
3)快速滑动不适合做大量异步任务,结合滑动监听,等滑动结束之后加载当前显示在屏幕范围的内容。
4)getView中避免做耗时操作,主要针对图片:ImageLoader来处理(原理:三级缓存)
5)对于一个列表,如果刷新数据只是某一个item的数据,可以使用局部刷新,在列表数据量比较大的情况下,节省不少性能开销。
六、Bitmap优化:
1)减少内存开支:图片过大,超过控件需要的大小的情况下,不要直接加载原图,而是对图片进行尺寸压缩,方式是BitmapFactroy.Options 采样,inSampleSize 转成需要的尺寸的图片。
2)减少流量开销:对图片进行质量压缩,再上传服务器。图片有三种存在形式:硬盘上时是file,网络传输时是stream,内存中是stream或bitmap,所谓的质量压缩,它其实只能实现对file的影响,你可以把一个file转成bitmap再转成file,或者直接将一个bitmap转成file时,这个最终的file是被压缩过的,但是中间的bitmap并没有被压缩。bitmap.compress(Bitmap.CompressFormat.PNG,100,bos);
七、线程优化:
使用线程池。为什么要用线程池?
1、从“为每个任务分配一个线程”转换到“在线程池中执行任务”
2、通过重用现有的线程而不是创建新线程,可以处理多个请求在创建销毁过程中产生的巨大开销
3、当使用线程池时,在请求到来时间 ,不用等待系统重新创建新的线程,而是直接复用线程池中的线程,这样可以提高响应性。
4、通过和适当调整线程池的大小 ,可以创建足够多的线程以使处理器能够保持忙碌状态,同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存或者失败。
5、一个App里面所有的任务都放在线程池中执行后,可以统一管理 ,当应用退出时,可以把程序中所有的线程统一关闭,避免了内存和CPU的消耗。
6、如果这个任务是一个循环调度任务,你则必须在这个界面onDetach方法把这个任务给cancel掉,如果是一个普通任务则可cancel,可不cancel,但是最好cancel
7、整个APP的总开关会在应用退出的时间把整个线程池全部关闭。
八、一些性能优化建议:
1)避免创建过多对象,造成频繁的gc
2)不要过多使用枚举,枚举占用的空间比整型大很多
3)字符串的拼接使用StringBuffer、StringBuilder来替代直接使用String,因为使用String会创建多个String对象,参考第一条。
4)适当使用软引用,(弱引用就不太推荐了)
5)使用内存缓存和磁盘缓存。
2. RecyclerView是如何使用的,阐述ViewHold和adapter的区别
首先,要明白RecyclerView是做什么的?其次是为什么要用RecyclerView?这里牵扯到RecyclerView和ListView的区别,这里不废话,大家自行网络即可!
以下示例我用的Android API 29 ,启用了AndroidX。
第一步,添加依赖
创建一个新的工程,在app/build.gradle中的dependencies闭包添加以下内容:
implementation 'com.android.support:appcompat-v7:29.0.0'
注意这里的‘29.0.0’要和你的 compileSdkVersion 版本号一致。版本号是28,就改为28。
别的不需要改,以下是此时的项目目录以及gradle文件内容
第二步,创建RecyclerView布局
在你的布局文件中添加RecyclerView,切记要为RecyclerView添加id。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_card"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
第三步,为RecyclerView中的item创建一个统一的布局文件
在layout下创建一个名为layout_rv_card的布局文件,文件名命名要和你的实际业务相匹配方便后期查找。
在此布局文件中,可以添加任何你想展示的视图,在这里我们先创建两个TextView为大家展示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center" />
<TextView
android:id="@+id/tv_state"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center" />
</LinearLayout>
别忘了添加id。
第四步,创建Adapter
Adapter是连接后端数据和前端显示的适配器接口,是数据和UI(View)之间一个重要的纽带。
如果之前没有接触过,先跟着做,做完之后就会理解了。
我们在项目目录下新建一个java类,取名为CardAdapter。
然后在CardAdapter中我们创建一个内部类MyViewHolder继承RecyclerView.ViewHolder。
为什么要创建内部类?
答:就是为了方便而已,创建在外部也是可以,但是如果项目大了之后,类太多不好区分,而且基本上因为布局的不同ViewHolder也基本不同,不会复用,所以创建在内部即可。
为什么要使用ViewHolder?
答:ViewHolder的主要任务:容纳View视图。前边我们提到Adapter连接了后端数据和前端显示,viewHolder的作用就是提供前端的视图文件。
接下来,我们在ViewHolder绑定视图文件,同在activity中类似,itemView顾名思义即列表中每一项的视图,每一项都要绑定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* @Author: Messi Mei
* @Date: 2020/8/24 13:46
* @Email: [email protected]
**/
public class CardAdapter {
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView tvId,tvState;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
tvId = itemView.findViewById(R.id.tv_id);
tvState = itemView.findViewById(R.id.tv_state);
}
}
}
第五步,完成Adapter
在我们创建的CardAdapter类继承我们创建的内部类MyViewHolder,并重写提供的三个方法:
onCreateViewHolder(),onBindViewHolder(),getItemCount()
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* @Author: Messi Mei
* @Date: 2020/8/24 13:46
* @Email: [email protected]
**/
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.MyViewHolder> {
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 0;
}
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView tvId,tvState;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
tvId = itemView.findViewById(R.id.tv_id);
tvState = itemView.findViewById(R.id.tv_state);
}
}
}
5.1重写onCreateViewHolder方法,返回我们的内部类MyViewHolder ,此处为将我们的item布局文件和adapter绑定。
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.layout_rv_card,parent,false);
return new MyViewHolder(view);
}
也可以写为
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_rv_card,parent,false);
return new MyViewHolder(view);
}
一样的。
5.2为每一项视图,添加数据。
此处holder为你每一项的MyViewHolder对象,position 定位,可以理解为list下标。
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.tvId.setText(position);
holder.tvState.setText(position+position);
}
5.3返回列表大小
@Override
public int getItemCount() {
return 0;
}
第六步,创建数据接口
创建一个List列表(当然也可以是JsonArray),以此为例,我们创一个Card实体类,为List添加该类泛型。
6.1接下来在CardAdapter创建一个带该list的构造器
List<Card> list;
public CardAdapter(List<Card> list) {
this.list = list;
}
6.2在getItemCount方法中返回该列表大小
@Override
public int getItemCount() {
return list == null ? 0 : list.size();
}
6.3在onBindViewHolder方法中绑定真实数据
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.tvId.setText(list.get(position).getId());
holder.tvState.setText(list.get(position).getState());
}
以下为整体代码(文末有我在使用RecycleView中对Adapter的改进代码,如有需要可以参考):
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
/** @Author: Messi Mei @Date: 2020/8/24 13:46 @Email: [email protected] */
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.MyViewHolder> {
private List<Card> list;
public CardAdapter(List<Card> list) {
this.list = list;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_rv_card, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.tvId.setText(list.get(position).getId());
holder.tvState.setText(list.get(position).getState());
}
@Override
public int getItemCount() {
return list == null ? 0 : list.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder {
private TextView tvId, tvState;
MyViewHolder(@NonNull View itemView) {
super(itemView);
tvId = itemView.findViewById(R.id.tv_id);
tvState = itemView.findViewById(R.id.tv_state);
}
}
}
第七步,使用RecycleView
在Activity中创建RecycleView对象并绑定视图,创建Adapter对象。
在initData中初始化数据。
package com.mz.myapplication;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private CardAdapter cardAdapter;
private List<Card> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}
private void initData() {
list = new ArrayList<>();
for (int i = 0; i < 50; i++) {
Card card = new Card();
card.setId(i+1+"");
if (i%2 == 1){
card.setState("在线");
}else{
card.setState("离线");
}
list.add(card);
}
}
private void initView() {
recyclerView = findViewById(R.id.rv_card);
cardAdapter = new CardAdapter(list);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(cardAdapter);
}
}
至此,运行即可。
如果你想要更复杂的列表布局,在布局文件中加入相应的视图即可,修改完后记得在ViewHolder中加入、绑定相应的视图,在Adapter中的onBindViewHolder为其添加、绑定数据。
而
3. android 怎么在外面拿recyclerview 中viewholder的控件
据官方的介绍,该控件用于在有限的窗口中展示大量数据集,其实这样功能的控件我们并不陌生,例如:ListView、GridView。
那么有了ListView、GridView为什么还需要RecyclerView这样的控件呢?整体上看RecyclerView架构,提供了一种插拔式的体验,高度的解耦,异常的灵活,通过设置它提供的不同LayoutManager,ItemDecoration , ItemAnimator实现令人瞠目的效果。
你想要控制其显示的方式,请通过布局管理器LayoutManager
你想要控制Item间的间隔(可绘制),请通过ItemDecoration
你想要控制Item增删的动画,请通过ItemAnimator
你想要控制点击、长按事件,请自己写(擦,这点尼玛。)
基本使用
鉴于我们对于ListView的使用特别的熟悉,对比下RecyclerView的使用代码:
mRecyclerView = findView(R.id.id_recyclerview);
//设置布局管理器
mRecyclerView.setLayoutManager(layout);
//设置adapter
mRecyclerView.setAdapter(adapter)
//设置Item增加、移除动画
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//添加分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(
getActivity(), DividerItemDecoration.HORIZONTAL_LIST));12345678910
ok,相比较于ListView的代码,ListView可能只需要去设置一个adapter就能正常使用了。而RecyclerView基本需要上面一系列的步骤,那么为什么会添加这么多的步骤呢?
那么就必须解释下RecyclerView的这个名字了,从它类名上看,RecyclerView代表的意义是,我只管Recycler View,也就是说RecyclerView只管回收与复用View,其他的你可以自己去设置。可以看出其高度的解耦,给予你充分的定制自由(所以你才可以轻松的通过这个控件实现ListView,GirdView,瀑布流等效果)。
附上出处链接:http://blog.csdn.net/lmj623565791/article/details/45059587
4. android view怎么回收
切换Layout的话,你的Activity将长驻内存,也就是说你的应用中该Activity始终都是存在的,调用另外一个Activity的话,上一个Activity实例就会被销毁和回收掉的.如果不想在程序中让Activity数量过多,你可以考虑用Layout方式.
5. Android-ViewPager2
ViewPager2
简单说就是将RecycleView再封装了一遍,然后协同FragmentStateAdapter将RecycleView的每个Item与Fragment绑定。
特性
支持从左到右,或者从上到下布局
由于适配基于的是RecyclerView.Adapter,所以内存优化也直接采用RecyclerView.Adapter的内存优化机制,相对于viewpager,内存优化更高效合理,且notifyDataSetChanged也更高效了。由于不用开发者自己实现内存和notifyDataSetChanged,也更简便了。
相比ViewPager,ViewPager2修复了不能关闭预加载和更新Adapter不生效的痛点。
目前ViewPager2对Fragment支持只能用FragmentStateAdapter,FragmentStateAdapter在遇到预加载时,只会创建Fragment对象,不会把Fragment真正的加入到布局中,所以自带懒加载效果。
FragmentStateAdapter不会一直保留Fragment实例,回收的ItemView也会移除Fragment,所以得做好Fragment重建后恢复数据的准备。
FragmentStateAdapter在遇到offscreenPageLimit>0时,处理离屏Fragment和可见Fragment没有什么区别,所以无法通过setUserVisibleHint判断显示与否。
基本方法
部分核心方法使用参照RecycleView和ViewPager,如设置分割线addItemDecoration(),设置当前项setCurrentItem()等。
setAdapter() 设置适配器
setOrientation() 设置布局方向
setCurrentItem() 设置当前Item下标
beginFakeDrag() 开始模拟拖拽
fakeDragBy() 模拟拖拽中
endFakeDrag() 模拟拖拽结束
setUserInputEnabled() 设置是否允许用户输入/触摸
setOffscreenPageLimit()设置屏幕外加载页面数量
registerOnPageChangeCallback() 注册页面改变回调
setPageTransformer() 设置页面滑动时的变换效果
。。。还有好多。使用的时候大家可以具体看一下。
offscreenPageLimit()
不设置它则不会预加载,一旦设置了,由于limit必须>0,所以会进行预加载limit个页面
viewpager2的预加载在加载时已经准备好了View布局,但是没有加载到parent视图上,所以自带懒加载效果。 而viewpager加载的时候View已经添加到parent上。所以会走生命周期的方法。
从 初始化 方法可以看出,viewpager2支持的一些特性以及为什么。
RecyclerViewImpl
基于RecyclerView的二次封装,对触摸事件,初始化等进行封装。
LinearLayoutManagerImpl
使用LinearLayoutManager,所以拥有LinearLayoutManager的特性,可以垂直或者水平。也就引申出为什么后面可以设置水平或者垂直滑动
PageTransformerAdapter
用于监听pager的改变。
基于 RecyclerView.Adapter实现
类似recycleView的使用。
基于FragmentStateAdapter实现
6. Android RecycleView滚动监听,以及view回收的细节
主要细节是,如果要在recycleView在回收的同时,做UI调整容易出现的问题。
一、xml可见的最外层设置为margin,view的回收还有添加的时间点可能不好计算,容易出现卡顿的情况,
二、如果是实用方法smoothScrollBy,第一个view刚好滚出可视区的时候是没有被回收的,技巧就是滚动距离加1
没图没真相,以下是业务需求的效果
首先想法是通过behavior解决问题,但是后来发现behavior需要好多个,各种相互观察好麻烦,所以想一下可不可以直接使用RecycleView,通过滚动监听改变图片大小实现功能,初步想法是第一个可视view不断变小,第二个可视的view不断变大,如下图
滚动的初步想法是这样的通过滚动距离dx,来改变view的大小
但是后来发现dx,效果不行,有兴趣的同学自己试一下,所以想了另外一个思路,根据下标为2的view也就是第三本书的位置,修改前面两本书的宽度,所以不需要dx了,这里顺便提一下,书本这个view是重写了ImageView的,把宽高比固定为3:4,所以只需要不断的改变书本的宽度就好了,其中bookWidth 为小书本的原始高度
到这里基本上就实现了滚动效果了,但是业务需求是当手指不在滑动的时候,不允许书本被覆盖的,所以这里就要,监听滚动状态
这个地方必须使用smoothScrollBy,不能是ScrollBy,因为ScrollBy不会回调onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy)这个方法,造成view宽度没有重新设定。
项目地址: https://gitee.com/feng87/BookShelf
7. android getview什么时候调用
举个例子吧:当系统 要显示一个gridview,gridview里有10个子元素,但界面只够显示5个,其它五个是你滚动的时候才出现。现如今显示了这个gridview,你怎么知道它显示的是哪五个呢,这个时候是不是要position你有了position还不够啊,你还得控制显示它,比如现在显示的是4,5,6,7,8这五个你得更新显示这五个元素的uiandroid系统为了省内存,它会把第1,2,3 new出来的view传给你问你是不是要复用,这就是那个参数view的用途,如果你复用它,就将它返回(返回之前需重新设置这个view里的值如:更新view里的textView的显示等)。所以你会在getView里经常看到:if(vew=null){view=......}return view;
8. android开发 BaseAdapter的convertView参数是什么意思
这个convertView其实就是最关键的部分 原理上讲 当ListView滑动的过程中 会有item被滑出屏幕 而不再被使用 这时候Android会回收这个条目的view 这个view也就是这里的convertView
当item1被移除屏幕的时候 我们会重新new一个View给新显示的item_new 而如果使用了这个convertView 我们其实可以复用它 这样就省去了new View的大量开销
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
//复用了回收的view 只需要直接作内容填充的修改就好了
} else {
view = new Xxx(...);
//没有供复用的view 按一般的做法新建view
}
return view;
}
9. Android 怎么隐藏控件,又不回收控件的空间
在Android开发中,大部分控件都有visibility这个属性,其属性有3个分别为“visible ”、“invisible”、“gone”。主要用来设置控制控件的显示和隐藏。有些人可能会疑惑Invisible和gone是有什么区别的???
其在XML文件和Java代码中设置如下:
可见(visible)
XML文件:android:visibility="visible"
Java代码:view.setVisibility(View.VISIBLE);
不可见(invisible)
XML文件:android:visibility="invisible"
Java代码:view.setVisibility(View.INVISIBLE);
隐藏(GONE)
XML文件:android:visibility="gone"
Java代码:view.setVisibility(View.GONE);
VISIBLE:设置控件可见
INVISIBLE:设置控件不可见
GONE:设置控件隐藏
而INVISIBLE和GONE的主要区别是:当控件visibility属性为INVISIBLE时,界面保留了view控件所占有的空间;而控件属性为GONE时,界面则不保留view控件所占有的空间。
http://blog.csdn.net/chindroid/article/details/8000713
10. android 自定义view怎么做能提高效率
越少越好
为了加速视图,从那些调用频繁的活动中减少不必要的代码。在OnDraw()方法中开始绘制,它会给你最大的
效益。特别低,你也应该减少在onDraw()方法中的内存分配,因为任何内存分配都可能导致内存回收,这将会
引起不连贯。 在初始化或者动画之间分配对象。绝不要在动画运行时分配内存。
另一方面需要减少onDraw()方法中的开销,只在需要时才调用onDraw()方法。通常invalidate()方法会调用
onDraw()方法,因此减少对invalidate()的不必要调用。如果可能,调用它的重载版本即带有参数的invalidate()
方法而不是无参的invalidate()方法。该带参数的方法invalidate()能使draw过程更有效,以及减少对落在该矩形
区域(参数指定的区域)外视图的不必要重绘 。