Android中复用问题哲理性解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介:

Android中列表的复用机制提高了APP的运行效率,但随之而来的复用的问题总是让程序员们头痛,一个

bug找头天也找不到。我就把自己解决这方面的经验贡献出来供大家参考:


问题1:什么是复用

    复用其实指的是复用View,而绑定View的数据是变化的。


问题2:复用的原理探究

    

 为了彻底弄清楚复用的原理,和特地写了段小程序。

   Adapter代码:

     

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class  MyAdapter  extends  BaseAdapter{
 
     @Override
     public  int  getCount() {
         return  20 ;
     }
 
     @Override
     public  Object getItem( int  position) {
         return  null ;
     }
 
     @Override
     public  long  getItemId( int  position) {
         return  position;
     }
 
     @Override
     public  View getView( int  position, View convertView, ViewGroup parent) {
 
 
 
         Log.i(TAG,  "aaaaaaaaaa---------- getView: position = "  + position  +  ",convertView = "  + convertView);
 
         ViewHolder holder;
         if (convertView ==  null ){
             convertView = LayoutInflater.from(SimpleCheckBoxListActivity. this ).inflate(R.layout.adapter_simple_checkbox_item, null , false );
             holder =  new  ViewHolder();
             holder.tv = (TextView) convertView.findViewById(R.id.tv);
             holder.cb = (CheckBox) convertView.findViewById(R.id.cb);
             convertView.setTag(holder);
         } else {
             holder = (ViewHolder) convertView.getTag();
         }
         holder.tv.setText( "index = "  + position);
 
         Log.i(TAG,  "bbbbbbbbbb---------- getView: position = "  + position +  ",convertView = "  + convertView.toString());
 
 
         //将convertView缓存起来,方便后面的分析。
         itemViews.put(position,convertView);
 
         //分析当前position是否复用了之前哪个位置的view
         int  reusePosition = analyseReusedWhichPosition(position);
         if (reusePosition != - 1 ){
             Log.i(TAG,  "getView: 位置 "  + position +  "复用了位置"  + reusePosition +  "的view" );
         }
         return  convertView;
     }
 
 
     class  ViewHolder{
         TextView tv;
         CheckBox cb;
     }
 
 
     //分析当前position是否复用了之前哪个位置的view
     private  int  analyseReusedWhichPosition( int  currentPosition){
         View currentPositionView = itemViews.get(currentPosition);
         for  ( int  i =  0 ; i < currentPosition; i++) {
             View beforePositionView = itemViews.get(i);
             if (beforePositionView ==  null ){
                 continue ;
             }
 
             if (beforePositionView == currentPositionView){
                 return  i;
             }
         }
 
         return  - 1 ;
     }
}

   日志分析:

 1)程序初次运行

    wKiom1fgmJ6ClokBAAAeqYToh4o638.png   


   打印的日志:

    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
aaaaaaaaaa---------- getView: position =  0 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  0 ,convertView = android.widget.LinearLayout{42eceab0 V.E..... ......I.  0 , 0 - 0 , 0 }    
aaaaaaaaaa---------- getView: position =  1 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  1 ,convertView = android.widget.LinearLayout{42ee4650 V.E..... ......I.  0 , 0 - 0 , 0 }    
aaaaaaaaaa---------- getView: position =  2 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  2 ,convertView = android.widget.LinearLayout{42ee6140 V.E..... ......I.  0 , 0 - 0 , 0 }    
aaaaaaaaaa---------- getView: position =  3 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  3 ,convertView = android.widget.LinearLayout{42ee7c10 V.E..... ......I.  0 , 0 - 0 , 0 }    
aaaaaaaaaa---------- getView: position =  4 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  4 ,convertView = android.widget.LinearLayout{42ee96e0 V.E..... ......I.  0 , 0 - 0 , 0 }    
aaaaaaaaaa---------- getView: position =  5 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  5 ,convertView = android.widget.LinearLayout{42eeb1e8 V.E..... ......I.  0 , 0 - 0 , 0 }    
aaaaaaaaaa---------- getView: position =  6 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  6 ,convertView = android.widget.LinearLayout{42eeccb8 V.E..... ......I.  0 , 0 - 0 , 0 }    
aaaaaaaaaa---------- getView: position =  7 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  7 ,convertView = android.widget.LinearLayout{42eee788 V.E..... ......I.  0 , 0 - 0 , 0 }

 2)接着向下滑动,索引0没有完全消失,索引8就出现了,这时还没有复用。

   wKioL1fgmauAGltGAAAowjgMwQ8355.png     

  打印的日志:

   

