前言:
上一篇文章
Android手写占位式插件化框架之Activity通信、Service通信和BroadcastReceiver通信
问题引出,在宿主app中获取插件包中静态注册的广播接收者StaticeReceiver,这个时候就需要apk解析原理系统源码分析,分析后进行再来操作。
apk解析原理系统源码分析笔记如下:
1.静态注册的广播是什么时候注册的? 手机开机的时候去,所有的APP再次进行安装一遍,安装后系统会去解析AndroidManifest.xml文件 解析静态广播后就会自动注册。 2.我们去分析安装 会在data/app/下放置目录 这是系统安装时做的事情 data/data/包名/ 应用所属目录 data/dalvik-cache 虚拟机去加载执行指令 3.该分析哪个目录? data/app 放置目录 手机开机安装APP的时候,安装之后,马上就会全盘扫描 data/app 放置目录 解析出APP apk文件里面所有的组件,包括权限 系统会解析AndroidManifest.xml文件 Android系统会在安装过后,会马上扫描此目录data/app 放置目录 -->解析apk文件里面的配置信息AndroidManifest.xml 如果里面有静态配置的广播,就会要去注册广播。 分析系统源码,是如何进行解析apk PackageManagerService 目标:看系统是如何去解析APK文件里面的组件信息的。 系统是在安装的时候才会去扫描APK 分析PackageManagerService 是由谁启动的 手机开机的时候 Linux内核驱动-->init进程-->zygote进程 孵化SystemServer进程--> 把Android所有的服务启动一下(包括PackageManagerService启动) PackageManagerService启动 PMS如何去处理data/app/目录,如何解析APK /** 存储已安装应用程序的目录 */ private static final File sAppInstallDir = new File(Environment.getDataDirectory(), "app"); sAppInstallDir:/data/app/ 目录 sAppInstallDir,如何去解析APK文件的 scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0, packageParser, executorService); 这个方法就是扫描/data/app/目录下的apk文件-->解析AndroidManifest.xml里面的所有信息 扫描APK文件,解析APK,scanDirTracedLI--> parsePackage:解析apk文件里面的所有信息 Package-->apk里面的AndroidManifest配置信息(所有的) 拿到了Package,就能拿到静态的广播信息 最终的目标: <!--静态注册的广播--> <receiver android:name=".StaticReceiver"> <intent-filter> <action android:name="plugin.static_receiver" /> </intent-filter> </receiver>
分析完apk解析原理后,然后通过反射技术进行获取对应的信息。
一、在宿主APP中的PluginManager类中,增加一个方法parserApkAction(),通过反射源码,来解析apk文件里的所有信息。
/** * @Author: ly * @Date: 2023/7/14 * @Description: 插件管理类,获取插件中的资源Resources和类加载器DexClassLoader */ public class PluginManager { private static final String TAG = PluginManager.class.getSimpleName(); private static PluginManager pluginManager; private Context context; //Activity class private DexClassLoader dexClassLoader; private Resources resources; private PluginManager(Context context) { this.context = context; } public static PluginManager getInstance(Context context) { if (pluginManager == null) { synchronized (PluginManager.class) { if (pluginManager == null) { pluginManager = new PluginManager(context); } return pluginManager; } } return pluginManager; } /** * 加载插件(2.1 Activity class, 2.2 layout) */ public void loadPlugin() { try { //getExternalFilesDir:表示应用程序的私有目录 File privateDir = context.getExternalFilesDir(null); //路径: /storage/emulated/0/Android/data/com.example.pluginproject/files Log.i(TAG, "privateDir: " + privateDir.getAbsolutePath()); File file = new File(privateDir.getAbsolutePath() + File.separator + "p.apk"); if (!file.exists()) { Log.d(TAG, "插件包,不存在"); return; } String pluginPath = file.getAbsolutePath(); //下面是加载插件里面的class //dexClassLoader 需要一个缓存目录 /data/data/当前应用的包名/pDir File fileDir = context.getDir("pDir", Context.MODE_PRIVATE); //fileDir.getAbsolutePath(): /data/user/0/com.example.pluginproject/app_pDir Log.d(TAG, "fileDir: " + fileDir.getAbsolutePath()); //pluginPath:插件文件的路径,表示插件APK文件的位置。 //fileDir.getAbsolutePath():表示应用程序的私有目录路径,作为DexClassLoader的第二个参数传递,用于指定Dex文件的输出目录。 //null:表示没有指定库(Native Library)的路径,如果插件中有依赖的库文件,可以传入库目录的路径。 //context.getClassLoader():获取应用程序的类加载器作为DexClassLoader的父类加载器。 dexClassLoader = new DexClassLoader(pluginPath, fileDir.getAbsolutePath(), null, context.getClassLoader()); //下面是加载插件里面的layout文件 //加载资源 AssetManager assetManager = AssetManager.class.newInstance(); //我们执行此方法,为了把插件包的路径添加进去 // public int addAssetPath(String path) Method method = assetManager.getClass().getMethod("addAssetPath", String.class);//类类型Class method.invoke(assetManager, pluginPath);//插件包的路径,pluginPath Resources r = context.getResources();//宿主的资源配置信息 //特殊的resource,加载插件里面的资源的resource this.resources = new Resources(assetManager, r.getDisplayMetrics(), r.getConfiguration());//参数二和参数三,配置信息 } catch (Exception e) { e.printStackTrace(); } } public ClassLoader getClassLoader() { return dexClassLoader; } public Resources getResources() { return resources; } /** * 反射系统源码,来解析apk文件里的所有信息 */ public void parserApkAction() { //1.执行此方法public Package parsePackage(File packageFile, int flags),就是为了拿到Package try { //getExternalFilesDir:表示应用程序的私有目录 File privateDir = context.getExternalFilesDir(null); //路径: /storage/emulated/0/Android/data/com.example.pluginproject/files Log.i(TAG, "privateDir: " + privateDir.getAbsolutePath()); File file = new File(privateDir.getAbsolutePath() + File.separator + "p.apk"); if (!file.exists()) { Log.d(TAG, "插件包,不存在"); return; } //实例化PackageParser对象 Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser"); Object packageParser = packageParserClass.newInstance(); Method parsePackageMethod = packageParserClass.getMethod("parsePackage", File.class, int.class); Object mPackage = parsePackageMethod.invoke(packageParser, file, PackageManager.GET_ACTIVITIES);//执行方法 //继续分析Package //得到receivers Field receiversFiled = mPackage.getClass().getDeclaredField("receivers"); Object receivers = receiversFiled.get(mPackage); ArrayList arrayList = (ArrayList) receivers; //此Activity不是组件的Activity,是PackageParser类的内部类 for (Object mActivity : arrayList) {//mActivity 对应 <receiver android:name=".StaticReceiver"> //获取<intent-filter> intents == 对应很多intent-filter //通过反射拿到intents Class<?> componentClass = Class.forName("android.content.pm.PackageParser$Component"); Field intentsField = componentClass.getDeclaredField("intents"); ArrayList<IntentFilter> intents = (ArrayList) intentsField.get(mActivity); Class<?> packageUserStateClass = Class.forName("android.content.pm.PackageUserState"); Class<?> userHandleClass = Class.forName("android.os.UserHandle"); Method getCallingUserIdMethod = userHandleClass.getMethod("getCallingUserId"); int userId = (int) getCallingUserIdMethod.invoke(null); //拿到android:name=".StaticReceiver" //ActivityInfo.name --> android:name=".StaticReceiver" //分析源码如何拿到ActivityInfo //执行此方法generateActivityInfo,就能拿到ActivityInfo //public static final ActivityInfo generateActivityInfo(Activity a, int flags, // PackageUserState state, int userId) Method generateActivityInfoMethod = packageParserClass.getMethod("generateActivityInfo", mActivity.getClass(), int.class, packageUserStateClass, int.class); //执行此方法,拿到ActivityInfo ActivityInfo activityInfo = (ActivityInfo) generateActivityInfoMethod.invoke(null, mActivity, 0, packageUserStateClass.newInstance(), userId); String receiverClassName = activityInfo.name;//com.example.plugin_package.StaticReceiver BroadcastReceiver broadcastReceiver = (BroadcastReceiver) getClassLoader().loadClass(receiverClassName).newInstance(); for (IntentFilter intentFilter : intents) { //注册广播 context.registerReceiver(broadcastReceiver, intentFilter); } } } catch (Exception e) { e.printStackTrace(); } } }
1.2 MainActivity中增加两个方法分别为:loadStaticReceiver,sendStaticReceiver
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 0); } } @Override protected void onStart() { super.onStart(); } @Override protected void onResume() { super.onResume(); } @Override protected void onPause() { super.onPause(); } @Override protected void onStop() { super.onStop(); } @Override protected void onDestroy() { super.onDestroy(); } /** * 加载插件 * * @param view */ public void loadPlugin(View view) { PluginManager.getInstance(this).loadPlugin(); } /** * 启动插件里面的Activity * * @param view */ public void startPluginActivity(View view) { File privateDir = getExternalFilesDir(null); File file = new File(privateDir.getAbsolutePath() + File.separator + "p.apk"); String path = file.getAbsolutePath(); File file1 = new File(path); if (!file1.exists() || file1.isFile()) { Log.i("TAG", "插件包路径无效"); } Log.i("TAG", "path: " + path); //获取插件包里面的Activity PackageManager packageManager = getPackageManager(); PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES); ActivityInfo activityInfo = packageInfo.activities[1]; //占位 代理Activity Intent intent = new Intent(this, ProxyActivity.class); // intent.putExtra("className", "com.example.plugin_package.PluginActivity"); intent.putExtra("className", activityInfo.name); startActivity(intent); } /** * 注册插件里面配置的静态广播 * * @param view */ public void loadStaticReceiver(View view) { PluginManager.getInstance(this).parserApkAction(); } /** * 发送给静态广播接收者 * * @param view */ public void sendStaticReceiver(View view) { Intent intent = new Intent(); intent.setAction("plugin.static_receiver"); sendBroadcast(intent); } }
二、在插件包声明类StaticReceiver
/** * @Author: ly * @Date: 2023/7/15 * @Description: 插件包中的静态广播 */ public class StaticReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "我是静态注册的广播,我收到广播了!", Toast.LENGTH_SHORT).show(); } }
2.1 在AndroidManifest.xml文件中进行注册
<!--静态注册的广播--> <receiver android:name=".StaticReceiver"> <intent-filter> <action android:name="plugin.static_receiver" /> </intent-filter> </receiver>
效果图如下: