diff --git a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/injector/loaderactivity/LoaderActivityInjector.groovy b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/injector/loaderactivity/LoaderActivityInjector.groovy index 34a04e9..3f3d14b 100644 --- a/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/injector/loaderactivity/LoaderActivityInjector.groovy +++ b/replugin-plugin-gradle/src/main/groovy/com/qihoo360/replugin/gradle/plugin/injector/loaderactivity/LoaderActivityInjector.groovy @@ -45,8 +45,8 @@ public class LoaderActivityInjector extends BaseInjector { 'android.app.ActivityGroup' : 'com.qihoo360.replugin.loader.a.PluginActivityGroup', 'android.support.v4.app.FragmentActivity' : 'com.qihoo360.replugin.loader.a.PluginFragmentActivity', 'android.support.v7.app.AppCompatActivity': 'com.qihoo360.replugin.loader.a.PluginAppCompatActivity', - 'androidx.fragment.app.FragmentActivity' : 'com.qihoo360.replugin.loader.a.PluginFragmentActivity', - 'androidx.appcompat.app.AppCompatActivity': 'com.qihoo360.replugin.loader.a.PluginAppCompatActivity', + 'androidx.fragment.app.FragmentActivity' : 'com.qihoo360.replugin.loader.a.PluginFragmentActivityAndroidX', + 'androidx.appcompat.app.AppCompatActivity': 'com.qihoo360.replugin.loader.a.PluginAppCompatActivityAndroidX', 'android.preference.PreferenceActivity' : 'com.qihoo360.replugin.loader.a.PluginPreferenceActivity', 'android.app.ExpandableListActivity' : 'com.qihoo360.replugin.loader.a.PluginExpandableListActivity' ] diff --git a/replugin-plugin-library/build.gradle b/replugin-plugin-library/build.gradle index 5009a00..9d46173 100644 --- a/replugin-plugin-library/build.gradle +++ b/replugin-plugin-library/build.gradle @@ -44,6 +44,7 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) provided 'com.android.support:appcompat-v7:23.4.0' + provided 'androidx.appcompat:appcompat:1.1.0' } project.ext.RP_ARTIFACT_ID = 'replugin-plugin-lib' diff --git a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/compat/CompatConfig.java b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/compat/CompatConfig.java new file mode 100644 index 0000000..ecf38de --- /dev/null +++ b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/compat/CompatConfig.java @@ -0,0 +1,45 @@ +package com.qihoo360.replugin.compat; + +/** + * 兼容层配置 + * + * @author LQR + * @since 2021/12/8 + */ +public final class CompatConfig { + + private static volatile CompatConfig sInstance; + + public static final boolean DEPENDENCY_ANDROIDX; + public static final boolean DEPENDENCY_SUPPORT; + + static { + DEPENDENCY_ANDROIDX = findClassByClassName("androidx.fragment.app.FragmentActivity"); + DEPENDENCY_SUPPORT = findClassByClassName("android.support.v4.app.FragmentActivity"); + } + + private CompatConfig() { + } + + public static CompatConfig getInstance() { + if (sInstance == null) { + synchronized (CompatConfig.class) { + if (sInstance == null) { + sInstance = new CompatConfig(); + } + } + } + return sInstance; + } + + private static boolean findClassByClassName(String className) { + boolean hasDependency; + try { + Class.forName(className); + hasDependency = true; + } catch (ClassNotFoundException e) { + hasDependency = false; + } + return hasDependency; + } +} diff --git a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginAppCompatActivity.java b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginAppCompatActivity.java index c880c7f..2542caa 100644 --- a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginAppCompatActivity.java +++ b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginAppCompatActivity.java @@ -96,7 +96,7 @@ protected void onRestoreInstanceState(Bundle savedInstanceState) { // 1、可能无法恢复系统级View的保存的状态; // 2、如果自己代码处理不当,可能会出现异常。故自己代码一定要用SecExtraUtils来获取Bundle数据 if (LogRelease.LOGR) { - LogRelease.e("PluginFragmentActivity", "o r i s: p=" + getPackageCodePath() + "; " + e.getMessage(), e); + LogRelease.e("PluginAppCompatActivity", "o r i s: p=" + getPackageCodePath() + "; " + e.getMessage(), e); } } } diff --git a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginAppCompatActivityAndroidX.java b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginAppCompatActivityAndroidX.java new file mode 100644 index 0000000..76ac763 --- /dev/null +++ b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginAppCompatActivityAndroidX.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2005-2017 Qihoo 360 Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.qihoo360.replugin.loader.a; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Build; +import android.os.Bundle; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; + +import com.qihoo360.replugin.RePluginInternal; +import com.qihoo360.replugin.helper.LogRelease; +import com.qihoo360.replugin.loader.PluginResource; + +import java.lang.reflect.Field; + +/** + * @author RePlugin Team + */ +public abstract class PluginAppCompatActivityAndroidX extends AppCompatActivity { + + private PluginResource pluginResource; + + @Override + protected void attachBaseContext(Context newBase) { + newBase = RePluginInternal.createActivityContext(this, newBase); + pluginResource = new PluginResource(newBase); + super.attachBaseContext(newBase); + } + + @Override + public Resources getResources() { + if (pluginResource != null){ + return pluginResource; + } + return super.getResources(); + } + + @Override + public Context getBaseContext() { + + return super.getBaseContext(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + // + RePluginInternal.handleActivityCreateBefore(this, savedInstanceState); + + super.onCreate(savedInstanceState); + + // + RePluginInternal.handleActivityCreate(this, savedInstanceState); + } + + @Override + protected void onDestroy() { + // + RePluginInternal.handleActivityDestroy(this); + + super.onDestroy(); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + // + RePluginInternal.handleRestoreInstanceState(this, savedInstanceState); + + try { + super.onRestoreInstanceState(savedInstanceState); + } catch (Throwable e) { + // Added by Jiongxuan Zhang + // Crash Hash: B1F67129BC6A67C882AF2BBE62202BF0 + // java.lang.IllegalArgumentException: Wrong state class异常 + // 原因:恢复现场时,Activity坑位找错了。通常是用于占坑的Activity的层级过深导致 + // 举例:假如我们只有一个坑位可用,A和B分别是清理和通讯录的两个Activity + // 如果进程重启,系统原本恢复B,却走到了A,从而出现此问题 + // 解决:将其Catch住,这样系统在找ViewState时不会出错。 + // 后遗症: + // 1、可能无法恢复系统级View的保存的状态; + // 2、如果自己代码处理不当,可能会出现异常。故自己代码一定要用SecExtraUtils来获取Bundle数据 + if (LogRelease.LOGR) { + LogRelease.e("PluginAppCompatActivityAndroidX", "o r i s: p=" + getPackageCodePath() + "; " + e.getMessage(), e); + } + } + } + + @Override + public void startActivity(Intent intent) { + // + if (RePluginInternal.startActivity(this, intent)) { + // 这个地方不需要回调startActivityAfter,因为RePluginInternal最终还是会回调回来,最终还是要走super.startActivity() + return; + } + + super.startActivity(intent); + } + + @Override + public void startActivityForResult(Intent intent, int requestCode) { + startActivityForResult(intent, requestCode, null); + } + + @Override + public void startActivityForResult(Intent intent, int requestCode, Bundle options) { + // + if (RePluginInternal.startActivityForResult(this, intent, requestCode, options)) { + return; + } + + if (Build.VERSION.SDK_INT >= 16) { + super.startActivityForResult(intent, requestCode, options); + } else { + super.startActivityForResult(intent, requestCode); + } + } + + /** + * 这里的做法是支持Fragment中调用startActivityForResult特性 + *
+ * 由于卫士的插件需要hook住XXX-Activity的startActivity和startActivityForResult接口 + * 但早期版本的support-v4在startActivityFromFragment中直接调用了super.startActivityForResult, 因此这里还需要hook住这个点 + * 但新版的support-v4中Fragment最终的调用链还是会走到本XXX-Activity的startActivityForResult接口,因此不需要适配startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options)接口 + * + * @param fragment + * @param intent + * @param requestCode + */ + @Override + public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) { + if (requestCode == -1) { + startActivityForResult(intent, -1); + } else if ((requestCode & -65536) != 0) { + throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); + } else { + int newRequestCode = -1; + try { + Field f = Fragment.class.getDeclaredField("mIndex"); + boolean acc = f.isAccessible(); + if (!acc) { + f.setAccessible(true); + } + Object o = f.get(fragment); + if (!acc) { + f.setAccessible(acc); + } + int index = (Integer) o; + newRequestCode = ((index + 1) << 16) + (requestCode & '\uffff'); + } catch (Throwable e) { + // Do Noting + } + startActivityForResult(intent, newRequestCode); + } + } + + @Override + public String getPackageCodePath() { + return super.getPackageCodePath(); + } +} diff --git a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginFragmentActivityAndroidX.java b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginFragmentActivityAndroidX.java new file mode 100644 index 0000000..8cf9a00 --- /dev/null +++ b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/a/PluginFragmentActivityAndroidX.java @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2005-2017 Qihoo 360 Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.qihoo360.replugin.loader.a; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Build; +import android.os.Bundle; + +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; + +import com.qihoo360.replugin.RePluginInternal; +import com.qihoo360.replugin.helper.LogRelease; +import com.qihoo360.replugin.loader.PluginResource; + +import java.lang.reflect.Field; + +/** + * @author RePlugin Team + */ +public abstract class PluginFragmentActivityAndroidX extends FragmentActivity { + + private PluginResource pluginResource; + + @Override + protected void attachBaseContext(Context newBase) { + newBase = RePluginInternal.createActivityContext(this, newBase); + pluginResource = new PluginResource(newBase); + super.attachBaseContext(newBase); + } + + @Override + public Resources getResources() { + if (pluginResource != null){ + return pluginResource; + } + return super.getResources(); + } + + @Override + public Context getBaseContext() { + + return super.getBaseContext(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + // + RePluginInternal.handleActivityCreateBefore(this, savedInstanceState); + + super.onCreate(savedInstanceState); + + // + RePluginInternal.handleActivityCreate(this, savedInstanceState); + } + + @Override + protected void onDestroy() { + // + RePluginInternal.handleActivityDestroy(this); + + super.onDestroy(); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + // + RePluginInternal.handleRestoreInstanceState(this, savedInstanceState); + + try { + super.onRestoreInstanceState(savedInstanceState); + } catch (Throwable e) { + // Added by Jiongxuan Zhang + // Crash Hash: B1F67129BC6A67C882AF2BBE62202BF0 + // java.lang.IllegalArgumentException: Wrong state class异常 + // 原因:恢复现场时,Activity坑位找错了。通常是用于占坑的Activity的层级过深导致 + // 举例:假如我们只有一个坑位可用,A和B分别是清理和通讯录的两个Activity + // 如果进程重启,系统原本恢复B,却走到了A,从而出现此问题 + // 解决:将其Catch住,这样系统在找ViewState时不会出错。 + // 后遗症: + // 1、可能无法恢复系统级View的保存的状态; + // 2、如果自己代码处理不当,可能会出现异常。故自己代码一定要用SecExtraUtils来获取Bundle数据 + if (LogRelease.LOGR) { + LogRelease.e("PluginFragmentActivityAndroidX", "o r i s: p=" + getPackageCodePath() + "; " + e.getMessage(), e); + } + } + } + + @Override + public void startActivity(Intent intent) { + // + if (RePluginInternal.startActivity(this, intent)) { + // 这个地方不需要回调startActivityAfter,因为RePluginInternal最终还是会回调回来,最终还是要走super.startActivity() + return; + } + + super.startActivity(intent); + } + + @Override + public void startActivityForResult(Intent intent, int requestCode) { + startActivityForResult(intent, requestCode, null); + } + + @Override + public void startActivityForResult(Intent intent, int requestCode, Bundle options) { + // + if (RePluginInternal.startActivityForResult(this, intent, requestCode, options)) { + return; + } + + if (Build.VERSION.SDK_INT >= 16) { + super.startActivityForResult(intent, requestCode, options); + } else { + super.startActivityForResult(intent, requestCode); + } + } + + /** + * 这里的做法是支持Fragment中调用startActivityForResult特性 + *
+ * 由于卫士的插件需要hook住XXX-Activity的startActivity和startActivityForResult接口 + * 但早期版本的support-v4在startActivityFromFragment中直接调用了super.startActivityForResult, 因此这里还需要hook住这个点 + * 但新版的support-v4中Fragment最终的调用链还是会走到本XXX-Activity的startActivityForResult接口,因此不需要适配startActivityFromFragment(Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options)接口 + * + * @param fragment + * @param intent + * @param requestCode + */ + @Override + public void startActivityFromFragment(Fragment fragment, Intent intent, int requestCode) { + if (requestCode == -1) { + startActivityForResult(intent, -1); + } else if ((requestCode & -65536) != 0) { + throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); + } else { + int newRequestCode = -1; + try { + Field f = Fragment.class.getDeclaredField("mIndex"); + boolean acc = f.isAccessible(); + if (!acc) { + f.setAccessible(true); + } + Object o = f.get(fragment); + if (!acc) { + f.setAccessible(acc); + } + int index = (Integer) o; + newRequestCode = ((index + 1) << 16) + (requestCode & '\uffff'); + } catch (Throwable e) { + // Do Noting + } + startActivityForResult(intent, newRequestCode); + } + } + + @Override + public String getPackageCodePath() { + return super.getPackageCodePath(); + } +} diff --git a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/b/PluginLocalBroadcastManager.java b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/b/PluginLocalBroadcastManager.java index daa3fd5..3576aee 100644 --- a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/b/PluginLocalBroadcastManager.java +++ b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/loader/b/PluginLocalBroadcastManager.java @@ -28,6 +28,7 @@ import com.qihoo360.replugin.MethodInvoker; import com.qihoo360.replugin.RePluginEnv; import com.qihoo360.replugin.RePluginFramework; +import com.qihoo360.replugin.compat.CompatConfig; import java.util.ArrayList; import java.util.HashMap; @@ -390,7 +391,7 @@ public static class ProxyLocalBroadcastManagerVar { public static void initLocked(final ClassLoader classLoader) { // 填充LocalBroadcastManager各方法 - final String localBroadcastManager = "android.support.v4.content.LocalBroadcastManager"; + final String localBroadcastManager = CompatConfig.DEPENDENCY_ANDROIDX ? "androidx.localbroadcastmanager.content.LocalBroadcastManager" : "android.support.v4.content.LocalBroadcastManager"; getInstance = new MethodInvoker(classLoader, localBroadcastManager, "getInstance", new Class>[]{Context.class}); registerReceiver = new MethodInvoker(classLoader, localBroadcastManager, "registerReceiver", new Class>[]{BroadcastReceiver.class, IntentFilter.class}); unregisterReceiver = new MethodInvoker(classLoader, localBroadcastManager, "unregisterReceiver", new Class>[]{BroadcastReceiver.class}); diff --git a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/model/PluginInfo.java b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/model/PluginInfo.java index 1a56b28..80fe264 100644 --- a/replugin-plugin-library/src/main/java/com/qihoo360/replugin/model/PluginInfo.java +++ b/replugin-plugin-library/src/main/java/com/qihoo360/replugin/model/PluginInfo.java @@ -20,7 +20,6 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; -import android.support.annotation.NonNull; import android.text.TextUtils; import com.qihoo360.replugin.RePlugin; @@ -369,7 +368,6 @@ public String getApkDir() { * @param dirSuffix 目录后缀 * @return 插件的Dex所在目录的File对象 */ - @NonNull private File getDexDir(File dexDir, String dirSuffix) { File dir = new File(dexDir, makeInstalledFileName() + dirSuffix);