关于Android 中双list数据复制问题

文章介绍了在Java中如何正确处理包含bean数据类型的列表复制,以及使用CloneObjectUtils类进行对象序列化以实现深层次数据的独立复制。作者强调了当处理复杂数据结构时,避免数据共享的重要性。

1、基本类型的数据 没有hashcode 直接add
2、包含bean数据类型的list 间相互复制
不管是addall 还是 add
如:
list1.add(list2.get(1));
如果数据类型包含bean, debug会发现数据其实指向是一样的
当list2中数据修改时,list1中的数据同样被修改
此时正确的做法应该是再写一个copy方法
Bean data = new Bean();
data.setName(list2.get(i).getName());
基础数据的复制可以正常set就行,如果是复制的数据类型,则需要继续new 子类

3、把数据写入内存中再读出来,下面时网上的方法,bean对象继承Serializable

package com.example.chanp.testannotation.util;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
/**
 * Created by sjx on 2019/6/27.
 */
 
public class CloneObjectUtils {
 
    public static <T> T cloneObject(T obj) {
        T result = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectOutputStream outputStream = null;
        ObjectInputStream inputStream = null;
        try {
            //对象写到内存中
            byteArrayOutputStream = new ByteArrayOutputStream();
            outputStream = new ObjectOutputStream(byteArrayOutputStream);
            outputStream.writeObject(obj);
 
            //从内存中再读出来
            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            inputStream = new ObjectInputStream(byteArrayInputStream);
            result = (T) inputStream.readObject();
 
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null)
                    outputStream.close();
                if (inputStream != null)
                    inputStream.close();
                if (byteArrayOutputStream != null)
                    byteArrayOutputStream.close();
                if (byteArrayInputStream != null)
                    byteArrayInputStream.close();
 
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

调用:Bean bean = CloneObjectUtils.cloneObject(list1.get(0));
--------------------->>>>>>>>>>>>>>>>>
2023/08/28 by dpl

<think>我们已经在之前的对话中讨论了AIDL调用崩溃的问题及其解决方案。现在用户的问题转向了如何正确使用AIDL进行数据获取,这是一个更基础的问题,需要从使用步骤开始讲解。 根据引用[1][2][5],AIDL用于跨进程通信(IPC),允许客户端应用调用服务端应用暴露的方法并获取数据。下面将分步骤介绍实现方法。 步骤1:定义AIDL接口 在服务端创建AIDL文件,定义客户端可以调用的方法。例如,我们定义一个获取字符串数据的接口: IMyService.aidl (位于服务端src/main/aidl/com/example/service/) ``` package com.example.service; interface IMyService { String getData(); } ``` 步骤2:实现AIDL接口 在服务端创建一个Service,并在其内部实现AIDL接口的Stub类,重写方法: ```java public class MyService extends Service { private final IMyService.Stub binder = new IMyService.Stub() { @Override public String getData() throws RemoteException { return "Hello from Service!"; } }; @Override public IBinder onBind(Intent intent) { return binder; } } ``` 步骤3:在AndroidManifest.xml中声明Service 注意添加导出属性(exported)和权限(如果需要): ```xml <service android:name=".MyService" android:exported="true" android:process=":remote"> <!-- 可选,指定在独立进程运行 --> </service> ``` 步骤4:客户端绑定服务 在客户端应用中,复制相同的AIDL文件(包名必须一致),然后绑定服务: ```java private IMyService myService; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { myService = IMyService.Stub.asInterface(service); try { String data = myService.getData(); // 调用远程方法 Log.d("Client", "Received: " + data); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { myService = null; } }; // 绑定服务 Intent intent = new Intent(); intent.setComponent(new ComponentName("com.example.service", "com.example.service.MyService")); bindService(intent, connection, Context.BIND_AUTO_CREATE); ``` 步骤5:传递复杂数据 如果需要传递自定义对象,该对象必须实现Parcelable接口,并在AIDL文件中导入(如引用[3][4]所示)。例如: 定义User类(实现Parcelable)后,在AIDL中: ``` // IMyService.aidl package com.example.service; import com.example.service.User; // 导入自定义类型 interface IMyService { User getUserById(int id); } ``` 注意事项: 1. 客户端和服务端的AIDL接口和自定义Parcelable对象的包名必须完全一致。 2. 跨进程调用是同步的,避免在主线程调用以免ANR(如引用[2]提及的RPC特性)。 3. 服务端应处理多线程并发问题,因为Binder方法调用可能来自多个客户端。 通过以上步骤,客户端即可安全地获取服务端数据。若需回调机制(服务端主动通知客户端),可定义回调接口(如引用[4]所示),并使用RemoteCallbackList管理回调以避免Binder失效问题。 下面生成相关问题。 </think>### Android中使用JAVA通过AIDL调用获取服务端数据的完整方法 以下是基于AIDL实现跨进程通信获取服务端数据的标准流程,结合引用内容[^1][^2][^3][^4][^5]: #### 一、基础步骤(核心流程) 1. **定义AIDL接口文件** 在服务端创建`.aidl`文件声明数据获取方法(如引用[^3]所示): ```java // IDataService.aidl package com.example.service; import com.example.model.Product; // 导入自定义Parcelable对象 interface IDataService { // 获取基本类型数据 String getBasicData(); // 获取自定义对象(需实现Parcelable) Product getProductInfo(in int productId); // 注册回调接口(引用[^4]) void registerCallback(IDataCallback callback); } ``` 2. **创建回调接口AIDL**(引用[^4]) ```java // IDataCallback.aidl package com.example.service; import com.example.model.Product; interface IDataCallback { void onDataReceived(in Product product); void onError(in String errorMsg); } ``` 3. **实现服务端Service** ```java public class DataService extends Service { private final IDataService.Stub binder = new IDataService.Stub() { @Override public String getBasicData() { return "服务端数据示例"; } @Override public Product getProductInfo(int productId) { return new Product(productId, "手机", 2999); // 返回自定义对象 } @Override public void registerCallback(IDataCallback callback) { // 使用RemoteCallbackList管理回调(防泄漏) callbackList.register(callback); } }; private RemoteCallbackList<IDataCallback> callbackList = new RemoteCallbackList<>(); @Override public IBinder onBind(Intent intent) { return binder; } } ``` 4. **客户端绑定服务**(引用[^2]) ```java // 客户端Activity/Fragment private IDataService dataService; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { dataService = IDataService.Stub.asInterface(service); try { // 注册回调 dataService.registerCallback(new IDataCallback.Stub() { @Override public void onDataReceived(Product product) { runOnUiThread(() -> updateUI(product)); } @Override public void onError(String errorMsg) { showToast(errorMsg); } }); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { dataService = null; } }; // 绑定服务 Intent intent = new Intent(); intent.setComponent(new ComponentName("com.example.service", "com.example.service.DataService")); bindService(intent, connection, Context.BIND_AUTO_CREATE); ``` #### 二、关键实现细节 1. **自定义Parcelable对象**(引用[^4]) - 实现`Parcelable`接口才能跨进程传输 ```java public class Product implements Parcelable { private int id; private String name; private double price; // 必须添加CREATOR(反序列化) public static final Parcelable.Creator<Product> CREATOR = new Creator<>() { public Product createFromParcel(Parcel in) { return new Product(in.readInt(), in.readString(), in.readDouble()); } public Product[] newArray(int size) { return new Product[size]; } }; @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name); dest.writeDouble(price); } } ``` 2. **安全回调机制** - 使用`RemoteCallbackList`替代普通List(自动处理Binder死亡) - 回调时检查Binder存活状态: ```java int n = callbackList.beginBroadcast(); for (int i = 0; i < n; i++) { IDataCallback cb = callbackList.getBroadcastItem(i); if (cb.asBinder().isBinderAlive()) { cb.onDataReceived(product); // 存活才回调 } } callbackList.finishBroadcast(); ``` 3. **数据类型支持** | 数据类型 | 是否支持 | 注意事项 | |----------------|----------|------------------------------| | 基本类型 | ✅ | int, long, float等 | | String/CharSequence | ✅ | UTF-8编码 | | List/Map | ✅ | 元素需支持AIDL | | Parcelable对象 | ✅ | 必须显式导入 | | 普通Java对象 | ❌ | 需转为Parcelable或基本类型 | #### 三、数据传输优化建议 1. **性能优化** - 大数据传输使用`ParcelFileDescriptor`传输文件描述符 - 频繁调用合并为批量接口减少IPC次数 2. **异常处理** ```java try { Product product = dataService.getProductInfo(101); } catch (RemoteException e) { // 远程服务断开 rebindService(); } catch (SecurityException e) { // 权限不足 requestPermissions(); } ``` 3. **权限控制** 在`AndroidManifest.xml`声明自定义权限: ```xml <permission android:name="com.example.service.ACCESS_DATA"/> <service android:permission="com.example.service.ACCESS_DATA" android:exported="true"> ``` > **数学关系**:跨进程调用耗时$t_{ipc}$与数据量$d$的关系为: > $$t_{ipc} = k \cdot d + c$$ > 其中$k$为序列化系数,$c$为固定开销。控制$d$可优化性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值