巧用Kotlin扩展方法修改第三方库的代码(主要是为了解决No static method encodeBase64String([B)Ljava/lang/String; in class Lorg/apache/commons/codec/binary/Base64; 问题)

公司业务中需要用到阿里云的视觉智能图像搜索库,但是这个库只提供了java sdk,并没有android sdk。直接移植到android上,会出现bug:

java.lang.NoSuchMethodError: No static method encodeBase64String([B)Ljava/lang/String; in class Lorg/apache/commons/codec/binary/Base64; or its super classes (declaration of 'org.apache.commons.codec.binary.Base64' appears in /system/framework/org.apache.http.legacy.boot.jar)
        at com.aliyun.openapiutil.Client.getRPCSignature(Client.java:373)
        at com.aliyun.teaopenapi.Client.doRPCRequest(Client.java:179)
        at com.aliyun.imgsearch20200320.Client.searchImageWithOptions(Client.java:199)
        at com.xxx.xxx.AliRawSearch.search(AliRawSearch.kt:42)
        at com.xxx.xxx.ViewModel$search$1.invokeSuspend(ViewModel.kt:20)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

定位下去,原来是阿里云java sdk在向服务器穿参数的时候用到了Base64加密,具体使用org.apache.commons.codec.binary.Base64.encodeBase64String()这个方法。然而Android系统也有这个库,覆盖了sdk的引用,但是版本太老了,没有这个方法,所以才会报错。

网上的解决方案很多,但是大多数是针对自己写加密的情况,换一个库就可以了;针对第三方的库中包含Base64.encodeBase64String()的没有太好的解决方案,这里提供一个思路:用kotlin的扩展函数的功能,重新实现相关函数,直到将包含Base64.encodeBase64String()的函数重写,并替换其中的Base64.encodeBase64String()为可用的方法。

实例如下:

跟着调试信息一步一步向前查询,依次将com.aliyun.imgsearch20200320.Client.searchImageWithOptions()com.aliyun.teaopenapi.Client.doRPCRequest()com.aliyun.openapiutil.Client.getRPCSignature()的代码复制出来,并替换为自己重写的方法:

先看sdk库中的函数调用流程:

public SearchImageResponse searchImageWithOptions(SearchImageRequest request, RuntimeOptions runtime) throws Exception {
       // 其他功能实现
	return (SearchImageResponse)TeaModel.toModel(
		this.doRPCRequest("SearchImage", "2020-03-20", "HTTPS", "POST", "AK", "json", req, runtime), // doRPCRequest()这个方法报错
		new SearchImageResponse()
	);
    }

public Map<String, ?> doRPCRequest(String action, String version, String protocol, String method, String authType, String bodyType, OpenApiRequest request, RuntimeOptions runtime) throws Exception {
        // 其他功能实现
	request_.query.put(
		"Signature", 
		com.aliyun.openapiutil.Client.getRPCSignature(signedParam, request_.method, str)	// getRPCSignature()这个方法报错
	);
                    }
					
public static String getRPCSignature(Map<String, String> signedParams, String method, String secret) throws Exception {
	return Base64.encodeBase64String(signData);		// 这段代码报错
    }

这样一来,我们只需要将这三个函数重新实现,并替换报错的代码为正确的代码就可以了:

// 自己创建文件,直接复制上面函数的代码,重新实现上述函数的功能,并替换相关函数
// 重新实现的函数在函数名末尾加R以做区别
@Throws(java.lang.Exception::class)
fun com.aliyun.imgsearch20200320.Client.searchImageWithOptionsR(request: SearchImageRequest,runtime: RuntimeOptions): SearchImageResponse {
    // 其他功能实现
    return TeaModel.toModel(
        doRPCRequestR("SearchImage","2020-03-20","HTTPS","POST","AK","json",req,runtime),		// 调用重写的函数
        SearchImageResponse()
    )
}

@Throws(java.lang.Exception::class)
fun com.aliyun.teaopenapi.Client.doRPCRequestR(action: String?,version: String?,protocol: String?,method: String?,authType: String?,bodyType: String?,request: OpenApiRequest,runtime: RuntimeOptions): Map<String, *> {
    // 其他功能实现
                request_.query["Signature"] =
                    getRPCSignatureR(signedParam, request_.method, str)		// 调用重写的函数
           
}

@Throws(java.lang.Exception::class)
fun com.aliyun.teaopenapi.Client.getRPCSignatureR(signedParams: Map<String, String>,method: String,secret: String): String {
    // 其他功能实现
		// Base64.encodeBase64String(signData)		// 这句代码报错,注释掉
		String(Base64.encodeBase64(signData))		// 替换为可行的实现方式
    }

重写完成后,再调用searchImageWithOptionsR()实现功能即可。

posted @ 2022-06-16 10:10  李振欣  阅读(460)  评论(0编辑  收藏  举报