Android反编译(三)— 手动编译
发布时间:2025-05-15 09:22:06 发布人:远客网络
一、Android反编译(三)— 手动编译
1、 PS:最近没工作,没工作就没需求,没需求就没什么技术总结的灵感,那就没更新什么。但是两个月不更新了,要是三个月不更新就会出大事,所以这次打算做一件有意思又不难的事。
2、之前有发文章写过反编译,今天就来试试反编译之正编译,开玩笑的,就是试试手动编译的过程,平时我们在项目中编译出包都是使用Gradle直接执行assemble任务就能解决,我打算试试手动模拟整个过程。当然我也是第一次这样搞,所以如果有写得不对的地方,还望指出。
3、众所周知,apk实质上就是一个压缩包。复习一下,我们写个最简单的Demo,然后打包,然解压,注意是解压,不是反编译,意义是不同的。
4、注意我这个Demo很简单,什么都不引入
5、然后我们看看整个出包的过程,随便从网上拿张图
6、然后这里我们用Android SDK给我们提供的工具来完成整个流程,工具在sdk文件夹下的build-tools文件夹下,有什么aapt.exe、dx.bat,用的就是这些
7、这步应该是整个流程最简单的吧,我感觉,所以从最简单的开始。
8、对比项目,我是一开始最基本的项目,什么都没动,所以只有一个MainActivity.clas,所以这里肯定是要先想办法得到BuildConfig.class和R.class。
9、 aapt p-f-m-J<输出路径>-S<res路径>-I<android.jar路径>-M<Manifest路径>
10、下一步,我们需要BuildConfig.class
11、这个BuildConfig.java是由gradle在我们配置好gradle之后自己帮我们生成的,所以我们直接拿来用,然后再javac就得到class文件了
12、然后我们再编译我们的MainActivity.java并将它们放到同一个文件夹下, MainActivity因为引用了Android.jar和R文件,所以编译时注意点,我为此被动好好的复习了一遍javac,都是泪
13、最后一步,我们用dx工具就能打出dex文件了
14、然后执行命令就得到一个Dex文件,看看这个文件里面和上面直接打出的apk中的Dex文件有什么不同:
15、看图,我们上一步已经生成.dex了,那么我们需要和compiled Resource还有 Other Resource一起生成APK。
16、我们先来生成compiled Resource,也就是resources.arsc
17、发现之前使用aapt生成R文件的时候没写完整,当时可以加一个-F参数直接生成arsc和Manifest
18、导出的abc.zip里面就有resources.arsc和AndroidManifest.xml。
19、因为之前写漏了,所以肯定要重新编一次MainActivity.java和Dex
20、我们把刚才的dex文件和aapt生成的resources.arsc、AndroidManifest.xml和res放到一个文件夹里面。
21、 PS:res文件夹也是上面aapt的命令生成的
22、然后我们对比这个文件夹和之前apk解压的文件夹
23、再说说遇到的还有两个问题,并说下我解决问题的思路
24、(1)我把它们都放到一个文件夹之后,我压缩成压缩包,然后改后缀成.apk,然后发现安装不了,我就直接反编译,发现发编译失败,提示包有问题,以我多点玩包的经验,我感觉就是压缩工具出了问题,然后我去下个“好压”(这不是广告啊),然后就能正常反编译了。
25、(2)但是还是安装不了,再根据我多年的玩包经验,我感觉是签名问题,然后我随便给这个包上一个签名,就能正常安装得到上图的结果了。
26、总体来说,还真挺好玩的,这整个过程,就是翻车了几次。做完之后感觉非常牛逼,为什么这样说,因为我知道这整个过程,我就可以做到,我不经过gradle来打包,我自己写个python脚本来调用aapt和dx来打包也是能做到的。
27、当然上面纯属异想天开,因为这是个什么都没有的Demo所以觉得简单,要是一个真实的项目,我感觉肯定要有很多坑,别的先不说,一个项目那么多依赖关系,我这javac要搞死人。
28、最后如果有不对的地方,希望有大佬能够指出,毕竟能运行也不能证明完全没问题。然后我使用的build-tools是28的,不敢保证其它版本包括以后版本的玩法都一样。
二、Android Studio release版本去掉Log信息
1、在我们用我们的签名打包成apk时,为了apk运行更快不希望再有日志输出。我在这里总结了几种方法
2、第一种方法.利用 timber lib来进行限制
3、 2)然后重新编译下,通过之后配置自己的Application
4、3).当在debug模式时使用log日志方式是
5、调整到release模式下则只有Log.ERROR,Log.WARN能够打印出来
6、 build.gradle中buildTypes中配置buildConfigField
7、这个方法接收三个非空的参数,第一个:确定值的类型,第二个:指定key的名字,第三个:传值
8、上面的意思是:为LOG_DEBUG= true
9、那这个值怎么读取呢?在Groovy中,直接由BuildConfig类点出key名来取值,如下
三、android app 如何与uvc摄像头通讯
来看看是怎么操作UVC摄像头的吧.我们实现了一个专门检测UVC摄像头的服务:UVCCameraService类,主要代码如下:
mUSBMonitor= new USBMonitor(this, new USBMonitor.OnDeviceConnectListener(){@Override
public void onAttach(final UsbDevice device){
Log.v(TAG,"onAttach:"+ device);
mUSBMonitor.requestPermission(device);
public void onConnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock, final boolean createNew){
releaseCamera(); if(BuildConfig.DEBUG) Log.v(TAG,"onConnect:"); try{ final UVCCamera camera= new MyUVCCamera();
camera.setStatusCallback(new IStatusCallback(){//... uvc摄像头链接成功
Toast.makeText(UVCCameraService.this,"UVCCamera connected!", Toast.LENGTH_SHORT).show(); if(device!= null)
cameras.append(device.getDeviceId(), camera);
public void onDisconnect(final UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock){//... uvc摄像头断开链接
UVCCamera camera= cameras.get(device.getDeviceId()); if(mUVCCamera== camera){
Toast.makeText(UVCCameraService.this,"UVCCamera disconnected!", Toast.LENGTH_SHORT).show();
cameras.remove(device.getDeviceId());
Toast.makeText(UVCCameraService.this,"UVCCamera disconnected!", Toast.LENGTH_SHORT).show();
public void onCancel(UsbDevice usbDevice){
public void onDettach(final UsbDevice device){
releaseCamera();// AppContext.getInstance().bus.post(new UVCCameraDisconnect());
});123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
这个类主要实现UVC摄像头的监听\链接\销毁\反监听.当有UVC摄像头链接成功后,会创建一个mUVCCamera对象.
然后在MediaStream里,我们改造了switchCamera,当参数传2时,表示要切换到UVCCamera(0,1分别表示切换到后置\前置摄像头).
在创建摄像头时,如果是要创建uvc摄像头,那直接从服务里面获取之前创建的mUVCCamera实例:
UVCCamera value= UVCCameraService.liveData.getValue(); if(value!= null){// uvc camera.
value.setPreviewSize(width, height,1, 30, UVCCamera.PIXEL_FORMAT_YUV420SP,1.0f); return;// value.startPreview();
uvcError= new Exception("no uvccamera connected!"); return;
在预览时,如果uvc摄像头已经创建了,那执行uvc摄像头的预览操作:
UVCCamera value= uvcCamera;if(value!= null){
SurfaceTexture holder= mSurfaceHolderRef.get(); if(holder!= null){
value.setPreviewTexture(holder);
value.setFrameCallback(uvcFrameCallback, UVCCamera.PIXEL_FORMAT_YUV420SP/*UVCCamera.PIXEL_FORMAT_NV21*/);
cameraPreviewResolution.postValue(new int[]{width, height});
这里我们选的colorFormat为PIXEL_FORMAT_YUV420SP相当于标准摄像头的NV21格式.
同理,关闭时,调用的是uvc摄像头的关闭.
UVCCamera value= uvcCamera; if(value!= null){
因为我们这里并没有实质性的创建,所以销毁时也仅将实例置为null就可以了.
UVCCamera value= uvcCamera;if(value!= null){// value.destroy();
有了这些操作,我们看看上层怎么调用,
首先需要在Manifest里面增加若干代码,具体详见UVCCamera工程说明.如下:
<activity android:name=".UVCActivity" android:launchMode="singleInstance">
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"/>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter"/>
</activity>1234567891011121314151617181920
然后,的代码在UVCActivity里,这个类可以在library分支的myapplication工程里找到.即这里.
public void onPush(View view){//异步获取到MediaStream对象.
getMediaStream().subscribe(new Consumer<MediaStream>(){@Override
public void accept(final MediaStream mediaStream) throws Exception{//判断当前的推送状态.
MediaStream.PushingState state= mediaStream.getPushingState(); if(state!= null&& state.state> 0){//当前正在推送,那终止推送和预览
mediaStream.closeCameraPreview();
}else{// switch 0表示后置,1表示前置,2表示UVC摄像头
RxHelper.single(mediaStream.switchCamera(2), null).subscribe(new Consumer<Object>(){@Override
public void accept(Object o) throws Exception{//开启成功,进行推送.
mediaStream.startStream("cloud.easydarwin.org","554", id);
}, new Consumer<Throwable>(){@Override
public void accept(final Throwable t) throws Exception{// ooop...开启失败,提示下...
runOnUiThread(new Runnable(){@Override
Toast.makeText(UVCActivity.this,"UVC摄像头启动失败.."+ t.getMessage(), Toast.LENGTH_SHORT).show();
}12345678910111213141516171819202122232425262728293031323334353637
这样,整个推送就完成了.如果一切顺利,应当能在VLC播放出来UVC摄像头的视频了~~
我们再看看如何录像.也非常简单…
public void onRecord(View view){//开始或结束录像.
final TextView txt=(TextView) view;
getMediaStream().subscribe(new Consumer<MediaStream>(){@Override
public void accept(MediaStream mediaStream) throws Exception{ if(mediaStream.isRecording()){//如果正在录像,那停止.
//表示最大录像时长为30秒,30秒后如果没有停止,会生成一个新文件.依次类推...
//文件格式为test_uvc_0.mp4,test_uvc_1.mp4,test_uvc_2.mp4,test_uvc_3.mp4
String path= getExternalFilesDir(Environment.DIRECTORY_MOVIES)+"/test_uvc.mp4";
mediaStream.startRecord(path, 30000); final TextView pushingStateText= findViewById(R.id.pushing_state);
pushingStateText.append("\n录像地址:"+ path);
}123456789101112131415161718192021
UVC摄像头还支持后台推送,即不预览的情况下进行推送,同时再切换到前台继续预览.只需要调用一个接口即可实现,如下:
@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1){
ms.setSurfaceTexture(surfaceTexture);//设置预览的surfaceTexture}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture){
ms.setSurfaceTexture(null);//设置预览窗口为null,表示关闭预览功能
如果要彻底退出uvc摄像头的预览\推送,那只需要同时退出服务即可.
public void onQuit(View view){//退出
Intent intent= new Intent(this, MediaStream.class);