1
2
aaaaaaaaaa---------- getView: position =  8 ,convertView =  null    
bbbbbbbbbb---------- getView: position =  8 ,convertView = android.widget.LinearLayout{42ef5150 V.E..... ......I.  0 , 0 - 0 , 0 }

 3)位置9出现,索引0已经完全消失(复用开始出现)

 wKioL1fgmsvz-yOeAAAgn7xjnLM206.png      

 打印的日志:

  

1
2
3
4
aaaaaaaaaa---------- getView: position =  9 ,convertView = android.widget.LinearLayout{42eceab0 V.E..... ........  0 ,- 215 - 1080 , 1 }    
bbbbbbbbbb---------- getView: position =  9 ,convertView = android.widget.LinearLayout{42eceab0 V.E..... .......D  0 ,- 215 - 1080 , 1 }    
getView: 位置  9 复用了位置 0 的view    
可以发现索引 9 处的hashCode与索引 0 处的hashCode都是42eceab0

 4)紧接着向下滚动到最后(注意是慢慢地滚动

  wKioL1fgnAiiazBfAAAhMQBl3Uc573.png

 打印的日志:

   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
aaaaaaaaaa---------- getView: position =  10 ,convertView = android.widget.LinearLayout{42ee4650 V.E..... ........  0 ,- 213 - 1080 , 3 }    
bbbbbbbbbb---------- getView: position =  10 ,convertView = android.widget.LinearLayout{42ee4650 V.E..... .......D  0 ,- 213 - 1080 , 3 }    
getView: 位置  10 复用了位置 1 的view    
aaaaaaaaaa---------- getView: position =  11 ,convertView = android.widget.LinearLayout{42ee6140 V.E..... ........  0 ,- 205 - 1080 , 11 }    
bbbbbbbbbb---------- getView: position =  11 ,convertView = android.widget.LinearLayout{42ee6140 V.E..... .......D  0 ,- 205 - 1080 , 11 }    
getView: 位置  11 复用了位置 2 的view    
aaaaaaaaaa---------- getView: position =  12 ,convertView = android.widget.LinearLayout{42ee7c10 V.E..... ........  0 ,- 202 - 1080 , 14 }    
bbbbbbbbbb---------- getView: position =  12 ,convertView = android.widget.LinearLayout{42ee7c10 V.E..... .......D  0 ,- 202 - 1080 , 14 }    
getView: 位置  12 复用了位置 3 的view    
aaaaaaaaaa---------- getView: position =  13 ,convertView = android.widget.LinearLayout{42ee96e0 V.E..... ........  0 ,- 201 - 1080 , 15 }    
bbbbbbbbbb---------- getView: position =  13 ,convertView = android.widget.LinearLayout{42ee96e0 V.E..... .......D  0 ,- 201 - 1080 , 15 }    
getView: 位置  13 复用了位置 4 的view    
aaaaaaaaaa---------- getView: position =  14 ,convertView = android.widget.LinearLayout{42eeb1e8 V.E..... ........  0 ,- 188 - 1080 , 28 }    
bbbbbbbbbb---------- getView: position =  14 ,convertView = android.widget.LinearLayout{42eeb1e8 V.E..... .......D  0 ,- 188 - 1080 , 28 }    
getView: 位置  14 复用了位置 5 的view    
aaaaaaaaaa---------- getView: position =  15 ,convertView = android.widget.LinearLayout{42eeccb8 V.E..... ........  0 ,- 213 - 1080 , 3 }    
bbbbbbbbbb---------- getView: position =  15 ,convertView = android.widget.LinearLayout{42eeccb8 V.E..... .......D  0 ,- 213 - 1080 , 3 }    
getView: 位置  15 复用了位置 6 的view    
aaaaaaaaaa---------- getView: position =  16 ,convertView = android.widget.LinearLayout{42eee788 V.E..... ........  0 ,- 179 - 1080 , 37 }    
bbbbbbbbbb---------- getView: position =  16 ,convertView = android.widget.LinearLayout{42eee788 V.E..... .......D  0 ,- 179 - 1080 , 37 }    
getView: 位置  16 复用了位置 7 的view    
aaaaaaaaaa---------- getView: position =  17 ,convertView = android.widget.LinearLayout{42ef5150 V.E..... ........  0 ,- 181 - 1080 , 35 }    
bbbbbbbbbb---------- getView: position =  17 ,convertView = android.widget.LinearLayout{42ef5150 V.E..... .......D  0 ,- 181 - 1080 , 35 }    
getView: 位置  17 复用了位置 8 的view    
 
 
aaaaaaaaaa---------- getView: position =  18 ,convertView = android.widget.LinearLayout{42eceab0 V.E..... ........  0 ,- 195 - 1080 , 21 }    
bbbbbbbbbb---------- getView: position =  18 ,convertView = android.widget.LinearLayout{42eceab0 V.E..... .......D  0 ,- 195 - 1080 , 21 }    
getView: 位置  18 复用了位置 0 的view    
aaaaaaaaaa---------- getView: position =  19 ,convertView = android.widget.LinearLayout{42ee4650 V.E..... ........  0 ,- 210 - 1080 , 6 }    
bbbbbbbbbb---------- getView: position =  19 ,convertView = android.widget.LinearLayout{42ee4650 V.E..... .......D  0 ,- 210 - 1080 , 6 }    
getView: 位置  19 复用了位置 1 的view

 

 可以看到向下慢慢滑动的时候,复用是很有规律的。

 但是如果快速的向下滑动的时候,又发现不了什么规律:复用并非是连续的

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
aaaaaaaaaa---------- getView: position =  8 ,convertView = android.widget.LinearLayout{42f9a780 V.E..... ........  0 ,- 85 - 1080 , 131 }
bbbbbbbbbb---------- getView: position =  8 ,convertView = android.widget.LinearLayout{42f9a780 V.E..... .......D  0 ,- 85 - 1080 , 131 }
getView: 位置  8 复用了位置 0 的view
aaaaaaaaaa---------- getView: position =  9 ,convertView = android.widget.LinearLayout{42f9f818 V.E..... ........  0 , 384 - 1080 , 600 }
bbbbbbbbbb---------- getView: position =  9 ,convertView = android.widget.LinearLayout{42f9f818 V.E..... .......D  0 , 384 - 1080 , 600 }
getView: 位置  9 复用了位置 3 的view
aaaaaaaaaa---------- getView: position =  10 ,convertView = android.widget.LinearLayout{42f9dd48 V.E..... ........  0 , 138 - 1080 , 354 }
bbbbbbbbbb---------- getView: position =  10 ,convertView = android.widget.LinearLayout{42f9dd48 V.E..... .......D  0 , 138 - 1080 , 354 }
getView: 位置  10 复用了位置 2 的view
aaaaaaaaaa---------- getView: position =  11 ,convertView = android.widget.LinearLayout{42f9c278 V.E..... ........  0 ,- 108 - 1080 , 108 }
bbbbbbbbbb---------- getView: position =  11 ,convertView = android.widget.LinearLayout{42f9c278 V.E..... .......D  0 ,- 108 - 1080 , 108 }
getView: 位置  11 复用了位置 1 的view
aaaaaaaaaa---------- getView: position =  12 ,convertView =  null
bbbbbbbbbb---------- getView: position =  12 ,convertView = android.widget.LinearLayout{42fad3a0 V.E..... ......I.  0 , 0 - 0 , 0 }
aaaaaaaaaa---------- getView: position =  13 ,convertView = android.widget.LinearLayout{42fa2df0 V.E..... ........  0 , 60 - 1080 , 276 }
bbbbbbbbbb---------- getView: position =  13 ,convertView = android.widget.LinearLayout{42fa2df0 V.E..... .......D  0 , 60 - 1080 , 276 }
getView: 位置  13 复用了位置 5 的view
aaaaaaaaaa---------- getView: position =  14 ,convertView = android.widget.LinearLayout{42fa12e8 V.E..... ........  0 ,- 186 - 1080 , 30 }
bbbbbbbbbb---------- getView: position =  14 ,convertView = android.widget.LinearLayout{42fa12e8 V.E..... .......D  0 ,- 186 - 1080 , 30 }
getView: 位置  14 复用了位置 4 的view
aaaaaaaaaa---------- getView: position =  15 ,convertView = android.widget.LinearLayout{42fa48c0 V.E..... ........  0 ,- 150 - 1080 , 66 }
bbbbbbbbbb---------- getView: position =  15 ,convertView = android.widget.LinearLayout{42fa48c0 V.E..... .......D  0 ,- 150 - 1080 , 66 }
getView: 位置  15 复用了位置 6 的view
aaaaaaaaaa---------- getView: position =  16 ,convertView = android.widget.LinearLayout{42f9a780 V.E..... ........  0 , 78 - 1080 , 294 }
bbbbbbbbbb---------- getView: position =  16 ,convertView = android.widget.LinearLayout{42f9a780 V.E..... .......D  0 , 78 - 1080 , 294 }
getView: 位置  16 复用了位置 0 的view
aaaaaaaaaa---------- getView: position =  17 ,convertView = android.widget.LinearLayout{42f9f818 V.E..... ........  0 , 13 - 1080 , 229 }
bbbbbbbbbb---------- getView: position =  17 ,convertView = android.widget.LinearLayout{42f9f818 V.E..... .......D  0 , 13 - 1080 , 229 }
getView: 位置  17 复用了位置 3 的view
aaaaaaaaaa---------- getView: position =  18 ,convertView = android.widget.LinearLayout{42f9dd48 V.E..... ........  0 ,- 28 - 1080 , 188 }
bbbbbbbbbb---------- getView: position =  18 ,convertView = android.widget.LinearLayout{42f9dd48 V.E..... .......D  0 ,- 28 - 1080 , 188 }
getView: 位置  18 复用了位置 2 的view
aaaaaaaaaa---------- getView: position =  19 ,convertView = android.widget.LinearLayout{42fa6390 V.E..... ........  0 ,- 168 - 1080 , 48 }
bbbbbbbbbb---------- getView: position =  19 ,convertView = android.widget.LinearLayout{42fa6390 V.E..... .......D  0 ,- 168 - 1080 , 48 }
getView: 位置  19 复用了位置 7 的view


5)最后,向上滚动到索引为0的位置

   

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
aaaaaaaaaa---------- getView: position =  11 ,convertView = android.widget.LinearLayout{4304de70 V.E..... ........  0 ,- 212 - 1080 , 4 }
bbbbbbbbbb---------- getView: position =  11 ,convertView = android.widget.LinearLayout{4304de70 V.E..... .......D  0 ,- 212 - 1080 , 4 }
getView: 位置  11 复用了位置 8 的view
aaaaaaaaaa---------- getView: position =  10 ,convertView = android.widget.LinearLayout{4303ee70 V.E..... ........  0 , 1829 - 1080 , 2045 }
bbbbbbbbbb---------- getView: position =  10 ,convertView = android.widget.LinearLayout{4303ee70 V.E..... .......D  0 , 1829 - 1080 , 2045 }
getView: 位置  10 复用了位置 1 的view
aaaaaaaaaa---------- getView: position =  9 ,convertView = android.widget.LinearLayout{ 43040940  V.E..... ........  0 , 1829 - 1080 , 2045 }
bbbbbbbbbb---------- getView: position =  9 ,convertView = android.widget.LinearLayout{ 43040940  V.E..... .......D  0 , 1829 - 1080 , 2045 }
getView: 位置  9 复用了位置 2 的view
aaaaaaaaaa---------- getView: position =  8 ,convertView = android.widget.LinearLayout{4303d378 V.E..... ........  0 , 1830 - 1080 , 2046 }
bbbbbbbbbb---------- getView: position =  8 ,convertView = android.widget.LinearLayout{4303d378 V.E..... .......D  0 , 1830 - 1080 , 2046 }
getView: 位置  8 复用了位置 0 的view
aaaaaaaaaa---------- getView: position =  7 ,convertView = android.widget.LinearLayout{430474b8 V.E..... ........  0 , 1825 - 1080 , 2041 }
bbbbbbbbbb---------- getView: position =  7 ,convertView = android.widget.LinearLayout{430474b8 V.E..... .......D  0 , 1825 - 1080 , 2041 }
getView: 位置  7 复用了位置 6 的view
aaaaaaaaaa---------- getView: position =  6 ,convertView = android.widget.LinearLayout{43048f88 V.E..... ........  0 , 1824 - 1080 , 2040 }
bbbbbbbbbb---------- getView: position =  6 ,convertView = android.widget.LinearLayout{43048f88 V.E..... .......D  0 , 1824 - 1080 , 2040 }
aaaaaaaaaa---------- getView: position =  5 ,convertView = android.widget.LinearLayout{43043ee0 V.E..... ........  0 , 1822 - 1080 , 2038 }
bbbbbbbbbb---------- getView: position =  5 ,convertView = android.widget.LinearLayout{43043ee0 V.E..... .......D  0 , 1822 - 1080 , 2038 }
getView: 位置  5 复用了位置 4 的view
aaaaaaaaaa---------- getView: position =  4 ,convertView = android.widget.LinearLayout{430459e8 V.E..... ........  0 , 1823 - 1080 , 2039 }
bbbbbbbbbb---------- getView: position =  4 ,convertView = android.widget.LinearLayout{430459e8 V.E..... .......D  0 , 1823 - 1080 , 2039 }
aaaaaaaaaa---------- getView: position =  3 ,convertView = android.widget.LinearLayout{ 43042410  V.E..... ........  0 , 1829 - 1080 , 2045 }
bbbbbbbbbb---------- getView: position =  3 ,convertView = android.widget.LinearLayout{ 43042410  V.E..... .......D  0 , 1829 - 1080 , 2045 }
aaaaaaaaaa---------- getView: position =  2 ,convertView = android.widget.LinearLayout{4304de70 V.E..... ........  0 , 1826 - 1080 , 2042 }
bbbbbbbbbb---------- getView: position =  2 ,convertView = android.widget.LinearLayout{4304de70 V.E..... .......D  0 , 1826 - 1080 , 2042 }
aaaaaaaaaa---------- getView: position =  1 ,convertView = android.widget.LinearLayout{4303ee70 V.E..... ........  0 , 1828 - 1080 , 2044 }
bbbbbbbbbb---------- getView: position =  1 ,convertView = android.widget.LinearLayout{4303ee70 V.E..... .......D  0 , 1828 - 1080 , 2044 }
aaaaaaaaaa---------- getView: position =  0 ,convertView = android.widget.LinearLayout{ 43040940  V.E..... ........  0 , 1788 - 1080 , 2004 }
bbbbbbbbbb---------- getView: position =  0 ,convertView = android.widget.LinearLayout{ 43040940  V.E..... .......D  0 , 1788 - 1080 , 2004 }



