从零开始分析InstantRun源码,java面试评语及录用意见

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

if (isMainProcess()) { //只支持主进程
Log.i(“InstantRun”, “starting instant run server: is main process”);
Server.create(getContext());
return true;
}
Log.i(“InstantRun”, “not starting instant run server: not main process”);
return true;
}

然后启动一个socket监听Android Studio推送的消息

private class SocketServerThread extends Thread {
private SocketServerThread() {}

public void run() {
while (true) {
try {
LocalServerSocket localServerSocket = Server.this.serverSocket;
if (localServerSocket == null)
return;
LocalSocket localSocket = localServerSocket.accept();
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Received connection from IDE: spawning connection thread”);
(new Server.SocketServerReplyThread(localSocket)).run();
if (wrongTokenCount > 50) {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Stopping server: too many wrong token connections”);
Server.this.serverSocket.close();
return;
}
} catch (Throwable throwable) {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Fatal error accepting connection on local socket”, throwable);
}
}
}
}

然后在SocketServerReplyThread的run方法值接受数据并处理

//处理补丁
private int handlePatches(List paramList, boolean paramBoolean, int paramInt) {
if (paramBoolean)
FileManager.startUpdate();
for (ApplicationPatch applicationPatch : paramList) {
String str = applicationPatch.getPath();
if (str.equals(“classes.dex.3”)) { //如果有classes.dex.3处理热插拔
paramInt = handleHotSwapPatch(paramInt, applicationPatch);
continue;
}
if (isResourcePath(str))
//处理资源补丁
paramInt = handleResourcePatch(paramInt, applicationPatch, str);
}
if (paramBoolean)
FileManager.finishUpdate(true);
return paramInt;
}

这里先来看看ApplicationPatch是什么

public static List read(DataInputStream paramDataInputStream) throws IOException {
int j = paramDataInputStream.readInt();
if (Log.logging != null && Log.logging.isLoggable(Level.FINE))
Log.logging.log(Level.FINE, “Receiving " + j + " changes”);
ArrayList arrayList = new ArrayList(j);
for (int i = 0; i < j; i++) {
String str = paramDataInputStream.readUTF();
byte[] arrayOfByte = new byte[paramDataInputStream.readInt()];
paramDataInputStream.readFully(arrayOfByte);
arrayList.add(new ApplicationPatch(str, arrayOfByte));
}
return arrayList;
}

可以看到ApplicationPatch是从Socket接收到的数据输入流中调用readFully来读取的,关于readFully的使用while循环判断byte数组是否已经读满所有数据,如果没有读满则继续读取补充直到读满为止,从而改善输入流出现空档,造成read方法直接跳出的问题。即通过缓冲来保证数量的完整,也算是常用的一种方法。所以以后若要读取特定长度的数据,使用readFully读取更加安全。

#####1.处理热插拔

下面来看看是如何处理热插拔的

//处理热插拔
private int handleHotSwapPatch(int paramInt, ApplicationPatch paramApplicationPatch) {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Received incremental code patch”);
try {
//创建或获取“data/data/applicationid/files/instant-run/dex”文件路径
String str1 = FileManager.writeTempDexFile(paramApplicationPatch.getBytes());
if (str1 == null) {
Log.e(“InstantRun”, “No file to write the code to”);
return paramInt;
}
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, "Reading live code from " + str1);

