Skip to content

Commit

Permalink
fix(core): BroadcastReceiver不再以Wrapper方式支持
Browse files Browse the repository at this point in the history
Shadow对于BroadcastReceiver所需的处理只涉及onReceive方法传回的参数。
由于插件的BroadcastReceiver是通过正常的Context对象直接注册到系统中的,
所以对于系统来说这些BroadcastReceiver和宿主的其他BroadcastReceiver
没有什么区别。系统回调onReceive方法时传回的Context和Intent也都是宿主的。
其中需要把Context换回ShadowContext,Intent中的ClassLoader换回插件的。

Tencent#865#issuecomment-1134246493指出:系统控件 ViewFlipper
在 onAttachedToWindow() 方法中使用 Contenxt.registerReceiverAsUser
 方法进行广播注册;在 onDetachedFromWindow() 方法中使用
 Context.unregisterReceiver 方法进行取消广播注册。

本地提交改为通过Transform修改插件中的onReceive方法,在BroadcastReceiver
内部进行参数转换。从而避免引入新的类型,同时也不依赖如何注册和反注册Receiver。

fix Tencent#865
close Tencent#1105
  • Loading branch information
shifujun committed Nov 19, 2022
1 parent 8d9c7a7 commit 185e558
Show file tree
Hide file tree
Showing 12 changed files with 402 additions and 93 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,6 @@ public class ShadowApplication extends ShadowContext {

public boolean isCallOnCreate;

/**
* BroadcastReceiver到BroadcastReceiverWrapper对象到映射关系
* <p>
* 采用WeakHashMap<BroadcastReceiver, WeakReference<BroadcastReceiverWrapper>>
* 使key和value都采用弱引用持有,以保持原本BroadcastReceiver的GC回收时机。
* <p>
* BroadcastReceiver由原有业务代码强持有(也可能不持有),BroadcastReceiver原本在registerReceiver
* 之后交由系统持有,现在由BroadcastReceiverWrapper代替它被系统强持有。
* 所以BroadcastReceiverWrapper强引用持有BroadcastReceiver,保持了系统强引用BroadcastReceiver的关系。
* <p>
* 如果业务原本没有持有BroadcastReceiver,也就不会再有unregisterReceiver调用来,
* 也就不需要Map中有wrapper对应关系,所以用弱引用持有此关系没有影响。
*/
final private Map<BroadcastReceiver, WeakReference<BroadcastReceiverWrapper>>
mReceiverWrapperMap = new WeakHashMap<>();

@Override
public Context getApplicationContext() {
return this;
Expand Down Expand Up @@ -196,19 +180,4 @@ public static String getProcessName() {
return Application.getProcessName();
}

public BroadcastReceiverWrapper receiverToWrapper(BroadcastReceiver receiver) {
if (receiver == null) {
return null;
}
synchronized (mReceiverWrapperMap) {
WeakReference<BroadcastReceiverWrapper> weakReference
= mReceiverWrapperMap.get(receiver);
BroadcastReceiverWrapper wrapper = weakReference == null ? null : weakReference.get();
if (wrapper == null) {
wrapper = new BroadcastReceiverWrapper(receiver, this);
mReceiverWrapperMap.put(receiver, new WeakReference<>(wrapper));
}
return wrapper;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,15 @@

package com.tencent.shadow.core.runtime;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Pair;
import android.view.LayoutInflater;
Expand Down Expand Up @@ -234,44 +231,9 @@ public String getPackageName() {
return mApplicationInfo.packageName;
}


@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return super.registerReceiver(receiverToWrapper(receiver), filter);
}

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
return super.registerReceiver(receiverToWrapper(receiver), filter, flags);
}

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {
return super.registerReceiver(receiverToWrapper(receiver), filter, broadcastPermission, scheduler);
}

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
return super.registerReceiver(receiverToWrapper(receiver), filter, broadcastPermission, scheduler, flags);
}

@Override
public void unregisterReceiver(BroadcastReceiver receiver) {
BroadcastReceiverWrapper wrapper = receiverToWrapper(receiver);
if (wrapper != null) {
super.unregisterReceiver(wrapper);
} else {
super.unregisterReceiver(receiver);
}
}

@Override
public String getPackageCodePath() {
PluginPartInfo pluginInfo = PluginPartInfoManager.getPluginInfo(getClassLoader());
return pluginInfo.packageManager.getArchiveFilePath();
}

private BroadcastReceiverWrapper receiverToWrapper(BroadcastReceiver receiver) {
return mShadowApplication.receiverToWrapper(receiver);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* Tencent is pleased to support the open source community by making Tencent Shadow available.
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the BSD 3-Clause License (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of
* the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package com.tencent.shadow.core.transform.specific

import com.tencent.shadow.core.transform_kit.SpecificTransform
import com.tencent.shadow.core.transform_kit.TransformStep
import javassist.*
import javassist.compiler.Javac.CtFieldWithInit

/**
* 系统回调BroadcastReceiver的onReceive(Context context, Intent intent)方法时:
* 1. 传回的context是宿主的,需要修改为插件的。
* 2. intent的ExtrasClassLoader是宿主的,需要改为插件的。
*
* 如果是系统类的BroadcastReceiver,它也不会和插件的context或classloader有什么联系,
* 所以我们只需要修改插件代码中的BroadcastReceiver。
*
* 把原本插件的onReceive方法改个名字,再统一添加一个onReceive方法。
* 在新增的onReceive方法中修改收到的系统回调参数,再转调被改名了的原本插件的onReceive方法。
*/
class ReceiverSupportTransform : SpecificTransform() {

companion object {
const val AndroidBroadcastReceiverClassname = "android.content.BroadcastReceiver"
const val AndroidContextClassname = "android.content.Context"
const val AndroidIntentClassname = "android.content.Intent"
}

private fun CtClass.isReceiver(): Boolean = isClassOf(AndroidBroadcastReceiverClassname)

override fun setup(allInputClass: Set<CtClass>) {
mClassPool.importPackage("android.content")
mClassPool.importPackage("com.tencent.shadow.core.runtime")

val androidContext = mClassPool[AndroidContextClassname]
val androidIntent = mClassPool[AndroidIntentClassname]

/**
* 收集覆盖了onReceive方法的Receiver作为修改目标
*/
val targetReceivers = mutableSetOf<CtClass>()
newStep(object : TransformStep {
override fun filter(allInputClass: Set<CtClass>) = allInputClass
.filter { it.isReceiver() }
.toSet()

override fun transform(ctClass: CtClass) {
val onReceiveMethod: CtMethod? =
try {
ctClass.getDeclaredMethod(
"onReceive",
arrayOf(androidContext, androidIntent)
)
} catch (e: NotFoundException) {
null
}
if (onReceiveMethod != null) {
targetReceivers.add(ctClass)
}
}
})

/**
* 对原本的onReceive方法改名,并添加新的onReceive方法。
*/
newStep(object : TransformStep {
override fun filter(allInputClass: Set<CtClass>) = targetReceivers

override fun transform(ctClass: CtClass) {
ctClass.defrost()

// 改名
val originalOnReceiveMethod: CtMethod =
ctClass.getDeclaredMethod(
"onReceive",
arrayOf(androidContext, androidIntent)
)
originalOnReceiveMethod.name = "onReceiveShadowContext"
originalOnReceiveMethod.modifiers =
Modifier.setPrivate(originalOnReceiveMethod.modifiers)

// 声明两个域变量保存onReceive收到的原始参数,供调用super方法时使用。
// the compiler embedded in Javassist does not support generics
arrayOf(
CtFieldWithInit.make(
"ThreadLocal originalOnReceiveContext = new ThreadLocal();",
ctClass
),
CtFieldWithInit.make(
"ThreadLocal originalOnReceiveIntent = new ThreadLocal();",
ctClass
),
).forEach {
ctClass.addField(it)
}

// 添加新onReceive方法
val newOnReceiveMethod = CtMethod.make(
"""
public void onReceive(Context context, Intent intent) {
try{
//保存收到的参数
originalOnReceiveContext.set(context);
originalOnReceiveIntent.set(intent);
//通过当前插件类ClassLoader找到相关的插件Application
ClassLoader cl = this.getClass().getClassLoader();
PluginPartInfo info = PluginPartInfoManager.getPluginInfo(cl);
Context shadowContext = info.application;
Intent intentCopy = new Intent(intent);//不修改原本的intent
intentCopy.setExtrasClassLoader(cl);
//调用原本的onReceive方法
onReceiveShadowContext(shadowContext, intentCopy);
}finally {
originalOnReceiveContext.remove();
originalOnReceiveIntent.remove();
}
}
""".trimIndent(), ctClass
)
ctClass.addMethod(newOnReceiveMethod)

// 定义superOnReceiveMethod方法
val newSuperMethod = CtMethod.make(
"""
private void superOnReceive(Context context, Intent intent) {
context = (Context)originalOnReceiveContext.get();
intent = (Intent)originalOnReceiveIntent.get();
super.onReceive(context, intent);
}
""".trimIndent(), ctClass
)

//转调super.onReceive方法
val superMethod: CtMethod =
ctClass.superclass.getMethod(
"onReceive",
"(Landroid/content/Context;Landroid/content/Intent;)V"
)

if (Modifier.isAbstract(superMethod.modifiers).not()) {
val codeConverter = CodeConverter()
codeConverter.redirectMethodCall(superMethod, newSuperMethod)
try {
ctClass.instrument(codeConverter)
} catch (e: Exception) {
System.err.println("处理" + ctClass.name + "时出错:" + e)
throw e
}
ctClass.addMethod(newSuperMethod)
}
}

})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package android.content;

public abstract class BroadcastReceiver {
public abstract void onReceive(Context context, Intent intent);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
package android.content;

public class Intent {
public Intent() {
}

public Intent(Intent intent) {
}

public void setExtrasClassLoader(ClassLoader loader) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.tencent.shadow.core.runtime;

public class PluginPartInfo {
public ShadowApplication application = new ShadowApplication();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.tencent.shadow.core.runtime;

public class PluginPartInfoManager {
public static PluginPartInfo getPluginInfo(ClassLoader classLoader) {
return new PluginPartInfo();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.tencent.shadow.core.runtime;

public class ShadowApplication {
public class ShadowApplication extends ShadowContext {

public void onCreate() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.tencent.shadow.core.runtime;

import android.content.Context;

public class ShadowContext extends Context {
}
Loading

0 comments on commit 185e558

Please sign in to comment.