如上所述,到底谁复用了谁是随机不定的,这个我们也没有必要去关心。我们只要知道position是不变就行了。


 另外,除了打日志。可以选中某一个位置的checkbox,然后上下滑动,如果某个checkbox也莫名的选中了,那就说明这个位置的checkbox复用了之前选中的那个checkbox。

   

问题3:Adapter的notifyDataSetChanged()方法作了什么事情

 notifyDataSetChanged,会重新走一遍可见的position的getView方法。

 

问题4:复用出现的场景

    1.if-else的坑:在Adapter中,如果绑定View的数据的时候如果有if判断,往往很多人忘记了加else,这是大多数复用问题出现的根源之一。在一般情况下else不写没有逻辑错误,但是在ListView复

用的情况下如果不写错误就会带来错乱的麻烦。

     实际场景:

    wKiom1fOIjnzXZ7WAADoxtBhZN8794.png     比如每个item可能有或没有图片picarrList,之前我只加了if判断,如果有图片就显示。但后来上下一滑动之后发现没有图片的item竟然也显示了其它了item的图片,于是追根溯源发现是这里的问题。


   2.checkbox等的复用问题:果如下图,是一个简单的CheckBox列表

    wKiom1fgq9yCKR79AAGnsthYJK4167.png

  第1页刚好0-8索引,我将0索引处的checkbox设置为选中状态,然后向下滑动,发现下一个出现的checkbox(索引为10,不是9,也不一定就是10,而是索引0完全消失之后第一个出现的item)竟然也选中了。 

  百度了一下,可以用Map<Interger,Boolean>来记录对应position的checkbox的选中状态。而且网上

