新建项目
新建一个项目,然后在项目中添加一个项目和一个module
主项目中添加HookManager
import static android.os.Environment.DIRECTORY_DOWNLOADS; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.os.Environment; import android.widget.Toast; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; import dalvik.system.DexClassLoader; /** * Created by Dou on 2019/7/27. */ public class HookManager { private static final HookManager ourInstance = new HookManager(); private Resources resources; private DexClassLoader loader; public PackageInfo packageInfo; public static HookManager getInstance() { return ourInstance; } private HookManager() { } //用来加载插件 public void loadPlugin(Activity activity) { // 假如这里是从网络获取的插件 我们直接从sd卡获取 然后读取到我们的cache目录 String pluginName = "plug-debug.apk"; File filesDir = activity.getDir("plugin", activity.MODE_PRIVATE); String filePath = new File(filesDir, pluginName).getAbsolutePath(); File file = new File(filePath); if (file.exists()) { file.delete(); } File plug = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS) + "/" + pluginName); FileInputStream is = null; FileOutputStream os = null; //读取的目录 try { is = new FileInputStream(plug); //要输入的目录 os = new FileOutputStream(filePath); } catch (FileNotFoundException e) { e.printStackTrace(); } try { int len = 0; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1) { os.write(buffer, 0, len); } File f = new File(filePath); if (f.exists()) { Toast.makeText(activity, "加载完成", Toast.LENGTH_LONG).show(); } loadPathToPlugin(activity); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { os.close(); is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private void loadPathToPlugin(Activity activity) { File filesDir = activity.getDir("plugin", activity.MODE_PRIVATE); String name = "plug-debug.apk"; String path = new File(filesDir, name).getAbsolutePath(); //然后我们开始加载我们的apk 使用DexClassLoader File dexOutDir = activity.getDir("dex", activity.MODE_PRIVATE); loader = new DexClassLoader(path, dexOutDir.getAbsolutePath(), null, activity.getClassLoader()); //通过PackAgemanager 来获取插件的第一个activity是哪一个 PackageManager packageManager = activity.getPackageManager(); packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES); //然后开始加载我们的资源 肯定要使用Resource 但是它是AssetManager创建出来的 就是AssertManager 有一个addAssertPath 这个方法 但是私有的 所有使用反射 Class<?> assetManagerClass = AssetManager.class; try { AssetManager assertManagerObj = (AssetManager) assetManagerClass.newInstance(); Method addAssetPathMethod = assetManagerClass.getMethod("addAssetPath", String.class); addAssetPathMethod.setAccessible(true); addAssetPathMethod.invoke(assertManagerObj, path); //在创建一个Resource resources = new Resources(assertManagerObj, activity.getResources().getDisplayMetrics(), activity.getResources().getConfiguration()); } catch (Exception e) { e.printStackTrace(); } } public ClassLoader getClassLoader() { return loader; } public Resources getResource() { return resources; } }
主项目中添加ProxyActivity
import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.os.Bundle; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.example.baseinterface.ProxyActivityInterface; import java.lang.reflect.Constructor; /** * 类说明 一个占位的activity */ public class ProxyActivity extends AppCompatActivity { private ProxyActivityInterface pluginObj; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //在这里拿到了真实跳转的activity 拿出来 再去启动真实的activity String className = getIntent().getStringExtra("ClassName"); //通过反射在去启动一个真实的activity 拿到Class对象 try { Class<?> plugClass = getClassLoader().loadClass(className); Constructor<?> pluginConstructor = plugClass.getConstructor(new Class[]{}); //因为插件的activity实现了我们的标准 pluginObj = (ProxyActivityInterface) pluginConstructor.newInstance(new Object[]{}); pluginObj.attach(this);//注入上下文 pluginObj.onCreate(new Bundle()); } catch (Exception e) { if (e.getClass().getSimpleName().endsWith("ClassCastException")) { finish(); Toast.makeText(this, "不是插件类", Toast.LENGTH_LONG).show(); return; } e.printStackTrace(); } } @Override public void startActivity(Intent intent) { String className1 = intent.getStringExtra("ClassName"); Intent intent1 = new Intent(this, ProxyActivity.class); intent1.putExtra("ClassName", className1); super.startActivity(intent1); } @Override public ClassLoader getClassLoader() { return HookManager.getInstance().getClassLoader(); } @Override public Resources getResources() { return HookManager.getInstance().getResource(); } @Override protected void onStart() { super.onStart(); pluginObj.onStart(); } @Override protected void onDestroy() { super.onDestroy(); pluginObj.onDestroy(); } @Override protected void onPause() { super.onPause(); pluginObj.onPause(); } }
MainActivity中的两个方法,加载插件和跳转操作
private void loadPlugin() { HookManager.getInstance().loadPlugin(this); } /** * 跳转插件 */ private void startProxy() { // Intent intent = new Intent(this, ProxyActivity.class); intent.putExtra("ClassName", HookManager.getInstance().packageInfo.activities[0].name); //intent.putExtra("ClassName", "MainActivity"); startActivity(intent); }
权限和activity声明添加到manifest
<activity android:name=".ProxyActivity" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
module中添加interface
public interface ProxyActivityInterface { //生命周期的activity public void attach(Activity proxyActivity); public void onCreate(Bundle savedInstanceState); public void onStart(); public void onPause(); public void onDestroy(); }
插件项目中添加BaseActivity
import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; import androidx.annotation.NonNull; import com.example.baseinterface.ProxyActivityInterface; /** * Created by Dou on 2019/7/27. * <p> * 和上下文有关的方法都要重写 */ public class BaseActivity extends Activity implements ProxyActivityInterface { public Activity that; @Override public void attach(Activity proxyActivity) { that = proxyActivity; } @Override public void onCreate(Bundle savedInstanceState) { } @Override public void onStart() { } @Override public void onPause() { } @Override public void onDestroy() { } @Override public void setContentView(View view) { if (that != null) { Log.e("---","taht=null"); that.setContentView(view); } else { super.setContentView(view); } } @Override public void setContentView(int layoutResID) { that.setContentView(layoutResID); } @Override public View findViewById(int id) { return that.findViewById(id); } @Override public Intent getIntent() { if (that != null) { return that.getIntent(); } return super.getIntent(); } @Override public ClassLoader getClassLoader() { return that.getClassLoader(); } @NonNull @Override public LayoutInflater getLayoutInflater() { return that.getLayoutInflater(); } @Override public ApplicationInfo getApplicationInfo() { return that.getApplicationInfo(); } @Override public Window getWindow() { return that.getWindow(); } @Override public WindowManager getWindowManager() { return that.getWindowManager(); } @Override public void startActivity(Intent intent) { Intent m = new Intent(); m.putExtra("ClassName", intent.getComponent().getClassName()); that.startActivity(m); } @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { return that.registerReceiver(receiver, filter); } @Override public void sendBroadcast(Intent intent) { that.sendBroadcast(intent); } @Override public void onSaveInstanceState(Bundle outState) { } }
插件中的编程和正常的一样,只是要继承BaseActivity
如果遇到打开插件后页面空白就把build删除后重新编译即可
Attempt to read from field 'java.lang.String android.content.pm.ActivityInfo.parentActivityName' on a null object reference
如果加载就闪退是因为你没开权限
标签:插件,实现,void,activity,Override,import,android,public From: https://www.cnblogs.com/Frank-dev-blog/p/16818279.html