[zzerX@blog ~ ]:

记录一次Android Dwrable、Java List缓存填坑

前序

好像很久没有发博客了,最近也是很忙(借口!),有很多需要记录的知识没来得及写,此次动笔,因为这个坑也是挺折磨人。

Drawable缓存机制

起因:在ActivityA 和ActivityB中都有一个Button 这两个按钮,都使用了一个drawable对象,当在ActivityA改变drawable的样式时,ActivityB随之改变。

activitya.xml

<Button
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:text="Button"
		android:background="@drawable/button"
		android:id="@+id/bt1"/>

	<Button
		android:layout_width="wrap_content"
		style="?android:attr/buttonBarButtonStyle"
		android:layout_height="wrap_content"
		android:text="点击改变Button背景样式"
		android:id="@+id/set_button"/>
		<Button
		android:layout_width="wrap_content"
		style="?android:attr/buttonBarButtonStyle"
		android:layout_height="wrap_content"
		android:text="启动ActivityB"
		android:id="@+id/start_activity_button"
		android:visibility="gone"/>

MainActivity.java


public class MainActivity extends AppCompatActivity {

    Button bug_button;
    Button set_button;
    Button start_activity_button;
    
    int bug_button_style;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
	
        init(this);

    }

    private void init(final Context context) {
       bug_button = findViewById(R.id.bt1);
       
        set_button = findViewById(R.id.set_button);
        start_activity_button = findViewById(R.id.start_activity_button);
        
        set_button.setOnClickListener(new OnClickListener(){

                @Override
                public void onClick(View p1) {
                    if(bug_button_style == 0){
                        bug_button_style = 1;
                        bug_button.setBackgroundDrawable(Util.createButton(context,0xFF636867));
                    }else{
                        bug_button_style = 0;
                        bug_button.setBackgroundDrawable(Util.createButton(context,0xFF588BC0));
                        
                    }
                    bug_buttobu.per
                }
                
            
        });
        final Intent intent = new Intent(this, ActivityB.class);
        start_activity_button.setOnClickListener(new OnClickListener(){

                @Override
                public void onClick(View p1) {

                   context. startActivity(intent,null);
                }
                
            
        });
        
    }

工具类


public class Util {
    public static LayerDrawable createButton(Context context,int color){
        
        LayerDrawable  drawable = (LayerDrawable) context.getDrawable(R.drawable.button);
        ((GradientDrawable)drawable.getDrawable(0)).setColor(color);
        return drawable;
    }
  }

最后drawable文件 button.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>

        <shape xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle">
            <!--rectangle矩形-->
            <!--圆角-->
            <size
                android:width="35dp"
                android:height="35dp"></size>
            <corners
                android:topLeftRadius="10dp"
                android:topRightRadius="10dp"
                android:bottomRightRadius="10dp"
                android:bottomLeftRadius="10dp"
            />
            <solid android:color="@color/button_default_bg"></solid>
            <!--大小-->

            <!--描边-->

            <stroke 
                android:width="5dp"
                android:color="@color/button_disable_bg" />
        </shape>
    </item>
</layer-list>

大概就是这样的,ActivityB没有东西 就不贴了。
然后运行效果跟前面说的一致,后面查阅才发现Drawable是有缓存的,解决办法同样很简单。
工具类中getDrawable之后使用mutate

context.getDrawable(R.drawable.button).mutate();

List深拷贝与浅拷贝

发现这个是因为在一个项目中,我使用了两个RecyclerView(先不考虑这样使用是否有弊端) :


第一个用于显示数据

第二个显示数据并进行修改

但两个适配器引用的数据是相等的(第二个RecyclerView的数据从第一个拿),然后神奇的事情发生了, 再第二个RecyclerView中修改的数据即使没有保存,也能在第一个RecyclerView生效。
写了一个Demo如图:
原数据被破坏

很诧异,因为有了上一个因为drawable缓存导致的问题,所以起初认为也是drawable的问题,直到我甚至替换了不一样的Drawable,还把第二个RecyclerView改成ListView,但都无法解决,所以问题并不在Android部分,并且两个适配器相同的东西也只有一个List集合而已,那是不是这个List集合也有缓存呢?(留下了基础不扎实的眼泪)

public class Main {
	
   
	public static void main(String[] args)  {
		
        
        List<String> oldList = new ArrayList<String>();
        
        for(int i = 0;i<5;i++){
            oldList.add("小明的同学" + i);
        }
       
        
           List<String>  newList = oldList; 
           
            newList.set(3,"小明的朋友");

            System.out.println(oldList.toString());

            System.out.println(newList.toString());
       }
}

输出结果是:

[小明的同学0, 小明的同学1, 小明的同学2, 小明的朋友, 小明的同学4]
[小明的同学0, 小明的同学1, 小明的同学2, 小明的朋友, 小明的同学4]

可以看到两个List的数据都变了,可是我明明只改了newList,其实是因为我只对List进行了 浅拷贝

浅拷贝是对前一个数据的”引用传递”,像**ListA = ListB; **

**ListA.addAll(ListB); **

两个都是浅拷贝。

那怎么进行深拷贝

网上的深拷贝大概两个办法,第一个是把原List序列化

public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {  
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();     
     ObjectOutputStream out = new ObjectOutputStream(byteOut);   
       out.writeObject(src);     
     ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());      
    ObjectInputStream in = new ObjectInputStream(byteIn);        
  @SuppressWarnings("unchecked")      
        List<T> dest = (List<T>) in.readObject();         
 return dest;      
}  

第二种则是子集对象实现Cloneable接口 ,并重写clone ,然后我稍微封装了一下。

public static <T extends DeepClone> List<T> deepClone(List<T> src) {  

        List<T> list = new ArrayList<>();     
   for(T obj : src){         
   list.add((T)obj.clone()); 
       }      
  return list;    
    }        
    public static class DeepClone implements Cloneable{  
      @Override   
     protected Object clone() {       
     Test test = null;     
       try {     
           test = (Test) super.clone();    
        } catch (CloneNotSupportedException e) { 
               e.printStackTrace();     
       }      
      return test;     
   }    
            }

让对象继承这个DeepClone 然后使用deepClone克隆List就行了😁。

到此就结束了,缓存机制,引用传递的存在优化了效率,但如果不注意的话,还是会掉进坑里的。

, — Jun 7, 2020