的这个Map是事先就是预订好大小的了,但实际中Map的大小是确定的。

 细节1):Map<Interger,Boolean>来记录对应position的checkbox的选中状态,怎么初始化?

      --1-- 可以先在成员或者构造方法里实例化Map对象

        

1
Map<Integer,Boolean> isTitleCheckBoxSelected =  new  HashMap();

      --2-- 在getView方法里初始化Map对象,默认checkbox都是未选中状态

1
2
3
4
5
6
7
8
if (!isTitleCheckBoxSelected.containsKey(position)){
     Log.i(TAG,  "bindData: init checkbox "  + position);
     isTitleCheckBoxSelected.put(position, false );
     //如果启动了全选,则新出现的view也要选中。
     if (isSelectedAllStarted){
         isTitleCheckBoxSelected.put(position, true );
     }
}

   上面的这段代码其实是非常妙的,通过contains判断,保证了初始化。如果后面操作了map,

也不会影响这段代码对map的初始化。

   map这种数据结构,由于key是唯一,可以做去重操作。这一点List则不可直接做到。



 细节2):响应checkbox的OnCheckedChangeListener事件,将改变后的状态保存到map中。

   

1
2
3
4
5
6
7
header_checkbox.setOnCheckedChangeListener( new  CompoundButton.OnCheckedChangeListener() {
     @Override
     public  void  onCheckedChanged(CompoundButton buttonView,  boolean  isChecked) {
         if (isCheckedByCode)  return ;
         isTitleCheckBoxSelected.put(position, !isTitleCheckBoxSelected.get(position));
     }
});

  在onCheckedChanged方法里将对应position的checkbox的状态反转。


 细节3):将map中的对应position的状态值赋值给当前的checkbox

 但是有个问题,checkbox的setChecked方法,看其源码,会走OnCheckedChangeListener的回调

 wKioL1fgtDugC6HlAAMdFYRjDaI692.png   

 而此时,setChecked方法我只想设置View的状态,并不想走它的回调方法。下面有2种方法可以解决这个问题

 方法1:在setChecked方法的前后用一个变量夹住,在回调方法里通过这个变量判断回调是不是在代码

