美团Robust热更新

官方Github:https://github.com/Meituan-Dianping/Robust

0、前言

    网上关于robust使用的帖子很多,大部分帖子都会忽略某些细节,在没有搞明白robust原理的情况下,往往因为这些细节,导致热更新失败。我在用robust时也是各种碰壁,在此总结和重新梳理robust的应用流程。

1、总体流程

如果我们要写一个robust的demo,实现热更新功能,应该分下面几个步骤。

第一步:写一个有“bug”的demo

第二步:开启热更新,修改代码,

第三步:生成和部署jar包

第四步:测试热更新效果。

2、分步实现

    这里分步实现各个流程。follow me!

2.1、写一个有“bug”的demo

(1)声明权限

首先的首先是建一个android工程,然后声明权限。

    Robust需要在AndroidManifest中声明如下权限(如图1-0)

图1-0

(2)app的gradle文件中引入依赖

apply plugin:'com.android.application'

//apply plugin: 'auto-patch-plugin'(热更新代码后要开启这行,并注释掉下面那行

apply plugin:'robust'

compile'com.meituan.robust:robust:0.4.5'

图-1-1
图-1-2

(3)工程的gradle文件加入classpath

classpath 'com.meituan.robust:gradle-plugin:0.4.5'

classpath 'com.meituan.robust:auto-patch-plugin:0.4.5'


图1-3

(4)手动增加和修改robust.xml文件

注意事项:

1、robust.xml文件和src是同级目录,如下图1-4

图-1-4

2、xml文件是从上面官方github的demo中拷贝过来的。这个xml是热更新的一个配置文件。

3、修改robust.xml文件

一般来说只需要修改三个地方,如下图1-5.

图1-5
图1-6

(5)写demo

用三个class文件(分别是:MainActivity.class, SecondActiviry.class, PatchManipulateImp.class)来写一个简单的demo。

【MainActivity.class】

package com.gzyct.myapplication;

public class MainActivity extends AppCompatActivity {

protected Button load;

protected Button skip;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

super.setContentView(R.layout.main);

initView();

}

/**

*初始化界面

*/

private void initView() {

// 加载patch

load= (Button) findViewById(R.id.load);

load.setOnClickListener(newView.OnClickListener() {

@Override

public voidonClick(View v) {

newPatchExecutor(getApplicationContext(),newPatchManipulateImp(),newcallBack()).start();

}

});

// 页面跳转

skip= (Button) findViewById(R.id.skip);

skip.setOnClickListener(newView.OnClickListener() {

@Override

public voidonClick(View v) {

Intent intent =newIntent(MainActivity.this, SecondActivity.class);

startActivity(intent);

}

});

}

private class callBack  implements RobustCallBack {

@Override

public void onPatchListFetched(booleanresult,booleanisNet, List patches) {

// TODO

}

@Override

public void onPatchFetched(booleanresult,booleanisNet, Patch patch) {

// TODO

}

@Override

public void onPatchApplied(booleanresult, Patch patch) {

// TODO

}

@Override

public void logNotify(String log, String where) {

// TODO

}

@Override

public void exceptionNotify(Throwable throwable, String where) {

// TODO

}

}

}

[MainActivity的UI图]


图1-7

【PatchManipulateImp.class文件代码】

这段代码中要重载三个函数:

protectedList fetchPatchList(Context context)

protected booleanverifyPatch(Context context, Patch patch)

protected booleanensurePatchExist(Patch patch)

.fetchPatchList是作用什么用?

1、保存原始jar包存储的路径以及设置运行的jar包

.verifyPatch做什么用?

you can verify your patches here。官方demo的解释


package com.gzyct.myapplication;

import android.content.Context;

import android.os.Environment;

import com.meituan.robust.Patch;

import com.meituan.robust.PatchManipulate;

importjava.io.File;

importjava.util.ArrayList;

importjava.util.List;

/**

* Created by gt on 2017/11/15.

*/

