以太坊安卓钱包开发:账号导入与多账号管理全流程解析

·

关键词:以太坊安卓钱包、私钥导入、KeyStore、助记词、greenDAO、ETHWallet、账号管理、ReactiveX、钱包开发


上一篇我们讲解了如何在安卓中从零开始创建以太坊助记词钱包,本篇将从 三种主流导入方式(私钥、KeyStore、助记词)与 持久化存储+多账号切换 两个维度,给出可直接落地的代码级演示。

思维导图:用户为什么需要“导入模式”?

  1. 换机或重装 App 时需还原资产
  2. 探索第三方工具后想转回自研钱包
  3. 为了隔断旧钱包潜在风险,先把所有币转移到“新生成账号”,再把旧账号导入仅做查看——既能验证资产,又降低被盗概率

因此 UI 层应在显眼位置 二次提示用户备份:“若老钱包私钥可能已泄露,请先建新钱包+转账,再导入旧钱包。”

三种账号导入方案

1. 私钥导入——最直接的密钥复原

核心:将 64 位十六进制私钥转为 ECKeyPair,再调用既定 generateWallet()

public static ETHWallet loadWalletByPrivateKey(String privateKey, String pwd) {
    ECKeyPair ecKeyPair = ECKeyPair.create(Numeric.toBigInt(privateKey));
    return generateWallet(generateNewWalletName(), pwd, ecKeyPair);
}
⚠️ 注意:这里的 pwd 仅用于本地 KeyStore 文件加解密,链上交易并不使用它。

FAQ
Q:私钥带回车、0x 前缀能否兼容?
A:可用 Numeric.cleanHexPrefix() 清掉前缀,trim() 掉空白即可。

Q:如何防范粘贴板嗅探?
A:为私钥输入框启用 android:inputType="textVisiblePassword" + android:importantForAutofill="no",必要时配合 自定义键盘清剪贴板

2. KeyStore 文件导入——兼顾安全与易用

  1. 把 json 字符串反序列化为 WalletFile
  2. 用本地密码解密得到 ECKeyPair
public static ETHWallet loadWalletByKeystore(String keystore, String pwd) {
    WalletFile walletFile = objectMapper.readValue(keystore, WalletFile.class);
    return generateWallet(generateNewWalletName(), pwd,
                         Wallet.decrypt(pwd, walletFile));
}

提示 UI 提供 文件选择器文本粘贴 双通道,提升用户体验。

3. 助记词导入——BIP44 派生自由自在

DeterministicSeed ds = new DeterministicSeed(
        Arrays.asList(mnemonic.split(" ")),
        null, "", System.currentTimeMillis()/1000);
return generateWalletByMnemonic(name, ds, pathArray, pwd);
与创建新助记词钱包最大的区别:随机熵不由系统产生,而是完全来自用户输入。记得校验路径长度及前缀防止非法参数。

持久化:把 ETHWallet 写进 SQLite 而不是内存

无论哪种方式,最终都会得到一个 ETHWallet 对象,它包含地址、名称、加密密码、keystore 路径、助记词、是否选中等多种元信息。为了让下一次进 App 仍能读取,必须落库。

1) greenDAO 实体映射

@Entity
public class ETHWallet {
    @Id(autoincrement = true)
    private Long id;
    public String address;
    private String name;
    private String password;   // 加密后的 pwd
    private String keystorePath;
    private String mnemonic;
    private boolean isCurrent;
    private boolean isBackup;
}

2) 初始化数据库(建议放在 Application.onCreate)

DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "wallet.db", null);
SQLiteDatabase db = helper.getWritableDatabase();
DaoSession daoSession = new DaoMaster(db).newSession();
ETHWalletDao ethWalletDao = daoSession.getETHWalletDao();

3) 一行插入

ethWalletDao.insert(ethWallet);

👉 查看完整绿色数据库升级策略示例助你避开踩坑陷阱

多账号切换:一个简单读改写流程

用户可能持有多个账号,为了在交易、查看余额时定位当前活跃账号,我们使用布尔字段 isCurrent 标识。

// 新钱包入库前把其余账号设为未选中
public static void insertNewWallet(ETHWallet ethWallet) {
    updateCurrent(-1);          // 取消旧选中
    ethWallet.setCurrent(true);
    ethWalletDao.insert(ethWallet);
}

// 切换选中
public static ETHWallet updateCurrent(long id) {
    List<ETHWallet> all = ethWalletDao.loadAll();
    ETHWallet active = null;
    for (ETHWallet w : all) {
        w.setCurrent(w.getId() == id);
        ethWalletDao.update(w);
        if (w.getId() == id) active = w;
    }
    return active;
}

小技巧

  • 数据库触发器/事件总线可在状态变化时刷新 UI
  • 对 WalletDaoUtils 统一加 @Dao 或 @Singleton 便于单元测试

FAQ
Q:多线程并发切换会不会出现双 “current”?
A:通过事务或 SQL 更新语句 UPDATE ethwallet SET isCurrent = (id = ?) 方言可瞬间一次性修改,天然避免竞态。

Q:需要缓存到内存吗?
A:建议把 currentWallet 置于全局单例,并监听数据库变化。首次启动从磁盘读取后常驻内存,减少 I/O。

打通“创建/导入 → 落库”链路:ReactiveX 示例

为何不用 AsyncTask?耗时加解密、磁盘 I/O 若放在主线程会阻塞 UI,引发 ANR。 ReactiveX 清晰分工:

public Single<ETHWallet> loadWalletByPrivateKey(final String pk, final String pwd) {
    return Single.fromCallable(() -> {
        ETHWallet wallet = ETHWalletUtils.loadWalletByPrivateKey(pk, pwd);
        if (wallet != null) WalletDaoUtils.insertNewWallet(wallet);
        return wallet;
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread());
}

👉 一键查看如何在 10 行代码内优雅切换到 RxJava3

小结与展望

通过本文,你已掌握:

  1. 三种安全导入(私钥、KeyStore、助记词)的 底线做法与易错点
  2. greenDAO 对象持久化最佳实践
  3. 多账号的 增删改查+切换 全流程
  4. ReactiveX 在 钱包开发中的线程解放技巧

下一篇我们会把目光集中到 资产显示与价格更新,手把手封装一个可读可扩展的 Token 列表!

若有疑问,欢迎在评论区或 小专栏区块核心 提问,我们下一篇见。