String str2 = FileManager.getNativeLibraryFolder().getPath();
//反射构造AppPatchesLoaderImpl实例
Class<?> clazz = Class.forName(“com.android.tools.fd.runtime.AppPatchesLoaderImpl”, true, (ClassLoader)new DexClassLoader(str1, this.context.getCacheDir().getPath(), str2, getClass().getClassLoader()));
try {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, "Got the patcher class " + clazz);
PatchesLoader patchesLoader = (PatchesLoader)clazz.newInstance();
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, "Got the patcher instance " + patchesLoader);
//获取热修复所要替换的类的classname
String[] arrayOfString = (String[])clazz.getDeclaredMethod(“getPatchedClasses”, new Class[0]).invoke(patchesLoader, new Object[0]);
if (Log.isLoggable(“InstantRun”, 2)) {
Log.v(“InstantRun”, "Got the list of classes ");
int j = arrayOfString.length;
for (int i = 0; i < j; i++) {
String str = arrayOfString[i];
Log.v(“InstantRun”, "class " + str);
}
}
//执行AppPatchesLoaderImpl的load方法进行类修复
boolean bool = patchesLoader.load();
if (!bool)
paramInt = 3;
} catch (Exception exception) {}
} catch (Throwable throwable) {
Log.e(“InstantRun”, “Couldn’t apply code changes”, throwable);
paramInt = 3;
}
return paramInt;
}
//AppPatchesLoaderImpl实现抽象类AbstractPatchesLoaderImpl,重写getPatchedClasses方法,重写方法中有提供需要热更新的类
public abstract class AbstractPatchesLoaderImpl implements PatchesLoader {
public abstract String[] getPatchedClasses();

public boolean load() {
try {
//《《《《《《最关键的方法,一个个替换类中要替换的方法》》》》》》
for (String str : getPatchedClasses()) {
ClassLoader classLoader = getClass().getClassLoader();
Object object1 = classLoader.loadClass(str + “ o v e r r i d e " ) . n e w I n s t a n c e ( ) ; F i e l d f i e l d = c l a s s L o a d e r . l o a d C l a s s ( s t r ) . g e t D e c l a r e d F i e l d ( " override").newInstance(); Field field = classLoader.loadClass(str).getDeclaredField(" override").newInstance();Fieldfield=classLoader.loadClass(str).getDeclaredField("change”);
field.setAccessible(true);
Object object2 = field.get(null);
if (object2 != null) {
object2 = object2.getClass().getDeclaredField(“$obsolete”);
if (object2 != null)
object2.set(null, Boolean.valueOf(true));
}
field.set(null, object1);
if (Log.logging != null && Log.logging.isLoggable(Level.FINE))
Log.logging.log(Level.FINE, String.format(“patched %s”, new Object[] { str }));
}
} catch (Exception exception) {
if (Log.logging != null)
Log.logging.log(Level.SEVERE, String.format(“Exception while patching %s”, new Object[] { “foo.bar” }), exception);
return false;
}
return true;
}
}
//AppPatchesLoaderImpl继承AbstractPatchesLoaderImpl,真正的实现类,这里我们只要求change的MainActivity这个类里面的方法
public class AppPatchesLoaderImpl extends AbstractPatchesLoaderImpl {
public static final long BUILD_ID = 1597285889481L;

public AppPatchesLoaderImpl() {
}

public String[] getPatchedClasses() {
return new String[]{“com.example.jackie.instantrundemo.MainActivity”};
}
}

其中DexClassLoader的构造方法定义

//dexPath:被解压的apk路径,不能为空。
//optimizedDirectory:解压后的.dex文件的存储路径,不能为空。这个路径强烈建议使用应用程序的私有路径,不要放到sdcard上,否则代码容易被注入攻击。
//libraryPath:os库的存放路径,可以为空,若有os库,必须填写。
//parent:父类加载器,一般为context.getClassLoader(),使用当前上下文的类加载器。
DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent)

我们的MainActivity会被修改成这样

public class MainActivity extends AppCompatActivity {
public static final long serialVersionUID = 2158910920756968252L;

//重写空构造方法,方便于替换该方法的实现
public MainActivity() {
IncrementalChange var1 = KaTeX parse error: Expected '}', got 'EOF' at end of input: …t[])var1.accessdispatch("init a r g s . ( [ L c o m / e x a m p l e / j a c k i e / i n s t a n t r u n d e m o / M a i n A c t i v i t y ; [ L j a v a / l a n g / O b j e c t ; ) L j a v a / l a n g / O b j e c t ; " , n e w O b j e c t [ ] n u l l , n e w O b j e c t [ 0 ] ) ; O b j e c t [ ] v a r 2 = ( O b j e c t [ ] ) v a r 10001 [ 0 ] ; t h i s ( v a r 10001 , ( I n s t a n t R e l o a d E x c e p t i o n ) n u l l ) ; v a r 2 [ 0 ] = t h i s ; v a r 1. a c c e s s args.([Lcom/example/jackie/instantrundemo/MainActivity;[Ljava/lang/Object;)Ljava/lang/Object;", new Object[]{null, new Object[0]}); Object[] var2 = (Object[])var10001[0]; this(var10001, (InstantReloadException)null); var2[0] = this; var1.access args.([Lcom/example/jackie/instantrundemo/MainActivity;[Ljava/lang/Object;)Ljava/lang/Object;",newObject[]null,newObject[0]);Object[]var2=(Object[])var10001[0];this(var10001,(InstantReloadException)null);var2[0]=this;var1.accessdispatch(“init$body.(Lcom/example/jackie/instantrundemo/MainActivity;[Ljava/lang/Object;)V”, var2);
} else {
super();
}
}

public void onCreate(Bundle savedInstanceState) {
IncrementalChange var2 = KaTeX parse error: Expected '}', got 'EOF' at end of input: …) { var2.accessdispatch(“onCreate.(Landroid/os/Bundle;)V”, new Object[]{this, savedInstanceState});
} else {
super.onCreate(savedInstanceState);
this.setContentView(2130968603);
if(this.test(30) > 20333005) {
Log.d(“jackie”, “4444099994sf=dd=ddecf999=abc”);
} else {
Log.d(“jackie”, “==999999999999=”);
}

byte b = 0;
Toast.makeText(this, “hellodd4fdddd”, 1).show();
Log.d(“jackie”, “=d=666dd=ddddabc” + b);
}
}

