须知少年凌云志,曾许人间第一流。这篇文章主要讲述安卓热修复之AndFIX相关的知识,希望能为你提供帮助。
我致力于最新的前沿安卓技术分析和使用教学,不打算将很多很深的东西,因为有多少人愿意沉下你的心境去学习难点?我一般只会简单提及.文字错漏在所难免还希望同学们喜欢热修复介绍 热修复是什么? 如果你一个项目已经上线,出现了严重缺陷,那么你第一反应是推送新版本.那么问题来.老子刚下你的APP 你就叫我重新下载?啥东西!卸了.从而导致用户流量的减退.而热修复就是推送一个补丁文件到客户端(很小),用户打开应用时自动安装.是不是很棒?
AndFix 热修复原理 已经翻译的源码分析
阿里github官网(英文)
推荐自行看官网
如果时间上允许我会帮大家翻译完整版的
AndFix注意的地方
- 不支持添加字段 方法 资源 布局修改 类
- 虽然不支持添加字段 但是你可以在方法内添加 局部变量(后面解释)
1. 如果添加了在生成补丁包会有如下错误(大家先看看不需要先知道怎么生成,后面阐述)
文章图片
成功:
仔细看 上面的文字会有什么add modifie(添加修改) com.xxx(你修改的文件)
文章图片
AndFix使用
- 首先添加依赖
dependencies { compile ' com.alipay.euler:andfix:0.5.0@ aar' }
- 初始化 一般在application的实现类
我们首先创建一个类继承之 application 然后在oncreat方法初始化,
我先介绍几个API:
此API返回你的应用版本名()
java
String appversion; appversion= getPackageManager().getPackageInfo(getPackageName(),0).ersionName;
文章图片
再来看个依赖包的类PatchManager
public PatchManager patchManager ;
此类有如下几个方法
参数为上下文 一般都在application中
patchManager = new PatchManager(this);
初始化 这里传入的是版本名 可以用上面介绍的一个api填入 也可以直接写死
patchManager.init(" 1.0" ); patchManager.init(versionName);
初始化加载补丁 (你直接当写死就行一般默认在new出来并初始版本后调用.这里还是不真正的加载方法)
patchManager.loadPatch();
打入补丁 参数为字符串 打入后生效 路径你随意写 文件名也是一样
addPatch(你的补丁路径)
//移除补丁路径
patchManager.removeAllPatch();
String path = Environment.getExternalStorageDirectory().getAbsoluteFile() + File.separator + " out.apatch" ; File file = new File(path); if (file.exists()){ Log.e(" fmy" ," 文件存在" ); try { patchManager.addPatch(path); patchManager.removeAllPatch(); Log.e(" fmy" ," 热修复成功" ); } catch (IOException e) { Log.e(" fmy" ," 热修复失败:" + e.getMessage()); } }else{ Log.e(" fmy" ," 文件不存在" ); }
完整代码:
package com.example.administrator.myapplication; import android.app.Application; import android.os.Environment; import android.util.Log; import com.alipay.euler.andfix.patch.PatchManager; import java.io.File; import java.io.IOException; /** * Created by Administrator on 2016/11/9. */public class MainAppliacation extends Application {public PatchManager patchManager ; private String path; @ Override public void onCreate() { super.onCreate(); patchManager = new PatchManager(this); patchManager.init(" 1.0" ); patchManager.loadPatch(); path = Environment.getExternalStorageDirectory().getAbsoluteFile() + File.separator + " out.apatch" ; File file = new File(path); if (file.exists()){ Log.e(" fmy" ," 文件存在" ); try { patchManager.addPatch(path); patchManager.removeAllPatch(); Log.e(" fmy" ," 热修复成功" ); } catch (IOException e) { Log.e(" fmy" ," 热修复失败:" + e.getMessage()); } }else{ Log.e(" fmy" ," 文件不存在" ); }} }
- 添加权限和把我们自定义application关联上
权限:
< uses-permission android:name= " android.permission.WRITE_EXTERNAL_STORAGE" /> < uses-permission android:name= " android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
//关联我们自定义application类
< application android:name= " .MainAppliacation" ....
完整清单文件:
< ?xml version= " 1.0" encoding= " utf-8" ?> < manifest xmlns:android= " http://schemas.android.com/apk/res/android" package= " com.example.administrator.myapplication" > < uses-permission android:name= " android.permission.WRITE_EXTERNAL_STORAGE" /> < uses-permission android:name= " android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> < application android:name= " .MainAppliacation" android:allowBackup= " true" android:icon= " @ mipmap/ic_launcher" android:label= " @ string/app_name" android:supportsRtl= " true" android:theme= " @ style/AppTheme" > < activity android:name= " .MainActivity" > < intent-filter> < action android:name= " android.intent.action.MAIN" /> < category android:name= " android.intent.category.LAUNCHER" /> < /intent-filter> < /activity> < /application> < /manifest>
- 生成新旧版本的两个已经签名的APK(签名需一样)
假设我们原本的apk 两个类 一个是activity 类 和一个我们自定义的application类(上面已经说了)
我们看看activity 文件
package com.example.administrator.myapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class MainActivity extends AppCompatActivity {String name = " 你好" ; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }//一个按钮的点击事件 public void onClick(View view) {Toast.makeText(this, name, Toast.LENGTH_SHORT).show(); } }
看下布局文件吧
< ?xml version= " 1.0" encoding= " utf-8" ?> < RelativeLayout xmlns:android= " http://schemas.android.com/apk/res/android" xmlns:tools= " http://schemas.android.com/tools" android:id= " @ + id/activity_main" android:layout_width= " match_parent" android:layout_height= " match_parent" android:paddingBottom= " @ dimen/activity_vertical_margin" android:paddingLeft= " @ dimen/activity_horizontal_margin" android:paddingRight= " @ dimen/activity_horizontal_margin" android:paddingTop= " @ dimen/activity_vertical_margin" tools:context= " com.example.administrator.myapplication.MainActivity" > < Button android:onClick= " onClick" android:layout_width= " wrap_content" android:layout_height= " wrap_content" android:text= " 看看热修复是否成功" /> < /RelativeLayout>
大概的效果图
文章图片
打包签名此apk 并取名为old.apk(这里是为了后面好讲此命名)
修改一下activity文件
修改了name的数值
package com.example.administrator.myapplication; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class MainActivity extends AppCompatActivity {String name = " 修复了!!!!" ; @ Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }//一个按钮的点击事件 public void onClick(View view) {Toast.makeText(this, name, Toast.LENGTH_SHORT).show(); }}
打包签名此apk 并取名为new.apk(这里是为了后面好讲此命名)
- 下载生成补丁工具
阿里github官网(英文)
在github下载源码不用我教吧?把阿里整个master下载到本地 并解压 ![这里写图片描述](https://img-blog.csdn.net/20161109113056564)
打开上述目录的tools目录 解压工具包到你喜欢的位置
文章图片
现在我打开我解压的后的目录
文章图片
- 将我们生成的new.apk 和old.apk 还有签名文件一并放入此文件夹
文章图片
- 生成补丁
在此目录下按下Shit键+ 鼠标右键 选择 可以看到一个选项为”在此处打开一个命令窗口” —> > 不好截图 一点截图就关掉了所以 I’m so sorry
在跳出命令窗口输入如下命令
命令 : apkpatch.bat -f new.apk -t old.apk -o output1 -k debug.keystore -p android -a androiddebugkey -e android-f < new.apk> : 新版本 -t < old.apk> : 旧版本 -o < output> : 输出目录 -k < keystore> : 打包所用的keystore -p < password> : keystore的密码 -a < alias> : keystore 用户别名 -e < alias password> : keystore 用户别名密码
提示在 dos 窗口按下tab 输入此文件夹下的某个文件名首字母或者一段可以提示哦
文章图片
-o o 我这里的用法是 在此目录下创建一个o文件夹然后将生产的东西放入此文件夹
我们打开看看我们的o文件夹有什么
文章图片
可以看到有一个叫
“new-4d459be7271c372742862b1a885b176a.apatch”的文件
这个就是我们需要的 其他文件用处我以后会补充的 如加固的一些问题
我们把此文件改名 “out.apatch”
现在呢我们演示一下吧:
首先安装old.apk 到手机中 我们运行看看
文章图片
我们把文件out.apath导入到此模拟器的sd卡根目录 在关闭程序(记得清后台!!!!!) 不然你不会重新调用application的oncreat
文章图片
What ?为什么没有变成我们后面改成的字符串?其实是我故意写下的一个坑 我的目的很简单 如果让同学一帆风顺的写下来你永远不会影响深刻对AndFIX哪些部分可以热修复 如前文 布局文件等
这里之所以 不出来 是因为他把重新再name的类中赋值当成重新生成了一个字段(前面说过新字段不可以热修复),所以解锁方法如下
package com.example.administrator.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {String name =
"
你好"
;
@
Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}//一个按钮的点击事件
public void onClick(View view) {
name =
"
修复了"
;
Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
}}
然后重新生成一个new.apk 在次和签名文件生成一个新的补丁效果就出来了.这里我就不重复无聊的步骤了,我现在导入正确的补丁
文章图片
(^__^) 我很无聊吧?
混淆
# 先版本两个字换成你的发布版本 一般是release
#debug
# release
-applymapping build/outputs/mapping/换成你的版本(
一般是release版本)
/mapping.txt
-keep class * extends java.lang.annotation.Annotation
-keep class com.alipay.euler.** {*;
}
-keepclasseswithmembernames class * { # 保持 native 方法不被混淆
native <
methods>
;
}
【安卓热修复之AndFIX】第一次编译的时候注释这行( 前面加个#号)
-applymapping build/outputs/mapping/换成你的版本(
一般是release版本)
/mapping.txt
推荐阅读
- 安卓自定义View文章数据滚动显示数值
- android单位尺寸理解
- Android Studio第十七期 - 自定义圆形进度条
- android 项目学习随笔八(xUtils的BitmapUtils模块)
- Android中解析读取复杂word,excel,ppt等的方法
- Android 仿QQ新浪相册的实现
- AndroidMVP模式
- android调用系统拍照那些事
- 使用Fiddler分析Android版API