Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ buildscript {

plugins {
id 'com.android.application'
//制作补丁时将这个打开,auto-patch-plugin紧跟着com.android.application
// id 'auto-patch-plugin'
id 'kotlin-android'
id 'kotlin-kapt'
id 'robust'
}

apply plugin: 'study.plugin'
Expand All @@ -23,7 +26,7 @@ android {

defaultConfig {
applicationId "com.example.study"
minSdkVersion 21
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
Expand Down Expand Up @@ -53,6 +56,32 @@ android {
enabled = true
}

signingConfigs {
release {
storeFile file('my-release-key.keystore')
storePassword '123456'
keyAlias 'my-key-alias'
keyPassword '123456'
}
debug {
storeFile file('my-release-key.keystore')
storePassword '123456'
keyAlias 'my-key-alias'
keyPassword '123456'
}
}

lintOptions {
abortOnError false
}

// //打patch的时候才需要,如果没有依赖patch插件,不要添加该内容
// hotfixConfig{
// //patch的包名,不要和bundle的包名一致,可以在后面追加一个.patch
// patchPackName 'ctrip.wireless.android.patch'
// //patch内容的输入目录 路径的后面需要加上分隔符
// hotfixDir 'hotfix_build/outputs/hotfix/'
// }
}

dependencies {
Expand Down Expand Up @@ -98,5 +127,7 @@ dependencies {
// runtimeOnly "com.example.study:shop:1.0.3-SNAPSHOT"
// runtimeOnly "com.example.study:user:1.0.3-SNAPSHOT"

implementation 'ctrip.wireless.android:robust:0.4.99.1'

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
Binary file added app/my-release-key.keystore
Binary file not shown.
72 changes: 72 additions & 0 deletions app/robust.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<switch>
<!--true代表打开Robust,请注意即使这个值为true,Robust也默认只在Release模式下开启-->
<!--false代表关闭Robust,无论是Debug还是Release模式都不会运行robust-->
<turnOnRobust>true</turnOnRobust>
<!--<turnOnRobust>false</turnOnRobust>-->

<!--是否开启手动模式,手动模式会去寻找配置项patchPackname包名下的所有类,自动的处理混淆,然后把patchPackname包名下的所有类制作成补丁-->
<!--这个开关只是把配置项patchPackname包名下的所有类制作成补丁,适用于特殊情况,一般不会遇到-->
<!--<manual>true</manual>-->
<manual>false</manual>

<!--是否强制插入插入代码,Robust默认在debug模式下是关闭的,开启这个选项为true会在debug下插入代码-->
<!--但是当配置项turnOnRobust是false时,这个配置项不会生效-->
<forceInsert>true</forceInsert>
<!-- <forceInsert>false</forceInsert>-->

<!--是否捕获补丁中所有异常,建议上线的时候这个开关的值为true,测试的时候为false-->
<catchReflectException>true</catchReflectException>
<!--<catchReflectException>false</catchReflectException>-->

<!--是否在补丁加上log,建议上线的时候这个开关的值为false,测试的时候为true-->
<!--<patchLog>true</patchLog>-->
<patchLog>false</patchLog>

<!--项目是否支持progaurd-->
<proguard>false</proguard>
<!--<proguard>false</proguard>-->

<!--项目是否支持ASM进行插桩,默认使用ASM,推荐使用ASM,Javaassist在容易和其他字节码工具相互干扰-->
<useAsm>true</useAsm>
<!--<useAsm>false</useAsm>-->

<!--针对Java8级别的Lambda表达式,编译为private级别的javac函数,此时由开发者决定是否进行插桩处理-->
<forceInsertLambda>true</forceInsertLambda>
<!-- <forceInsertLambda>false</forceInsertLambda>-->
</switch>

<!--需要热补的包名或者类名,这些包名下的所有类都被会插入代码-->
<!--这个配置项是各个APP需要自行配置,就是你们App里面你们自己代码的包名,
这些包名下的类会被Robust插入代码,没有被Robust插入代码的类Robust是无法修复的-->
<packname name="hotfixPackage">
<name>com.example.study</name>
</packname>

<!--不需要Robust插入代码的包名,Robust库不需要插入代码,如下的配置项请保留,还可以根据各个APP的情况执行添加-->
<exceptPackname name="exceptPackage">
</exceptPackname>

<!--不需要设置public的类-->
<publicExceptClassname name="publicExceptClassname">
<!--匹配R文件优化后找不到R外部类的情况-->
<name>.+\.R\$.+</name>
<name>com.appsflyer.internal.a$a</name>
<name>com.appsflyer.internal.a$c</name>
<name>com.appsflyer.internal.a$e</name>
<name>com.appsflyer.internal.a$b</name>
</publicExceptClassname>

<!--补丁的包名,请保持和类PatchManipulateImp中fetchPatchList方法中设置的补丁类名保持一致( setPatchesInfoImplClassFullName("com.meituan.robust.patch.PatchesInfoImpl")),
各个App可以独立定制,需要确保的是setPatchesInfoImplClassFullName设置的包名是如下的配置项,类名必须是:PatchesInfoImpl-->
<patchPackname name="patchPackname">
<name>com.meituan.robust.patch</name>
</patchPackname>

<!--自动化补丁中,不需要反射处理的类,这个配置项慎重选择-->
<noNeedReflectClass name="classes no need to reflect">

</noNeedReflectClass>
</resources>
Binary file added app/robust/baksmali-2.1.2.jar
Binary file not shown.
Binary file added app/robust/dx.jar
Binary file not shown.
Binary file added app/robust/methodsMap.robust
Binary file not shown.
Binary file added app/robust/smali-2.1.2.jar
Binary file not shown.
9 changes: 8 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".MyApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Study">
Expand All @@ -23,7 +27,10 @@
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="remove"/>
tools:node="merge">
<meta-data android:name="com.example.study.init.MapInitializer"
android:value="androidx.startup" />
</provider>

<activity
android:name=".MainActivity"
Expand Down
47 changes: 45 additions & 2 deletions app/src/main/java/com/example/study/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,28 @@ package com.example.study
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.whenResumed
import com.example.study.databinding.ActivityMainBinding
import com.example.study.hotfix.PatchManipulateImp
import com.example.study.hotfix.PermissionUtils
import com.example.study.hotfix.RobustCallBackSample
import com.example.study.init.TaskStartup
import com.example.study.ui.ComponentActivity
import com.example.study.ui.MultithreadActivity
import com.example.study.ui.RVActivity
import com.meituan.robust.patch.annotaion.Modify
import kotlinx.coroutines.*
import kotlin.coroutines.resume
import com.meituan.robust.PatchExecutor
import com.meituan.robust.PatchManipulate
import com.example.study.hotfix.PermissionUtils.isGrantSDCardReadPermission





class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -62,11 +74,42 @@ class MainActivity : AppCompatActivity() {
}

fun openComponentActivity(view: View) {
startActivity(Intent(this, ComponentActivity::class.java))
if(PermissionUtils.isGrantSDCardReadPermission(this)) {
PatchExecutor(applicationContext, PatchManipulateImp(), RobustCallBackSample()).start()
}else {
PermissionUtils.requestSDCardReadPermission(this, REQUEST_CODE_SDCARD_READ);
}
}

private val REQUEST_CODE_SDCARD_READ = 1

fun openMultithreading(view: View) {
startActivity(Intent(this, MultithreadActivity::class.java))
Toast.makeText(this, "未修复", Toast.LENGTH_SHORT).show()
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
REQUEST_CODE_SDCARD_READ -> handlePermissionResult()
else -> {
}
}
}

private fun handlePermissionResult() {
if (isGrantSDCardReadPermission(this)) {
PatchExecutor(applicationContext, PatchManipulateImp(), RobustCallBackSample()).start()
} else {
Toast.makeText(
this,
"failure because without sd card read permission",
Toast.LENGTH_SHORT
).show()
}
}

}
117 changes: 117 additions & 0 deletions app/src/main/java/com/example/study/hotfix/PatchManipulateImp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.example.study.hotfix;

import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.meituan.robust.Patch;
import com.meituan.robust.PatchManipulate;
import com.meituan.robust.RobustApkHashUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;


/**
* Created by chenyy on 2022/1/17.
*/

public class PatchManipulateImp extends PatchManipulate {
/***
* connect to the network ,get the latest patches
* l联网获取最新的补丁
* @param context
*
* @return
*/
@Override
protected List<Patch> 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);
Log.w("robust","robustApkHash :" + robustApkHash);
//connect to network to get patch list on servers
//在这里去联网获取补丁列表
Patch patch = new Patch();
patch.setName("123");
//we recommend LocalPath store the origin patch.jar which may be encrypted,while TempPath is the true runnable jar
//LocalPath是存储原始的补丁文件,这个文件应该是加密过的,TempPath是加密之后的,TempPath下的补丁加载完毕就删除,保证安全性
//这里面需要设置一些补丁的信息,主要是联网的获取的补丁信息。重要的如MD5,进行原始补丁文件的简单校验,以及补丁存储的位置,这边推荐把补丁的储存位置放置到应用的私有目录下,保证安全性
patch.setLocalPath(Environment.getExternalStorageDirectory().getPath()+ File.separator+"robust"+File.separator + "patch");

//setPatchesInfoImplClassFullName 设置项各个App可以独立定制,需要确保的是setPatchesInfoImplClassFullName设置的包名是和xml配置项patchPackname保持一致,而且类名必须是:PatchesInfoImpl
//请注意这里的设置
patch.setPatchesInfoImplClassFullName("ctrip.wireless.android.patch.PatchesInfoImpl");
List patches = new ArrayList<Patch>();
patches.add(patch);
return patches;
}

/**
*
* @param context
* @param patch
* @return
*
* you can verify your patches here
*/
@Override

protected boolean verifyPatch(Context context, Patch patch) {
//do your verification, put the real patch to patch
//放到app的私有目录
patch.setTempPath(context.getCacheDir()+ File.separator+"robust"+File.separator + "patch");
//in the sample we just copy the file
try {
copy(patch.getLocalPath(), patch.getTempPath());
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("copy source patch to local patch error, no patch execute in path "+patch.getTempPath());
}

return true;
}
public void copy(String srcPath,String dstPath) throws IOException {
File src=new File(srcPath);
if(!src.exists()){
throw new RuntimeException("source patch does not exist ");
}
File dst=new File(dstPath);
if(!dst.getParentFile().exists()){
dst.getParentFile().mkdirs();
}
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dst);
try {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
out.close();
}
} finally {
in.close();
}
}
/**
*
* @param patch
* @return
*
* you may download your patches here, you can check whether patch is in the phone
*/
@Override
protected boolean ensurePatchExist(Patch patch) {
return true;
}
}
Loading