public int test(int a) {
IncrementalChange var2 = KaTeX parse error: Expected '}', got 'EOF' at end of input: …ber)var2.accessdispatch(“test.(I)I”, new Object[]{this, new Integer(a)})).intValue();
} else {
byte age = 100;
int b = 300189 + age;

for(int i = 0; i < b + 9; ++i) {
a += b;
}

return 20 + a;
}
}
//增加类的构造方法,方便与修改类的任何构造方法
MainActivity(Object[] var1, InstantReloadException var2) {
String var3 = (String)var1[1];
switch(var3.hashCode()) {
case -2089128195:
super();
return;
case 173992496:
this();
return;
default:
throw new InstantReloadException(String.format(“String switch could not find ‘%s’ with hashcode %s in %s”, new Object[]{var3, Integer.valueOf(var3.hashCode()), “com/example/jackie/instantrundemo/MainActivity”}));
}
}
}

在MainActivity中做个小修改,点击小闪电执行Instant Run,可以看到build下面文件夹4000中会找一个MainActivity$override.class和AppPatchesLoaderImpl.class

要替换的MainActivity$override实现了IncrementalChange,从这里面进行方法的替换,所有的方法都会被替换,因为change值不为空

public class MainActivityKaTeX parse error: Expected '}', got 'EOF' at end of input: …ic MainActivityoverride() {
}

public static Object init$args(MainActivity[] var0, Object[] var1) {
Object[] var2 = new Object[]{new Object[]{var0, new Object[0]}, “android/support/v7/app/AppCompatActivity.()V”};
return var2;
}

public static void init$body(MainActivity KaTeX parse error: Expected '}', got 'EOF' at end of input: …etPrivateField(this, new Integer(100), MainActivity.class, “cmd”);
}

