使用studio写一个安卓app生成ETH跟TRX靓号生成器

42 阅读5分钟

ETH和TRX靓号生成器的Android应用。

1. 项目配置 (app/build.gradle)

plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 34

    defaultConfig {
        applicationId "com.example.walletaddressgenerator"
        minSdk 21
        targetSdk 34
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    
    kotlinOptions {
        jvmTarget = '1.8'
    }
    
    buildFeatures {
        viewBinding true
    }
}

dependencies {
    implementation 'androidx.core:core-ktx:1.12.0'
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.10.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'org.web3j:core:4.9.7'
    implementation 'org.tronj:tronj-core:2.1.4'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
    implementation 'androidx.activity:activity-ktx:1.8.1'
    
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

2. 主界面布局 (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:background="#f5f5f5">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <!-- 标题 -->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="ETH/TRX靓号生成器"
            android:textSize="24sp"
            android:textStyle="bold"
            android:textColor="#333333"
            android:gravity="center"
            android:layout_marginBottom="32dp" />

        <!-- 币种选择 -->
        <com.google.android.material.card.MaterialCardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="24dp"
            app:cardCornerRadius="12dp"
            app:cardElevation="4dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="16dp">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="选择币种"
                    android:textSize="16sp"
                    android:textStyle="bold"
                    android:layout_marginBottom="12dp" />

                <RadioGroup
                    android:id="@+id/radioGroup"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal">

                    <RadioButton
                        android:id="@+id/radioEth"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="ETH"
                        android:checked="true" />

                    <RadioButton
                        android:id="@+id/radioTrx"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="TRX" />
                </RadioGroup>
            </LinearLayout>
        </com.google.android.material.card.MaterialCardView>

        <!-- 靓号规则设置 -->
        <com.google.android.material.card.MaterialCardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="24dp"
            app:cardCornerRadius="12dp"
            app:cardElevation="4dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="16dp">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="靓号规则设置"
                    android:textSize="16sp"
                    android:textStyle="bold"
                    android:layout_marginBottom="12dp" />

                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="16dp"
                    app:boxCornerRadiusBottomEnd="8dp"
                    app:boxCornerRadiusBottomStart="8dp"
                    app:boxCornerRadiusTopEnd="8dp"
                    app:boxCornerRadiusTopStart="8dp">

                    <com.google.android.material.textfield.TextInputEditText
                        android:id="@+id/etPrefix"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="地址前缀 (如: 0x888)"
                        android:maxLines="1" />
                </com.google.android.material.textfield.TextInputLayout>

                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="16dp"
                    app:boxCornerRadiusBottomEnd="8dp"
                    app:boxCornerRadiusBottomStart="8dp"
                    app:boxCornerRadiusTopEnd="8dp"
                    app:boxCornerRadiusTopStart="8dp">

                    <com.google.android.material.textfield.TextInputEditText
                        android:id="@+id/etSuffix"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="地址后缀 (如: 888)"
                        android:maxLines="1" />
                </com.google.android.material.textfield.TextInputLayout>

                <com.google.android.material.textfield.TextInputLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:boxCornerRadiusBottomEnd="8dp"
                    app:boxCornerRadiusBottomStart="8dp"
                    app:boxCornerRadiusTopEnd="8dp"
                    app:boxCornerRadiusTopStart="8dp">

                    <com.google.android.material.textfield.TextInputEditText
                        android:id="@+id/etCount"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:hint="生成数量 (默认: 10)"
                        android:inputType="number"
                        android:maxLines="1"
                        android:text="10" />
                </com.google.android.material.textfield.TextInputLayout>
            </LinearLayout>
        </com.google.android.material.card.MaterialCardView>

        <!-- 控制按钮 -->
        <com.google.android.material.card.MaterialCardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="24dp"
            app:cardCornerRadius="12dp"
            app:cardElevation="4dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal"
                android:padding="16dp">

                <Button
                    android:id="@+id/btnGenerate"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:layout_marginEnd="8dp"
                    android:text="开始生成"
                    android:backgroundTint="#4CAF50"
                    android:textColor="@android:color/white"
                    style="@style/Widget.Material3.Button" />

                <Button
                    android:id="@+id/btnStop"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:layout_marginStart="8dp"
                    android:text="停止生成"
                    android:backgroundTint="#F44336"
                    android:textColor="@android:color/white"
                    style="@style/Widget.Material3.Button" />
            </LinearLayout>
        </com.google.android.material.card.MaterialCardView>

        <!-- 生成结果 -->
        <com.google.android.material.card.MaterialCardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:cardCornerRadius="12dp"
            app:cardElevation="4dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="16dp">

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="生成结果"
                    android:textSize="16sp"
                    android:textStyle="bold"
                    android:layout_marginBottom="12dp" />

                <TextView
                    android:id="@+id/tvStatus"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:text="准备就绪"
                    android:textSize="14sp"
                    android:layout_marginBottom="16dp"
                    android:textColor="#666666" />

                <androidx.recyclerview.widget.RecyclerView
                    android:id="@+id/recyclerView"
                    android:layout_width="match_parent"
                    android:layout_height="400dp"
                    android:background="#fafafa" />
            </LinearLayout>
        </com.google.android.material.card.MaterialCardView>
    </LinearLayout>
</ScrollView>

3. 列表项布局 (res/layout/item_address.xml)

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="2dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="16dp">

        <TextView
            android:id="@+id/tvAddress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="地址"
            android:textSize="14sp"
            android:textStyle="bold"
            android:layout_marginBottom="8dp" />

        <TextView
            android:id="@+id/tvPrivateKey"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="私钥"
            android:textSize="12sp"
            android:textColor="#666666"
            android:layout_marginBottom="8dp" />

        <TextView
            android:id="@+id/tvIndex"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="序号"
            android:textSize="12sp"
            android:textColor="#999999" />

    </LinearLayout>
</com.google.android.material.card.MaterialCardView>

4. 数据模型 (AddressInfo.kt)

data class AddressInfo(
    val index: Int,
    val address: String,
    val privateKey: String,
    val coinType: String
)

5. 适配器 (AddressAdapter.kt)

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class AddressAdapter : RecyclerView.Adapter<AddressAdapter.ViewHolder>() {

    private val addresses = mutableListOf<AddressInfo>()

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val tvIndex: TextView = itemView.findViewById(R.id.tvIndex)
        val tvAddress: TextView = itemView.findViewById(R.id.tvAddress)
        val tvPrivateKey: TextView = itemView.findViewById(R.id.tvPrivateKey)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_address, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val addressInfo = addresses[position]
        holder.tvIndex.text = "序号: ${addressInfo.index}"
        holder.tvAddress.text = "地址: ${addressInfo.address}"
        holder.tvPrivateKey.text = "私钥: ${addressInfo.privateKey}"
    }

    override fun getItemCount(): Int = addresses.size

    fun addAddress(addressInfo: AddressInfo) {
        addresses.add(0, addressInfo) // 添加到开头
        notifyItemInserted(0)
    }

    fun clearAddresses() {
        addresses.clear()
        notifyDataSetChanged()
    }
}

6. 钱包生成工具 (WalletGenerator.kt)

import org.web3j.crypto.ECKeyPair
import org.web3j.crypto.Keys
import org.web3j.utils.Numeric
import java.math.BigInteger
import java.security.SecureRandom

object WalletGenerator {

    private val secureRandom = SecureRandom()

    // 生成ETH钱包
    fun generateEthWallet(): Pair<String, String> {
        val ecKeyPair = Keys.createEcKeyPair()
        val publicKey = "0x" + Keys.getAddress(ecKeyPair)
        val privateKey = ecKeyPair.privateKey.toString(16)
        return Pair(publicKey, privateKey)
    }

    // 生成TRX钱包 (简化版,实际TRX地址生成更复杂)
    fun generateTrxWallet(): Pair<String, String> {
        // TRX地址生成逻辑 (这里使用简化的实现)
        val ecKeyPair = Keys.createEcKeyPair()
        val publicKey = "T" + Keys.getAddress(ecKeyPair).substring(2) // 模拟TRX地址格式
        val privateKey = ecKeyPair.privateKey.toString(16)
        return Pair(publicKey, privateKey)
    }

    // 检查地址是否符合靓号规则
    fun isVanityAddress(address: String, prefix: String?, suffix: String?): Boolean {
        var matches = true
        
        if (!prefix.isNullOrEmpty()) {
            matches = matches && address.startsWith(prefix, ignoreCase = true)
        }
        
        if (!suffix.isNullOrEmpty()) {
            matches = matches && address.endsWith(suffix, ignoreCase = true)
        }
        
        return matches
    }
}

7. 主Activity (MainActivity.kt)

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.Button
import android.widget.RadioGroup
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.textfield.TextInputEditText
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity() {

    private lateinit var radioGroup: RadioGroup
    private lateinit var etPrefix: TextInputEditText
    private lateinit var etSuffix: TextInputEditText
    private lateinit var etCount: TextInputEditText
    private lateinit var btnGenerate: Button
    private lateinit var btnStop: Button
    private lateinit var tvStatus: TextView
    private lateinit var recyclerView: RecyclerView

    private lateinit var adapter: AddressAdapter
    private var isGenerating = false
    private var generatedCount = 0
    private var targetCount = 10
    private var totalTried = 0L

    private val handler = Handler(Looper.getMainLooper())
    private var generationJob: Job? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        initViews()
        setupRecyclerView()
        setupClickListeners()
    }

    private fun initViews() {
        radioGroup = findViewById(R.id.radioGroup)
        etPrefix = findViewById(R.id.etPrefix)
        etSuffix = findViewById(R.id.etSuffix)
        etCount = findViewById(R.id.etCount)
        btnGenerate = findViewById(R.id.btnGenerate)
        btnStop = findViewById(R.id.btnStop)
        tvStatus = findViewById(R.id.tvStatus)
        recyclerView = findViewById(R.id.recyclerView)
    }

    private fun setupRecyclerView() {
        adapter = AddressAdapter()
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = adapter
    }

    private fun setupClickListeners() {
        btnGenerate.setOnClickListener {
            startGeneration()
        }

        btnStop.setOnClickListener {
            stopGeneration()
        }
    }

    private fun startGeneration() {
        if (isGenerating) return

        val countText = etCount.text.toString()
        targetCount = if (countText.isNotEmpty()) countText.toInt() else 10
        if (targetCount <= 0) targetCount = 10

        generatedCount = 0
        totalTried = 0L
        adapter.clearAddresses()
        isGenerating = true
        updateStatus()

        generationJob = CoroutineScope(Dispatchers.Default).launch {
            generateAddresses()
        }
    }

    private fun stopGeneration() {
        isGenerating = false
        generationJob?.cancel()
        updateStatus()
    }

    private suspend fun generateAddresses() {
        val prefix = etPrefix.text?.toString()?.trim()
        val suffix = etSuffix.text?.toString()?.trim()
        val isEth = radioGroup.checkedRadioButtonId == R.id.radioEth

        while (isGenerating && generatedCount < targetCount) {
            totalTried++

            val (address, privateKey) = if (isEth) {
                WalletGenerator.generateEthWallet()
            } else {
                WalletGenerator.generateTrxWallet()
            }

            if (WalletGenerator.isVanityAddress(address, prefix, suffix)) {
                generatedCount++
                val addressInfo = AddressInfo(
                    index = generatedCount,
                    address = address,
                    privateKey = privateKey,
                    coinType = if (isEth) "ETH" else "TRX"
                )

                withContext(Dispatchers.Main) {
                    adapter.addAddress(addressInfo)
                    updateStatus()
                }

                // 短暂延迟,避免UI更新过快
                delay(10)
            }

            // 每1000次尝试更新一次状态
            if (totalTried % 1000 == 0L) {
                withContext(Dispatchers.Main) {
                    updateStatus()
                }
            }
        }

        withContext(Dispatchers.Main) {
            isGenerating = false
            updateStatus()
        }
    }

    private fun updateStatus() {
        val coinType = if (radioGroup.checkedRadioButtonId == R.id.radioEth) "ETH" else "TRX"
        val prefix = etPrefix.text?.toString()?.trim() ?: "无"
        val suffix = etSuffix.text?.toString()?.trim() ?: "无"

        val status = if (isGenerating) {
            "正在生成 $coinType 靓号...\n" +
            "规则: 前缀=$prefix, 后缀=$suffix\n" +
            "进度: $generatedCount/$targetCount\n" +
            "尝试次数: $totalTried"
        } else {
            if (generatedCount > 0) {
                "生成完成!找到 $generatedCount 个符合条件的 $coinType 地址\n" +
                "总尝试次数: $totalTried"
            } else {
                "准备就绪\n规则: 前缀=$prefix, 后缀=$suffix"
            }
        }

        tvStatus.text = status
    }

    override fun onDestroy() {
        super.onDestroy()
        stopGeneration()
    }
}

8. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.Material3.Light"
        tools:targetApi="31">
        
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

9. 颜色资源 (res/values/colors.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
</resources>

10. 主题资源 (res/values/themes.xml)

<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="Base.Theme.WalletAddressGenerator" parent="Theme.Material3.Light">
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
    </style>
    
    <style name="Theme.WalletAddressGenerator" parent="Base.Theme.WalletAddressGenerator" />
</resources>

使用说明:

  1. 功能特点

    • 支持ETH和TRX靓号生成
    • 可自定义地址前缀和后缀
    • 实时显示生成进度和统计信息
    • 显示生成的地址和对应私钥
    • 支持停止生成操作
  2. 使用方法

    • 选择币种(ETH或TRX)
    • 设置靓号规则(前缀、后缀)
    • 设置生成数量
    • 点击"开始生成"按钮
    • 查看生成结果
  3. 注意事项

    • 生成的私钥请妥善保管
    • 复杂的靓号规则可能需要较长的生成时间
    • 实际TRX地址生成可能需要更复杂的逻辑