如何在应用中嵌入其他应用并叠加布局(附BlockLauncher参考案例)
嘿,很高兴你参考了BlockLauncher这个项目——我正好研究过它的源码,它实现内嵌游戏的核心逻辑其实是围绕原生库加载+游戏视图嵌入来做的,给你一步步拆解具体怎么实现:
步骤1:准备游戏原生库文件
首先你得拿到目标游戏的原生库(一般是.so格式),这些文件对应不同的CPU架构(比如arm64-v8a、armeabi-v7a)。把它们放到Android项目的src/main/jniLibs目录下,这样打包时会自动适配不同设备的架构,包含对应的库文件。
步骤2:加载游戏原生库
BlockLauncher主要用System.loadLibrary()来加载库,这个方法不需要写绝对路径,只需要传入库名(比如游戏库叫libgame.so,就传"game")。你可以在自定义的Application类或者游戏承载Activity的onCreate()方法里提前加载:
static { try { // 按依赖顺序加载库,先加载依赖库再加载主游戏库 System.loadLibrary("game_dependency"); System.loadLibrary("game"); } catch (UnsatisfiedLinkError e) { e.printStackTrace(); // 处理加载失败的情况,比如提示用户安装对应架构的版本 } }
如果你的库文件不在默认路径,也可以用System.load()传入绝对路径,但适配性不如前者,不推荐。
步骤3:嵌入游戏视图
加载库之后,游戏通常会提供一个可嵌入的View(比如SurfaceView或者自定义GameView),或者需要你继承游戏的主Activity。比如BlockLauncher就是继承了Minecraft的主Activity,然后在里面插入自己的逻辑。
如果是View形式,你可以直接在自己的布局里嵌套:
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 游戏主视图 --> <com.game.GameView android:id="@+id/game_view" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- 用于放置悬浮按钮的容器,层级在游戏之上 --> <RelativeLayout android:id="@+id/overlay_container" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout>
步骤4:实现悬浮操作按钮
悬浮按钮需要脱离Activity的窗口层级,用WindowManager创建全局窗口,这样游戏全屏时也能显示。核心步骤:
- 申请
SYSTEM_ALERT_WINDOW权限(Android 6.0以上需要动态申请,还要引导用户到系统设置里开启); - 创建按钮布局(比如一个ImageView或FloatingActionButton);
- 通过
WindowManager.LayoutParams设置窗口类型(比如TYPE_APPLICATION_OVERLAY)和位置参数; - 添加到WindowManager并绑定点击事件(比如触发截图)。
示例代码片段:
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); ImageView fabScreenshot = new ImageView(this); fabScreenshot.setImageResource(R.drawable.ic_screenshot); WindowManager.LayoutParams params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ); params.gravity = Gravity.BOTTOM | Gravity.RIGHT; params.x = 20; params.y = 20; windowManager.addView(fabScreenshot, params); fabScreenshot.setOnClickListener(v -> takeScreenshot());
步骤5:实现截图功能
如果游戏用普通View渲染,直接用getDrawingCache()就能获取截图:
private void takeScreenshot() { View gameView = findViewById(R.id.game_view); gameView.setDrawingCacheEnabled(true); Bitmap bitmap = Bitmap.createBitmap(gameView.getDrawingCache()); gameView.setDrawingCacheEnabled(false); // 保存截图到本地 saveBitmapToFile(bitmap); }
但如果是SurfaceView或OpenGL渲染的游戏,getDrawingCache()会失效,这时候要用MediaProjection API实现全局截图(需要用户授权,是Android官方推荐的稳定方案)。
步骤6:其他工具集成
比如帧率显示,可以通过Choreographer监听帧回调计算帧率,再显示在悬浮窗;快捷设置(比如调整画质、音量)则需要调用游戏暴露的JNI接口,或者通过反射调用内部方法(注意兼容性,不同游戏逻辑差异大)。
关键注意事项
- 权限适配:除了悬浮窗权限,保存截图需要
WRITE_EXTERNAL_STORAGE权限(Android 10+可以用MediaStore替代); - 兼容性:不同游戏的库结构、视图逻辑差异很大,你可能需要像BlockLauncher那样,针对目标游戏做定制化适配;
- 性能优化:游戏本身资源消耗大,你的内嵌工具要尽量轻量化,比如Activity销毁时及时移除悬浮窗,后台时暂停游戏避免耗电。
内容的提问来源于stack exchange,提问作者Han Programer