public static void onCreate(MainActivity KaTeX parse error: Expected '}', got 'EOF' at end of input: …Activity.accesssuper($this, “onCreate.(Landroid/os/Bundle;)V”, var2);
t h i s . s e t C o n t e n t V i e w ( 2130968603 ) ; i f ( this.setContentView(2130968603); if( this.setContentView(2130968603);if(this.test(30) > 20333005) {
Log.d(“jackie”, “44440999940sf=dd=ddecf999=abc”);
} else {
Log.d(“jackie”, “==999999999999=”);
}

byte b = 0;
//因为修改了全局变量的值,所以进行处理
AndroidInstantRuntime.setPrivateField( t h i s , n e w I n t e g e r ( ( ( N u m b e r ) A n d r o i d I n s t a n t R u n t i m e . g e t P r i v a t e F i e l d ( this, new Integer(((Number)AndroidInstantRuntime.getPrivateField( this,newInteger(((Number)AndroidInstantRuntime.getPrivateField(this, MainActivity.class, “cmd”)).intValue() + 100), MainActivity.class, “cmd”);
Toast.makeText($this, “hellodd4fdddd”, 1).show();
Log.d(“jackie”, “=d=666dd=ddddabc” + b);
}

public static int test(MainActivity KaTeX parse error: Expected '}', got 'EOF' at end of input: …etPrivateField(this, MainActivity.class, “cmd”)).intValue();
int b = 300189 + ageabc;

for(int i = 0; i < b + 9; ++i) {
a += b;
}

return 20 + a;
}

//替换相对应的方法,最后两个方法我估计是前面MainActivity类里面的copy
public Object accessKaTeX parse error: Expected '}', got 'EOF' at end of input: …88: return initargs((MainActivity[])var2[0], (Object[])var2[1]);
case 1043612718:
init$body((MainActivity)var2[0], (Object[])var2[1]);
return null;
default:
throw new InstantReloadException(String.format(“String switch could not find ‘%s’ with hashcode %s in %s”, new Object[]{var1, Integer.valueOf(var1.hashCode()), “com/example/jackie/instantrundemo/MainActivity”}));
}
}
}

#####2.处理资源补丁(温插拔)

下面来看看是如何处理资源补丁的,但是设计到资源的处理需要重启当前界面,我们先来看看重启App这种状况下的逻辑

//Server类的handle()
case 5:
if (authenticate(param1DataInputStream)) {
Activity activity1 = Restarter.getForegroundActivity(Server.this.context);
if (activity1 != null) {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Restarting activity per user request”);
Restarter.restartActivityOnUiThread(activity1);
}
continue;
}
return;
case 1:
if (authenticate(param1DataInputStream)) {
List list = ApplicationPatch.read(param1DataInputStream);
if (list != null) {
bool = Server.hasResources(list);
i = param1DataInputStream.readInt();
//处理acitivity或者把资源写到文件中
i = Server.this.handlePatches(list, bool, i);
boolean bool1 = param1DataInputStream.readBoolean();
param1DataOutputStream.writeBoolean(true);
//重启Activity
Server.this.restart(i, bool, bool1);
}
continue;
}

我们先来看看handlePatches里面的handleResourcePatch是如何处理资源的

//Server.class
//处理资源补丁
private static int handleResourcePatch(int paramInt, ApplicationPatch paramApplicationPatch, String paramString) {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Received resource changes (” + paramString + “)”);
FileManager.writeAaptResources(paramString, paramApplicationPatch.getBytes());
return Math.max(paramInt, 2);
}
//FileManager.class
//writeAaptResources
public static void writeAaptResources(String paramString, byte[] paramArrayOfbyte) {
File file1 = getResourceFile(getWriteFolder(false)); //获取资源文件
File file2 = file1.getParentFile();
if (!file2.isDirectory() && !file2.mkdirs()) {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, "Cannot create local resource file directory " + file2);
return;
}
//把内容写到resources.ap_资源中
if (paramString.equals(“resources.ap_”)) {
writeRawBytes(file1, paramArrayOfbyte);
return;
}
//把raw资源写入
writeRawBytes(file1, paramArrayOfbyte);
}
//getWriteFolder
public static File getWriteFolder(boolean paramBoolean) {
String str;
if (leftIsActive()) {
str = “right”;
} else {
str = “left”;
}
File file = new File(getDataFolder(), str);
if (paramBoolean && file.exists()) {
delete(file);
if (!file.mkdirs())
Log.e(“InstantRun”, "Failed to create folder " + file);
}
return file;
}
//getResourceFile
private static File getResourceFile(File paramFile) { return new File(paramFile, “resources.ap_”); }

//writeRawBytes
public static boolean writeRawBytes(File paramFile, byte[] paramArrayOfbyte) {
try {
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(paramFile));
try {
bufferedOutputStream.write(paramArrayOfbyte);
bufferedOutputStream.flush();
return true;
} finally {
bufferedOutputStream.close();
}
} catch (IOException iOException) {
Log.wtf(“InstantRun”, "Failed to write file, clean project and rebuild " + paramFile, iOException);
throw new RuntimeException(String.format("InstantRun could not write file %1$s, clean project and rebuild ", new Object[] { paramFile }));
}
}
public static String writeTempDexFile(byte[] paramArrayOfbyte) {
File file = getTempDexFile();
if (file != null) {
writeRawBytes(file, paramArrayOfbyte);
return file.getPath();
}
Log.e(“InstantRun”, “No file to write temp dex content to”);
return null;
}

下面来看看Server.this.restart(i, bool, bool1)是如何处理的,不必拘泥于细节是如何启动的,在里面找到一行关键代码

MonkeyPatcher.monkeyPatchExistingResources(this.context, str, list);

具体实现如下