public class PatchManipulateImp  extends  PatchManipulate {

@Override

protected List fetchPatchList(Context context) {

//将app自己的robustApkHash上报给服务端,服务端根据robustApkHash来区分每一次apk build来给app下发补丁

//apkhash is the unique identifier for  apk,so you cannnot patch wrong apk.

//String robustApkHash = RobustApkHashUtils.readRobustApkHash(context);

Patch patch =newPatch();

patch.setName("123");

//we recommend LocalPath store the origin patch.jar which may be encrypted,while TempPath is the true runnable jar

// 下面的robust是生成补丁后,存在在手机的robust文件夹在中。如果你改成qwe,生成的补丁就放在qwe文件夹中。所以部署jar包到手机的时候,相关adb命令的参数和这里要对应上。

patch.setLocalPath(Environment.getExternalStorageDirectory().getPath() + File.separator+"robust"+ File.separator+"patch.jar");

patch.setTempPath(Environment.getExternalStorageDirectory().getPath() + File.separator+"robust"+ File.separator+"patch");

//setPatchesInfoImplClassFullName的包名是图1-5第三个红色框中的包名+PatchesInfoImpl, 这样robust可以通过xml文件找到当前类

patch.setPatchesInfoImplClassFullName("com.gzyct.myapplication.PatchesInfoImpl");

List patches =newArrayList();

patches.add(patch);

returnpatches;

}

@Override

protected booleanverifyPatch(Context context, Patch patch) {

return true;

}

@Override

protected booleanensurePatchExist(Patch patch) {

return true;

}

}

【SecondActivity.class的代码】

public class  SecondActivity  extends  Activity {

@Override

protected void onCreate(@NullableBundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.second);

title= (TextView) findViewById(R.id.title);

title.setText("hello , this is patched activity,and patch is success-13231313131123441412---");

title.setOnClickListener(newView.OnClickListener() {

@Override

public void onClick(View v) {

Toast.makeText(getApplicationContext(),"asfawdfawf", Toast.LENGTH_LONG).show();

}

});

init();

}

TextViewtitle;

private voidinitView() {

title= (TextView) findViewById(R.id.title);

title.setText("hello , this is patched activity");

}

【SecondActivity.classUI图】

图1-8

至此一个有“bug”的demo就写完了。

写完代码后,开始打包,下面将介绍如何打包以及热更新的准备工作。


2.2 打包


.(1)开启混淆

在app的gradle文件中开发混淆

图1-9

.(2)打包

可以通过命令方式或者传统方式打包。(我是用传统方式打包滴)

.通过命令打包

gradlew clean  assembleRelease --stacktrace --no-daemon

图1-10

.传统方式打包

图1-11

.2.3、开始热更新和修改代码

开启热更新前,有些准备工作要做,如下

(1)准备工作

.新建robust文件夹

src的同级目录新建robust文件夹


图1-12

.拷贝上面红线的文件到robust文件夹

在打包后(借用人家的图),在output下面将这几个文件夹拷贝到刚才我们新建的robust文件夹中。


图1-13


图1-14

(2)开启“热更新模式”

在app的gradle文件中,修改配置,入下图1-15,注释掉第三行,并开启第二行,和写bug代码时刚好相反。

图1-15

(3)修改“bug”

在secondactivity.class中修改“bug”

图1-16

2.4、生成和部署补丁包

(1)生成补丁包

补丁包可以通过命令生成,也可以用传统的签名打包方式生成。这里介绍用命令方式生成补丁,具体如下:

通过命令:gradlew clean  assembleRelease --stacktrace --no-daemon生成补丁(如下图1-17):

图 1-17

执行结束后,出现BUILD FAILED,没有关系。只要patch end successfully就ok啦!~~(图下图1-18)

图1-18

        接下来,我们可以发现:在app->build->output->robust目录下生成patch.jar以及patch.dex两个文件(如下图1-19)。

图1-19

(2)热更新部署

 上面的patch.jar是我们需要的热更新文件。通过adb命令将jar包传到手机(如下图1-20)

图1-20

用adb将jar传到手机时可能会遇到几个坑。

坑1

报错:Unable to create Debug Bridge: Unable to start adb server: error: could not install *smartsocket* listener: cannot bind to 127.0.0.1:5037: 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

原因:端口被占用

解决办法:

step1、通过netstat -aon|findstr “5037” 找出占用5037端口号的对应pid号。

step2、通过任务管理器kill进程

step3:重启adb


图1-21

坑2

报错:no device *** command ***

没有安装手机驱动:(

4、测试


T 1-21


T1-22


在没有load patch,直接skip new activity时的UI如下:


T1-23

load patch之后


T1-24

测试结论:

热更新成功

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容