里通过setChecked触发,如果是setChecked触发的,则不执行map的取反的操作。

1
2
3
isCheckedByCode =  true ;
header_checkbox.setChecked(isTitleCheckBoxSelected.get(position));
isCheckedByCode =  false ;

 这种方法多申请了个变量,耦合度比较高。

 方法2:在setChecked方法之前将checkbox的监听设置为null,在setChecked方法之后设置真正的监听。


 除了checkbox,其它的一些view,也可以通过以上的方法来解决复用的问题。解决复用要遵循一个原则:MV分离,在view一些事件监听里,一般情况下改变记录状态的Map值之后,切记立马就将值设置给View,而应该通过notifyDatasetChanged()方法将状态更新到view上。



 






      本文转自屠夫章哥  51CTO博客,原文链接:http://blog.51cto.com/4259297/1846668,如需转载请自行联系原作者



相关文章
|
11天前
|
安全 Android开发 iOS开发
安卓与iOS的较量:技术特性与用户体验的深度解析
在移动操作系统的战场上,安卓和iOS一直占据着主导地位。本文将深入探讨这两大平台的核心技术特性,以及它们如何影响用户的体验。我们将从系统架构、应用生态、安全性能和创新功能四个方面进行比较,帮助读者更好地理解这两个系统的异同。
41 3
|
2天前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
10 0
|
27天前
|
搜索推荐 Java Android开发
安卓与iOS开发差异深度解析
【8月更文挑战第20天】在移动应用开发的广阔舞台上,安卓与iOS这两大操作系统各领风骚,它们在设计理念、开发环境、用户群体等方面展现出各自独特的风貌。本文将深入探讨这两个平台在开发过程中的主要差异,帮助开发者更好地理解并选择适合自己项目需求的平台。
|
29天前
|
JSON Java Android开发
Android 开发者必备秘籍:轻松攻克 JSON 格式数据解析难题,让你的应用更出色!
【8月更文挑战第18天】在Android开发中,解析JSON数据至关重要。JSON以其简洁和易读成为首选的数据交换格式。开发者可通过多种途径解析JSON,如使用内置的`JSONObject`和`JSONArray`类直接操作数据,或借助Google提供的Gson库将JSON自动映射为Java对象。无论哪种方法,正确解析JSON都是实现高效应用的关键,能帮助开发者处理网络请求返回的数据,并将其展示给用户,从而提升应用的功能性和用户体验。
39 1
|
15天前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
68 0
|
16天前
|
开发者 算法 虚拟化
惊爆!Uno Platform 调试与性能分析终极攻略,从工具运用到代码优化,带你攻克开发难题成就完美应用
【8月更文挑战第31天】在 Uno Platform 中,调试可通过 Visual Studio 设置断点和逐步执行代码实现,同时浏览器开发者工具有助于 Web 版本调试。性能分析则利用 Visual Studio 的性能分析器检查 CPU 和内存使用情况,还可通过记录时间戳进行简单分析。优化性能涉及代码逻辑优化、资源管理和用户界面简化,综合利用平台提供的工具和技术,确保应用高效稳定运行。
29 0
|
16天前
|
机器学习/深度学习 TensorFlow 算法框架/工具
全面解析TensorFlow Lite:从模型转换到Android应用集成,教你如何在移动设备上轻松部署轻量级机器学习模型,实现高效本地推理
【8月更文挑战第31天】本文通过技术综述介绍了如何使用TensorFlow Lite将机器学习模型部署至移动设备。从创建、训练模型开始,详细演示了模型向TensorFlow Lite格式的转换过程,并指导如何在Android应用中集成该模型以实现预测功能,突显了TensorFlow Lite在资源受限环境中的优势及灵活性。
42 0
|
24天前
|
测试技术 API Android开发
Android经典实战之简化 Android 相机开发:CameraX 库的全面解析
CameraX是Android Jetpack的一个组件,旨在简化相机应用开发,提供了易于使用的API并支持从Android 5.0(API级别21)起的设备。其主要特性包括广泛的设备兼容性、简洁的API、生命周期感知、简化实现及方便的集成与测试。通过简单的几个步骤即可实现如拍照、视频录制等功能。此外,还提供了最佳实践指导以确保应用的稳定性和性能。
33 0
|
1天前
|
Android开发 开发者 Kotlin
探索安卓开发中的新特性
【9月更文挑战第14天】本文将引导你深入理解安卓开发领域的一些最新特性,并为你提供实用的代码示例。无论你是初学者还是经验丰富的开发者,这篇文章都会给你带来新的启示和灵感。让我们一起探索吧!
|
5天前
|
IDE 开发工具 Android开发
安卓与iOS开发对比:平台选择对项目成功的影响
【9月更文挑战第10天】在移动应用开发的世界中,选择正确的平台是至关重要的。本文将深入探讨安卓和iOS这两大主要移动操作系统的开发环境,通过比较它们的市场份额、开发工具、编程语言和用户群体等方面,为开发者提供一个清晰的指南。我们将分析这两个平台的优势和劣势,并讨论如何根据项目需求和目标受众来做出最佳选择。无论你是初学者还是有经验的开发者,这篇文章都将帮助你更好地理解每个平台的特性,并指导你做出明智的决策。

热门文章

最新文章

推荐镜像

更多