public static void monkeyPatchExistingResources(Context context, String externalResourceFile, Collection activities) {
Collection<WeakReference> references;
if (externalResourceFile != null) {
//通过反射获取AssetManager
AssetManager newAssetManager = AssetManager.class.getConstructor(new Class[0]).newInstance(new Object[0]);
Method mAddAssetPath = AssetManager.class.getDeclaredMethod(“addAssetPath”, new Class[]{String.class});
mAddAssetPath.setAccessible(true);
//将当前的资源文件路径添加到AssetManager中
if (((Integer) mAddAssetPath.invoke(newAssetManager, new Object[]{externalResourceFile})).intValue() == 0) {
throw new IllegalStateException(“Could not create new AssetManager”);
}
Method mEnsureStringBlocks = AssetManager.class.getDeclaredMethod(“ensureStringBlocks”, new Class[0]);
mEnsureStringBlocks.setAccessible(true);
//进行资源初始化StringBlock对象
mEnsureStringBlocks.invoke(newAssetManager, new Object[0]);
if (activities != null) {
for (Activity activity : activities) {
Resources resources = activity.getResources();
try {
Field mAssets = Resources.class.getDeclaredField(“mAssets”);
mAssets.setAccessible(true);
mAssets.set(resources, newAssetManager);
} catch (Throwable e) {
throw new IllegalStateException(e);
}
Resources.Theme theme = activity.getTheme();
try {
Field ma = Resources.Theme.class.getDeclaredField(“mAssets”);
ma.setAccessible(true);
ma.set(theme, newAssetManager);
} catch (NoSuchFieldException e2) {
Field themeField = Resources.Theme.class.getDeclaredField(“mThemeImpl”);
themeField.setAccessible(true);
Object impl = themeField.get(theme);
Field ma2 = impl.getClass().getDeclaredField(“mAssets”);
ma2.setAccessible(true);
ma2.set(impl, newAssetManager);
} catch (Throwable e3) {
Log.e(Logging.LOG_TAG, "Failed to update existing theme for activity " + activity, e3);
}
Field mt = ContextThemeWrapper.class.getDeclaredField(“mTheme”);
mt.setAccessible(true);
mt.set(activity, (Object) null);
Method mtm = ContextThemeWrapper.class.getDeclaredMethod(“initializeTheme”, new Class[0]);
mtm.setAccessible(true);
mtm.invoke(activity, new Object[0]);
if (Build.VERSION.SDK_INT < 24) {
Method mCreateTheme = AssetManager.class.getDeclaredMethod(“createTheme”, new Class[0]);
mCreateTheme.setAccessible(true);
Object internalTheme = mCreateTheme.invoke(newAssetManager, new Object[0]);
Field mTheme = Resources.Theme.class.getDeclaredField(“mTheme”);
mTheme.setAccessible(true);
mTheme.set(theme, internalTheme);
}
pruneResourceCaches(resources);
}
}
//获取当前JVM中的ResourcesManager的final ArrayMap<ResourcesKey, WeakReference > mActiveResources
if (Build.VERSION.SDK_INT >= 19) {
Class<?> resourcesManagerClass = Class.forName("android.app.ResourcesManager"); Method mGetInstance = resourcesManagerClass.getDeclaredMethod("getInstance", new Class[0]); mGetInstance.setAccessible(true); Object resourcesManager = mGetInstance.invoke((Object) null, new Object[0]); try { Field fMActiveResources = resourcesManagerClass.getDeclaredField("mActiveResources"); fMActiveResources.setAccessible(true); references = ((ArrayMap) fMActiveResources.get(resourcesManager)).values(); } catch (NoSuchFieldException e4) { Field mResourceReferences = resourcesManagerClass.getDeclaredField("mResourceReferences"); mResourceReferences.setAccessible(true); references = (Collection) mResourceReferences.get(resourcesManager); } } else { Class<?> activityThread = Class.forName(“android.app.ActivityThread”);
Field fMActiveResources2 = activityThread.getDeclaredField(“mActiveResources”);
fMActiveResources2.setAccessible(true);
references = ((HashMap) fMActiveResources2.get(getActivityThread(context, activityThread))).values();
}
//循环便利当前Resources,将其成员变量mAssets指向自定义的newAssetManager
for (WeakReference wr : references) {
Resources resources2 = (Resources) wr.get();
if (resources2 != null) {
try {
Field mAssets2 = Resources.class.getDeclaredField(“mAssets”);
mAssets2.setAccessible(true);
mAssets2.set(resources2, newAssetManager);
} catch (Throwable th) {
Field mResourcesImpl = Resources.class.getDeclaredField(“mResourcesImpl”);
mResourcesImpl.setAccessible(true);
Object resourceImpl = mResourcesImpl.get(resources2);
Field implAssets = resourceImpl.getClass().getDeclaredField(“mAssets”);
implAssets.setAccessible(true);
implAssets.set(resourceImpl, newAssetManager);
}
//更新资源
resources2.updateConfiguration(resources2.getConfiguration(), resources2.getDisplayMetrics());
}
}
}
}

