关于 DreamCat

主题名称:DreamCat | 版本:3.0.240224

主题开发:HanFengA7 | CornWorld

Designed by HanFengA7 Power by Typecho

Copyright © 2015-2025 by LychApe All rights reserved!

menu
refresh

如何在一个Kirin 990的麒麟OS中安装KMRE

作者: ciaoℒy

时间:

最近搞了一个擎云585的终端, 是个Kirin 990的机器, 真的是非常的垃圾啊. 寻思着既然是个ARM机器, 那用安卓应该会很棒吧? 发现在应用商店里有一个"移动运行环境(KMRE)". 遂安装试试. 然而安装不能用啊

背景

为什么我不能直接安装呢? 是因为我使用了docker-ce. 系统自带的docker.io版本比较老旧, 不能满足我的需求, 所以我安装了docker-ce. 但是现在kmre软件包就是依赖docker.io, 那该咋办呢?

(BTW, 我搜了一下Kylin OS V10SP1是基于Ubuntu bionic的, 所以我使用了docker官方的Ubuntu bionic源就可以了:

deb [arch=arm64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu bionic stable

改造kmre

首先想到的是解包原版的kmre并修改它的依赖.

# 注意这里是apt-get而不是apt, apt不带download命令
# 下载kmre软件包
apt-get download kmre

#解包
dpkg-deb -R kmre_2.3.19.0hw_arm64.deb kmre-mod

然后修改文件kmre-mod/DEBIAN/control文件, 把其中Dependsdocker.io给修改为docker-ce. 这里我除了修改依赖关系外, 我还修改了软件包的版本号: 从2.3.19.0hw修改为了2.3.19.1hw. 因为我发现如果不增加一个版本号的话, 自带的应用商店会一直提醒"更新kmre", 这样就不能在应用商店中安装Android应用了.

之后使用dpkg命令重新打包即可.

# 重新打包
dpkg-deb --build kmre-mod kmre-fixed.deb

# 安装
sudo apt install ./kmre-fixed.deb

改造libkmre

按照上述方法安装了kmre之后, 可以使用命令startapp com.android.settings启动环境试试. 正常来说这里应该会启动一个安卓系统的"设置"程序.

但是到这一步还是会遇到一个问题, 就是当运行KMRE APK 安装器时会遇到报错: "麒麟移动运行环境未安装!请到软件商店安装移动环境"

KMRE APK 安装器报错

查看项目的源代码, 在翻译文件开始定位报错的根源. 最终可以定位到是kylin-kmre-apk-installer调用了/usr/lib/libkmre.so中的is_android_env_installed方法检测kmre.

继续查看libkmre.so的源代码, 原来在is_android_env_installed里也检查了是否安装了docker.io. 将该仓库克隆到本地, 之后将其中的is_deb_package_installed("docker.io")全部给替换成(is_deb_package_installed("docker.io") || is_deb_package_installed("docker-ce")), 然后重新构建该项目.

# 构建前需要安装protobuf依赖, 包括protoc和libprotobuf-dev. 
# 如果遇到依赖问题的话, 可以试试先把冲突的包给remove掉, 再逐步安装
make -j8
sudo make uninstall

# 这里可以修改Makefile, 把install指令相关的行给接触注释, 之后直接
#sudo make install
# 或者直接手工复制
sudo cp ./libkmre.so /usr/lib/libkmre.so && chmod +x /usr/lib/libkmre.so

之后再启动KMRE APK 安装器的时候报错就变成了"麒麟移动运行环境未启动,请到软件商店启动移动环境". 这时候直接启动一下"设置"就可以了

startapp com.android.settings

安装adb

本来安装adb只需要一条命令sudo apt install adb就可以了. 然而我这边安装的时候却遇到了依赖问题.

adb依赖android-libadb, 而android-libadb依赖android-libbase=1:8.1.0+r23-5kylin2. 但是不知道为什么, apt说我的系统里已经安装了android-libbase=1:8.1.0+r23-5kylin2k0.2.

我解决这个依赖问题就是先卸载再重新安装:

# 先卸载依赖, 每次卸载都看看它的影响有多大.
sudo apt remove kmre-apk-installer
sudo apt remove android-libadb
sudo apt remove android-libbase
sudo apt remove android-liblog

# 安装adb
sudo apt install adb
sudo apt install kmre-apk-installer

之后就可以正常使用了.

管理应用

本以为使用adb就可以管理应用了, 然而执行adb devices后发现竟然发现不到设备. emmmmmm. 查看了一下libkmre.so的源代码, 发现可以用这个库来管理设备. 用ChatGPT帮忙生成了一份代码如下:

import ctypes
import json
import subprocess
import sys
import tkinter as tk
from tkinter import ttk, messagebox

LIB_PATH = "/usr/lib/libkmre.so"

class AppViewer(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("App 列表查看器")
        self.geometry("600x400")

        self.lib = self.load_library()
        self.app_list = self.get_app_list()

        self.tooltip = None
        self.last_iid = None

        self.create_table()

    def load_library(self):
        try:
            lib = ctypes.CDLL(LIB_PATH)
            lib.get_installed_applist.restype = ctypes.c_char_p
            lib.uninstall_app.argtypes = [ctypes.c_char_p]
            lib.uninstall_app.restype = ctypes.c_int
            return lib
        except OSError:
            messagebox.showerror("错误", "加载动态链接库 libkmre.so 失败")
            self.destroy()
            sys.exit(1)

    def get_app_list(self):
        try:
            result = self.lib.get_installed_applist()
            json_str = result.decode("utf-8")
            return json.loads(json_str)
        except Exception as e:
            messagebox.showerror("错误", f"获取应用列表失败: {str(e)}")
            self.destroy()
            sys.exit(1)

    def create_table(self):
        columns = ("app_name", "version_name", "start", "uninstall")

        self.tree = ttk.Treeview(self, columns=columns, show="headings")
        self.tree.heading("app_name", text="应用名称")
        self.tree.heading("version_name", text="版本号")
        self.tree.heading("start", text="启动")
        self.tree.heading("uninstall", text="卸载")

        self.tree.column("app_name", width=250, anchor="center")
        self.tree.column("version_name", width=100, anchor="center")
        self.tree.column("start", width=60, anchor="center")
        self.tree.column("uninstall", width=60, anchor="center")

        self.tree.pack(fill=tk.BOTH, expand=True)

        for i, app in enumerate(self.app_list):
            self.tree.insert("", "end", iid=i, values=(app["app_name"], app["version_name"], "[▶ 启动]", "[🗑️ 卸载]"))

        self.tree.bind("<Motion>", self.on_mouse_motion)
        self.tree.bind("<Leave>", lambda e: self.hide_tooltip())
        self.tree.bind("<ButtonRelease-1>", self.on_tree_click)

    def on_mouse_motion(self, event):
        region = self.tree.identify("region", event.x, event.y)
        if region != "cell":
            self.hide_tooltip()
            return

        row_id = self.tree.identify_row(event.y)
        if not row_id or row_id == self.last_iid:
            return

        self.last_iid = row_id
        pkg = self.app_list[int(row_id)]["package_name"]
        self.show_tooltip(pkg)

    def on_tree_click(self, event):
        row_id = self.tree.identify_row(event.y)
        col = self.tree.identify_column(event.x)
        if row_id:
            if col == "#3":  # 点击启动按钮
                app = self.app_list[int(row_id)]
                self.start_app(app["package_name"])
            elif col == "#4":  # 点击卸载按钮
                app = self.app_list[int(row_id)]
                self.confirm_uninstall(app["package_name"])

    def show_tooltip(self, text):
        self.hide_tooltip()
        x = self.winfo_pointerx()
        y = self.winfo_pointery()
        self.tooltip = tk.Toplevel(self)
        self.tooltip.wm_overrideredirect(True)
        self.tooltip.geometry(f"+{x + 10}+{y + 10}")
        label = tk.Label(self.tooltip, text=text, background="yellow", relief="solid", borderwidth=1)
        label.pack()

    def hide_tooltip(self):
        if self.tooltip:
            self.tooltip.destroy()
            self.tooltip = None
            self.last_iid = None

    def start_app(self, package_name):
        try:
            subprocess.Popen(["startapp", package_name])
        except Exception as e:
            messagebox.showwarning("启动失败", f"无法启动 {package_name}:{str(e)}")

    def confirm_uninstall(self, package_name):
        # 弹出确认框
        response = messagebox.askyesno("确认卸载", f"确定要卸载 {package_name} 吗?")
        if response:
            self.uninstall_app(package_name)

    def uninstall_app(self, package_name):
        try:
            result = self.lib.uninstall_app(package_name.encode("utf-8"))
            if result == 1:
                messagebox.showinfo("成功", f"{package_name} 卸载成功")
            elif result == -1:
                messagebox.showerror("错误", f"{package_name} 卸载失败:未指明的原因")
            elif result == -2:
                messagebox.showerror("错误", f"{package_name} 卸载失败:设备管理器问题")
            elif result == -3:
                messagebox.showerror("错误", f"{package_name} 卸载失败:用户受限")
            elif result == -4:
                messagebox.showerror("错误", f"{package_name} 卸载失败:设备所有者已阻止")
            elif result == -5:
                messagebox.showerror("错误", f"{package_name} 卸载失败:操作中止")
            elif result == -6:
                messagebox.showerror("错误", f"{package_name} 卸载失败:共享库依赖")
            elif result == -7:
                messagebox.showerror("错误", f"{package_name} 卸载失败:其他原因")
            else:
                messagebox.showerror("错误", f"{package_name} 卸载失败:未知错误")
        except Exception as e:
            messagebox.showerror("错误", f"卸载过程中发生错误:{str(e)}")

if __name__ == "__main__":
    app = AppViewer()
    app.mainloop()

#本文链接:https://blog.chaol.top/archives/101.html
#本文采用 CC BY-NC-SA 4.0 协议进行许可
#如无特别声明,该文章均为 ciaoℒy 原创,转载请遵循 署名-非商业性使用 4.0 国际(CC BY-NC 4.0)协议,即转载请注明文章来源。
#最后编辑时间为: 2025 年 05 月 03 日
none

create 添加新评论


account_circle
email
language
textsms



加我的QQ
加我的微博
加我的支付宝
加我的微信