如何在React Native的Android组件(Retrofit+协程+MVVM)获取API数据并传给RN渲染UI?
解决思路与实现步骤
一、Android端:基于MVVM+Retrofit+协程实现数据请求
1. 定义Retrofit API接口与数据实体
先编写Retrofit服务接口,用协程suspend方法发起异步请求,同时定义对应的数据实体类:
// API接口 interface ProductApiService { @GET("products") suspend fun getAllProducts(): Response<List<Product>> } // 商品数据实体 data class Product( val id: Int, val title: String, val price: Double, val description: String, val category: String, val image: String )
2. 实现Repository层封装请求逻辑
统一处理数据请求的业务逻辑,封装Retrofit调用并返回结果状态:
class ProductRepository(private val apiService: ProductApiService) { suspend fun fetchAllProducts(): Result<List<Product>> { return try { val response = apiService.getAllProducts() if (response.isSuccessful) { Result.success(response.body() ?: emptyList()) } else { Result.failure(Throwable("请求失败:${response.message()}")) } } catch (e: Exception) { Result.failure(e) } } }
3. ViewModel层管理UI状态与协程
借助viewModelScope发起协程请求,用LiveData维护数据状态:
class ProductViewModel(private val repository: ProductRepository) : ViewModel() { private val _productsState = MutableLiveData<Result<List<Product>>>() val productsState: LiveData<Result<List<Product>>> = _productsState fun loadAllProducts() { viewModelScope.launch { _productsState.value = repository.fetchAllProducts() } } }
4. 封装React Native原生模块
创建Native Module暴露给RN调用,内部触发ViewModel的数据请求,将结果转换为RN兼容格式后回调:
class ProductModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { private lateinit var productViewModel: ProductViewModel override fun getName(): String { return "ProductModule" // RN中调用的模块名称 } override fun initialize() { super.initialize() // 初始化Retrofit、Repository与ViewModel(实际项目建议用Hilt依赖注入) val retrofit = Retrofit.Builder() .baseUrl("你的API基础地址") .addConverterFactory(GsonConverterFactory.create()) .build() val apiService = retrofit.create(ProductApiService::class.java) val repository = ProductRepository(apiService) productViewModel = ProductViewModel(repository) } // 暴露给RN的异步方法,用Promise返回结果 @ReactMethod fun getAllProducts(promise: Promise) { productViewModel.loadAllProducts() productViewModel.productsState.observeForever { result -> when (result) { is Result.Success -> { // 将List<Product>转换为RN可识别的WritableArray val productsArray = Arguments.createArray() result.data.forEach { product -> val productMap = Arguments.createMap().apply { putInt("id", product.id) putString("title", product.title) putDouble("price", product.price) putString("description", product.description) putString("category", product.category) putString("image", product.image) } productsArray.pushMap(productMap) } promise.resolve(productsArray) // 移除观察者避免内存泄漏 productViewModel.productsState.removeObserver { } } is Result.Failure -> { promise.reject("FETCH_ERROR", result.exception.message) productViewModel.productsState.removeObserver { } } } } } }
5. 注册Native Module
创建Package类并在MainApplication中注册模块:
// Package类 class ProductPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> { return listOf(ProductModule(reactContext)) } override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> { return emptyList() } } // 在MainApplication的getPackages方法中添加 override fun getPackages(): List<ReactPackage> { val packages = PackageList(this).packages packages.add(ProductPackage()) return packages }
二、React Native端:调用原生模块并渲染UI
在RN组件中导入原生模块,调用方法获取数据后渲染列表:
import { NativeModules, FlatList, Text, View, ActivityIndicator, Alert } from 'react-native'; import { useState, useEffect } from 'react'; const { ProductModule } = NativeModules; const ProductListScreen = () => { const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetchProducts(); }, []); const fetchProducts = async () => { try { const result = await ProductModule.getAllProducts(); setProducts(result); } catch (error) { Alert.alert('请求失败', error.message); } finally { setLoading(false); } }; if (loading) { return <ActivityIndicator size="large" color="#0066cc" />; } return ( <View style={{ flex: 1, padding: 16 }}> <FlatList data={products} keyExtractor={(item) => item.id.toString()} renderItem={({ item }) => ( <View style={{ marginBottom: 16, padding: 12, borderWidth: 1, borderColor: '#eee', borderRadius: 8 }}> <Text style={{ fontSize: 18, fontWeight: '600' }}>{item.title}</Text> <Text style={{ fontSize: 16, color: '#ff6600', marginTop: 4 }}>${item.price.toFixed(2)}</Text> <Text style={{ marginTop: 8, color: '#666' }}>{item.description}</Text> </View> )} /> </View> ); }; export default ProductListScreen;
三、关键注意事项
- 内存泄漏防护:Native Module中LiveData的观察者在回调后必须移除,避免内存泄漏。
- 数据类型兼容:Android端的实体类需转换为RN支持的
WritableMap/WritableArray,确保字段类型匹配。 - 错误处理:两端都要覆盖网络异常、API返回错误等场景,给出友好提示。
- 依赖优化:实际项目建议用Hilt等依赖注入框架管理ViewModel、Repository实例,避免硬编码初始化。
内容的提问来源于stack exchange,提问作者Ashutosh
相关产品推荐
相关产品推荐