在研究过程中,本来想基于退出gradle4.1然后在gradle2.2.3重新再搞一下,后面想想不把4.1研究透再去搞2.2.3总是心有不甘,网上也基本找不到gradle 4.1的研究的,其实在2.3.0之后就没有instant-run.zip包了,但是好像所有人都没有提到这点,难道他们都是基于2.2.3或者更早的?

####找不到的项目代码去哪里了

下面来看看我们的MainActivity,MyApplication等文件在去哪里了,找到之前的Server(SocketServerThread),明白我们是从AS端接受这些文件的,接受这些dex文件后,存储在app的cache文件中(getWriteFolder),并进行处理,

private class SocketServerThread extends Thread {
private SocketServerThread() {}

public void run() {
while (true) {
try {
LocalServerSocket localServerSocket = Server.this.serverSocket;
if (localServerSocket == null)
return;
//接受AS发过来的文件
LocalSocket localSocket = localServerSocket.accept();
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Received connection from IDE: spawning connection thread”);
(new Server.SocketServerReplyThread(localSocket)).run();
if (wrongTokenCount > 50) {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Stopping server: too many wrong token connections”);
Server.this.serverSocket.close();
return;
}
} catch (Throwable throwable) {
if (Log.isLoggable(“InstantRun”, 2))
Log.v(“InstantRun”, “Fatal error accepting connection on local socket”, throwable);
}
}
}
}

下面可以使用我们一开始下载的源码,在instant-run下面的intant-run-client,InstantClient类中,将文件发送到设备中

private void transferBuildIdToDevice(@NonNull IDevice device, @NonNull String buildId) {
try {
String remoteIdFile = getDeviceIdFolder(mPackageName);
//noinspection SSBasedInspection This should work
File local = File.createTempFile(“build-id”, “txt”);
local.deleteOnExit();
Files.write(buildId, local, Charsets.UTF_8);
device.pushFile(local.getPath(), remoteIdFile);
} catch (IOException ioe) {
mLogger.warning(“Couldn’t write build id file: %s”, ioe);
} catch (AdbCommandRejectedException | TimeoutException | SyncException e) {
mLogger.warning(“%s”, Throwables.getStackTraceAsString(e));
}
}

我们回看之前的handleHotSwapPatch方法中读取文件的方式,可以看到在cache文件中

private int handleHotSwapPatch(int updateMode, @NonNull ApplicationPatch patch) {
···
try {
String dexFile = FileManager.writeTempDexFile(patch.getBytes());
···
String nativeLibraryPath = FileManager.getNativeLibraryFolder().getPath();
DexClassLoader dexClassLoader = new DexClassLoader(dexFile,
context.getCacheDir().getPath(), nativeLibraryPath,
getClass().getClassLoader());

####使用InstantRun更新的文件去哪里了

下面来看看我们的MainActivity$override,MyApplication$override,AppPatchesLoaderImpl等文件在去哪里了,我们进入应用内部的/data/data/com.example.jackie.instantrundemo/files/instant-run/dex-temp中会发现一个reload0x0000.dex文件,里面就有提供更新的内容,instant-run里面中的right是用于存储resource.ap_。

其他一些Gradle版本
Gradle2.2.3

在当前版本中,我们可以看到instant-run.zip包,里面包含的项目的代码和要替换的代码。解压后可以看到AndroidManifest.xml文件,从 AndroidManifest.xml 中我们看到了MyApplicationBootstrapApplication 替代,那么我们可以想象当 ApplicationInstant-run 自己的时,那么它至少可以像加载插件一样在应用启动的时候(程序入口)加载替换自己的dex和资源文件,从而达到修改运行程序的目的。

@Override
protected void attachBaseContext(Context context) {
// As of Marshmallow, we use APK splits and don’t need to rely on
// reflection to inject classes and resources for coldswap
//noinspection PointlessBooleanExpression
//是否使用了apk分裂安装
if (!AppInfo.usingApkSplits) {
String apkFile = context.getApplicationInfo().sourceDir;
long apkModified = apkFile != null ? new File(apkFile).lastModified() : 0L;
//判断资源resource.ap_是否进行了修改,将其路径保存在externalResourcePath
createResources(apkModified);
//创建classloade
//delegateClassLoader->PathClassLoader->IncrementalClassLoader->BootClassLoader
setupClassLoaders(context, context.getCacheDir().getPath(), apkModified);
}
//创建正真的Application
createRealApplication();

// This is called from ActivityThread#handleBindApplication() -> LoadedApk#makeApplication().
// Application#mApplication is changed right after this call, so we cannot do the monkey
// patching here. So just forward this method to the real Application instance.
super.attachBaseContext(context);

if (realApplication != null) {
try {
Method attachBaseContext =
ContextWrapper.class.getDeclaredMethod(“attachBaseContext”, Context.class);
attachBaseContext.setAccessible(true);
//执行自己的Application的attachBaseContext方法
attachBaseContext.invoke(realApplication, context);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}

该方法的主要目的在于,创建自定义的ClassLoader和真正的Application实例。而 BootstrapApplication 只起到一个壳子的作用。

替换Application的时候我们可以看看MonkeyPatcher中是如何替换的

public class MonkeyPatcher {
public static void monkeyPatchApplication(Context context, Application bootstrap, Application realApplication, String externalResourceFile) {
Class<?> activityThread; Class<?> loadedApkClass;
try {
//获取ActivityThread实例
activityThread = Class.forName(“android.app.ActivityThread”);
Object currentActivityThread = getActivityThread(context, activityThread);
Field mInitialApplication = activityThread.getDeclaredField(“mInitialApplication”);
mInitialApplication.setAccessible(true);
//替换ActivityThread的mInitialApplication成员变量
Application initialApplication = (Application) mInitialApplication.get(currentActivityThread);
if (realApplication != null && initialApplication == bootstrap) {
mInitialApplication.set(currentActivityThread, realApplication);
}
//替换ActivityThread的mAllApplications队列中的BootstrapApplication为realApplication
if (realApplication != null) {
Field mAllApplications = activityThread.getDeclaredField(“mAllApplications”);
mAllApplications.setAccessible(true);
List allApplications = (List) mAllApplications.get(currentActivityThread);
for (int i = 0; i < allApplications.size(); i++) {
if (allApplications.get(i) == bootstrap) {
allApplications.set(i, realApplication);
}
}
}
loadedApkClass = Class.forName(“android.app.LoadedApk”);
} catch (ClassNotFoundException e) {

最后

做任何事情都要用心,要非常关注细节。看起来不起眼的、繁琐的工作做透了会有意想不到的价值。
当然要想成为一个技术大牛也需要一定的思想格局,思想决定未来你要往哪个方向去走, 建议多看一些人生规划方面的书籍,多学习名人的思想格局,未来你的路会走的更远。

更多的技术点思维导图我已经做了一个整理,涵盖了当下互联网最流行99%的技术点,在这里我将这份导图分享出来,以及为金九银十准备的一整套面试体系,上到集合,下到分布式微服务

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ame(“android.app.LoadedApk”);
} catch (ClassNotFoundException e) {

最后

做任何事情都要用心,要非常关注细节。看起来不起眼的、繁琐的工作做透了会有意想不到的价值。
当然要想成为一个技术大牛也需要一定的思想格局,思想决定未来你要往哪个方向去走, 建议多看一些人生规划方面的书籍,多学习名人的思想格局,未来你的路会走的更远。

更多的技术点思维导图我已经做了一个整理,涵盖了当下互联网最流行99%的技术点,在这里我将这份导图分享出来,以及为金九银十准备的一整套面试体系,上到集合,下到分布式微服务

[外链图片转存中…(img-R5YdCjW2-1713679835755)]

[外链图片转存中…(img-SFIgUcaI-1713679835755)]

[外链图片转存中…(img-eSQkDnip-1713679835756)]

[外链图片转存中…(img-uxrPvtHp-1713679835757)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-eTbM1z94-1713679835757)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 21
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值