最后
针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!
往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
@NoArgsConstructor
@Data
public class Block {
/**
- 区块hash值
*/
private String hash;
/**
- 前一个区块的hash值
*/
private String prevBlockHash;
/**
- 区块交易
*/
private List transactions;
/**
- 时间戳,单位秒
*/
private long timeStamp;
/**
- 区块的高度
*/
private long height;
/**
- 工作量证明计数器
*/
private long nonce;
/**
-
创建新的区块
-
@param previousHash
-
@param transactions
-
@return
*/
public static Block newBlock(String previousHash, List transactions, long height) {
Block block = new Block(“”, previousHash, transactions, Instant.now().getEpochSecond(),height,0);
ProofOfWork pow = ProofOfWork.newProofOfWork(block);
PowResult powResult = pow.run();
block.setHash(powResult.getHash());
block.setNonce(powResult.getNonce());
// block.setHash();
return block;
}
private static final String ZERO_HASH = Hex.encodeHexString(new byte[32]);
/**
-
创建创世区块
-
@param coinbase
-
@return
*/
public static Block newGenesisBlock(Transaction coinbase) {
List transactions = new ArrayList<>();
transactions.add(coinbase);
return Block.newBlock(ByteUtils.ZERO_HASH, transactions,0);
}
/**
-
对区块中的交易信息进行Hash计算
-
@return
*/
public byte[] hashTransaction() {
byte[][] txIdArrays = new byte[this.getTransactions().size()][];
for (int i = 0; i < this.getTransactions().size(); i++) {
txIdArrays[i] = this.getTransactions().get(i).getTxId();
}
return DigestUtils.sha256(ByteUtils.merge(txIdArrays));
}
}
创建ByteUtils.java
该类的作用是将数据转换承byte[]字节数据
package com.example.blockchain;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.ArrayUtils;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.stream.Stream;
/**
-
字节数组工具类
*/
public class ByteUtils {
public static final String ZERO_HASH = Hex.encodeHexString(new byte[32]);
/**
-
将多个字节数组合并成一个字节数组
-
@param bytes
-
@return
*/
public static byte[] merge(byte[]… bytes) {
Stream stream = Stream.of();
for (byte[] b : bytes) {
stream = Stream.concat(stream, Arrays.stream(ArrayUtils.toObject(b)));
}
return ArrayUtils.toPrimitive(stream.toArray(Byte[]::new));
}
/**
-
long 转化为 byte[]
-
@param val
-
@return
*/
public static byte[] toBytes(long val) {
return ByteBuffer.allocate(Long.BYTES).putLong(val).array();
}
}
创建Blockchain.java
其中的方法有创建区块链,添加区块Block,区块链遍历,打包交易信息创建区块添加到区块链等
package com.example.blockchain;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
-
区块链
*/
@Data
@AllArgsConstructor
public class Blockchain {
/**
- 最后一个区块的hash
*/
private String lastBlockHash;
/**
-
创建区块链,createBlockchain
-
@param address
-
@return
*/
public static Blockchain createBlockchain(String address) {
String lastBlockHash = “”;
if (StringUtils.isBlank(lastBlockHash)){
//对应的bucket不存在,说明是第一次获取区块链实例
// 创建 coinBase 交易
Transaction coinbaseTX = Transaction.newCoinbaseTX(address, “”);
Block genesisBlock = Block.newGenesisBlock(coinbaseTX);
lastBlockHash = genesisBlock.getHash();
RocksDBUtils.getInstance().putBlock(genesisBlock);
RocksDBUtils.getInstance().putLastBlockHash(lastBlockHash);
}
return new Blockchain(lastBlockHash);
}
/**
-
根据block,添加区块
-
@param block
*/
public void addBlock(Block block) {
RocksDBUtils.getInstance().putLastBlockHash(block.getHash());
RocksDBUtils.getInstance().putBlock(block);
this.lastBlockHash = block.getHash();
}
/**
-
根据data添加区块
-
@param data
*/
// public void addBlock(String data) throws Exception{
//
// String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash();
// if (StringUtils.isBlank(lastBlockHash)){
// throw new Exception(“还没有数据库,无法直接添加区块。。”);
// }
// this.addBlock(Block.newBlock(lastBlockHash,data));
// }
/**
- 区块链迭代器:内部类
*/
public class BlockchainIterator{
/**
- 当前区块的hash
*/
private String currentBlockHash;
/**
-
构造函数
-
@param currentBlockHash
*/
public BlockchainIterator(String currentBlockHash) {
this.currentBlockHash = currentBlockHash;
}
/**
-
判断是否有下一个区块
-
@return
*/
public boolean hashNext() {
if (ByteUtils.ZERO_HASH.equals(currentBlockHash)) {
return false;
}
Block lastBlock = RocksDBUtils.getInstance().getBlock(currentBlockHash);
if (lastBlock == null) {
return false;
}
// 如果是创世区块
if (ByteUtils.ZERO_HASH.equals(lastBlock.getPrevBlockHash())) {
return true;
}
return RocksDBUtils.getInstance().getBlock(lastBlock.getPrevBlockHash()) != null;
}
/**
-
迭代获取区块
-
@return
*/
public Block next() {
Block currentBlock = RocksDBUtils.getInstance().getBlock(currentBlockHash);
if (currentBlock != null) {
this.currentBlockHash = currentBlock.getPrevBlockHash();
return currentBlock;
}
return null;
}
}
/**
-
添加方法,用于获取迭代器实例
-
@return
*/
public BlockchainIterator getBlockchainIterator() {
return new BlockchainIterator(lastBlockHash);
}
/**
-
打包交易,进行挖矿
-
@param transactions
*/
public void mineBlock(List transactions) throws Exception {
String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash();
Block lastBlock = RocksDBUtils.getInstance().getBlock(lastBlockHash);
if (lastBlockHash == null) {
throw new Exception("ERROR: Fail to get last block hash ! ");
}
Block block = Block.newBlock(lastBlockHash, transactions,lastBlock.getHeight()+1);
this.addBlock(block);
}
/**
-
从交易输入中查询区块链中所有已被花费了的交易输出
-
@param address 钱包地址
-
@return 交易ID以及对应的交易输出下标地址
-
@throws Exception
*/
private Map<String, int[]> getAllSpentTXOs(String address) {
// 定义TxId ——> spentOutIndex[],存储交易ID与已被花费的交易输出数组索引值
Map<String, int[]> spentTXOs = new HashMap<>();
for (BlockchainIterator blockchainIterator = this.getBlockchainIterator(); blockchainIterator.hashNext(); ) {
Block block = blockchainIterator.next();
for (Transaction transaction : block.getTransactions()) {
// 如果是 coinbase 交易,直接跳过,因为它不存在引用前一个区块的交易输出
if (transaction.isCoinbase()) {
continue;
}
for (TXInput txInput : transaction.getInputs()) {
if (txInput.canUnlockOutputWith(address)) {
String inTxId = Hex.encodeHexString(txInput.getTxId());
int[] spentOutIndexArray = spentTXOs.get(inTxId);
if (spentOutIndexArray == null) {
spentTXOs.put(inTxId, new int[]{txInput.getTxOutputIndex()});
} else {
spentOutIndexArray = ArrayUtils.add(spentOutIndexArray, txInput.getTxOutputIndex());
spentTXOs.put(inTxId, spentOutIndexArray);
}
}
}
}
}
return spentTXOs;
}
/**
-
查找钱包地址对应的所有未花费的交易
-
@param address 钱包地址
-
@return
*/
private Transaction[] findUnspentTransactions(String address) throws Exception {
Map<String, int[]> allSpentTXOs = this.getAllSpentTXOs(address);
Transaction[] unspentTxs = {};
// 再次遍历所有区块中的交易输出
for (BlockchainIterator blockchainIterator = this.getBlockchainIterator(); blockchainIterator.hashNext(); ) {
Block block = blockchainIterator.next();
for (Transaction transaction : block.getTransactions()) {
String txId = Hex.encodeHexString(transaction.getTxId());
int[] spentOutIndexArray = allSpentTXOs.get(txId);
for (int outIndex = 0; outIndex < transaction.getOutputs().length; outIndex++) {
if (spentOutIndexArray != null && ArrayUtils.contains(spentOutIndexArray, outIndex)) {
continue;
}
// 保存不存在 allSpentTXOs 中的交易
if (transaction.getOutputs()[outIndex].canBeUnlockedWith(address)) {
unspentTxs = ArrayUtils.add(unspentTxs, transaction);
}
}
}
}
return unspentTxs;
}
/**
-
查找钱包地址对应的所有UTXO
-
@param address 钱包地址
-
@return
*/
public TXOutput[] findUTXO(String address) throws Exception {
Transaction[] unspentTxs = this.findUnspentTransactions(address);
TXOutput[] utxos = {};
if (unspentTxs == null || unspentTxs.length == 0) {
return utxos;
}
for (Transaction tx : unspentTxs) {
for (TXOutput txOutput : tx.getOutputs()) {
if (txOutput.canBeUnlockedWith(address)) {
utxos = ArrayUtils.add(utxos, txOutput);
}
}
}
return utxos;
}
/**
-
寻找能够花费的交易
-
@param address 钱包地址
-
@param amount 花费金额
*/
public SpendableOutputResult findSpendableOutputs(String address, int amount) throws Exception {
Transaction[] unspentTXs = this.findUnspentTransactions(address);
int accumulated = 0;
Map<String, int[]> unspentOuts = new HashMap<>();
for (Transaction tx : unspentTXs) {
String txId = Hex.encodeHexString(tx.getTxId());
for (int outId = 0; outId < tx.getOutputs().length; outId++) {
TXOutput txOutput = tx.getOutputs()[outId];
if (txOutput.canBeUnlockedWith(address) && accumulated < amount) {
accumulated += txOutput.getValue();
int[] outIds = unspentOuts.get(txId);
if (outIds == null) {
outIds = new int[]{outId};
} else {
outIds = ArrayUtils.add(outIds, outId);
}
unspentOuts.put(txId, outIds);
if (accumulated >= amount) {
break;
}
}
}
}
return new SpendableOutputResult(accumulated, unspentOuts);
}
/**
-
从 DB 从恢复区块链数据
-
@return
-
@throws Exception
*/
public static Blockchain initBlockchainFromDB() throws Exception {
String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash();
if (lastBlockHash == null) {
throw new Exception("ERROR: Fail to init blockchain from db. ");
}
return new Blockchain(lastBlockHash);
}
}
创建ProofOfWork.java
(工作量证明)
工作量证明是经过困难的工作,将数据放入区块链中,这样别人就不太可能取修改区块链中的区块,想要修改其中一个块,必须经过大量计算,还要计算这个块之后的块
这里将计算难度设置为16,就是根据nonce计算出来的hash前16位必须为0,这样才能比目标值小,这样添加的区块才能被认可
package com.example.blockchain;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import java.math.BigInteger;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
-
工作量证明
-
@author hanru
*/
@Data
@AllArgsConstructor
public class ProofOfWork {
/**
-
难度目标位
-
0000 0000 0000 0000 1001 0001 0000 … 0001
-
256位Hash里面前面至少有16个零
*/
public static final int TARGET_BITS = 16;
/**
- 要验证的区块
*/
private Block block;
/**
- 难度目标值
*/
private BigInteger target;
/**
-
创建新的工作量证明对象
-
对1进行移位运算,将1向左移动 (256 - TARGET_BITS) 位,得到我们的难度目标值
-
@param block
-
@return
*/
public static ProofOfWork newProofOfWork(Block block) {
/*
1.创建一个BigInteger的数值1.
0000000…00001
2.左移256-bits位
以8 bit为例
0000 0001
0010 0000
8-6
*/
BigInteger targetValue = BigInteger.ONE.shiftLeft((256 - TARGET_BITS));
return new ProofOfWork(block, targetValue);
}
/**
-
运行工作量证明,开始挖矿,找到小于难度目标值的Hash
-
@return
*/
public PowResult run() {
long nonce = 0;
String shaHex = “”;
// System.out.printf(“开始进行挖矿:%s \n”, this.getBlock().getData());
System.out.printf(“开始进行挖矿: \n”);
long startTime = System.currentTimeMillis();
while (nonce < Long.MAX_VALUE) {
byte[] data = this.prepareData(nonce);
shaHex = DigestUtils.sha256Hex(data);
System.out.printf(“\r%d: %s”,nonce,shaHex);
if (new BigInteger(shaHex, 16).compareTo(this.target) == -1) {
System.out.println();
System.out.printf(“耗时 Time: %s seconds \n”, (float) (System.currentTimeMillis() - startTime) / 1000);
System.out.printf(“当前区块Hash: %s \n\n”, shaHex);
break;
} else {
nonce++;
}
}
return new PowResult(nonce, shaHex);
}
/**
-
根据block的数据,以及nonce,生成一个byte数组
-
注意:在准备区块数据时,一定要从原始数据类型转化为byte[],不能直接从字符串进行转换
-
@param nonce
-
@return
*/
private byte[] prepareData(long nonce) {
byte[] prevBlockHashBytes = {};
if (StringUtils.isNoneBlank(this.getBlock().getPrevBlockHash())) {
prevBlockHashBytes = new BigInteger(this.getBlock().getPrevBlockHash(), 16).toByteArray();
}
return ByteUtils.merge(
prevBlockHashBytes,
// this.getBlock().getData().getBytes(),
this.getBlock().hashTransaction(),
ByteUtils.toBytes(this.getBlock().getTimeStamp()),
ByteUtils.toBytes(TARGET_BITS),
ByteUtils.toBytes(nonce)
);
}
/**
-
验证区块是否有效
-
@return
*/
public boolean validate() {
byte[] data = this.prepareData(this.getBlock().getNonce());
return new BigInteger(DigestUtils.sha256Hex(data), 16).compareTo(this.target) == -1;
}
}
创建TXInput.java(交易中的输入)
package com.example.blockchain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class TXInput {
/**
- txId是前一次交易的ID
*/
private byte[] txId;
/**
- 交易输出索引
*/
private int txOutputIndex;
/**
- 解锁脚本
*/
private String scriptSig;
/**
-
判断解锁数据是否能够解锁交易输出
-
@param unlockingData
-
@return
*/
public boolean canUnlockOutputWith(String unlockingData) {
return this.getScriptSig().endsWith(unlockingData);
}
}
创建TXOutput.java(交易中的输出)
package com.example.blockchain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
- @author hanru
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TXOutput {
/**
- 数值金额
*/
private int value;
/**
- 锁定脚本
*/
private String scriptPubKey;
/**
-
判断解锁数据是否能够解锁交易输出
-
@param unlockingData
-
@return
*/
public boolean canBeUnlockedWith(String unlockingData) {
return this.getScriptPubKey().endsWith(unlockingData);
}
}
创建 Transaction.java
package com.example.blockchain;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Iterator;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Transaction {
private static final int SUBSIDY = 10;
/**
- 交易的Hash
*/
private byte[] txId;
/**
- 交易输入
*/
private TXInput[] inputs;
/**
- 交易输出
*/
private TXOutput[] outputs;
/**
- 设置交易ID
*/
总结
现在新技术层出不穷,如果每次出新的技术,我们都深入的研究的话,很容易分散精力。新的技术可能很久之后我们才会在工作中用得上,当学的新技术无法学以致用,很容易被我们遗忘,到最后真的需要使用的时候,又要从头来过(虽然上手会更快)。
我觉得身为技术人,针对新技术应该是持拥抱态度的,入了这一行你就应该知道这是一个活到老学到老的行业,所以面对新技术,不要抵触,拥抱变化就好了。
Flutter 明显是一种全新的技术,而对于这个新技术在发布之初,花一个月的时间学习它,成本确实过高。但是周末花一天时间体验一下它的开发流程,了解一下它的优缺点、能干什么或者不能干什么。这个时间,并不是我们不能接受的。
如果有时间,其实通读一遍 Flutter 的文档,是最全面的一次对 Flutter 的了解过程。但是如果我们只有 8 小时的时间,我希望能关注一些最值得关注的点。
(跨平台开发(Flutter)、java基础与原理,自定义view、NDK、架构设计、性能优化、完整商业项目开发等)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Iterator;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Transaction {
private static final int SUBSIDY = 10;
/**
- 交易的Hash
*/
private byte[] txId;
/**
- 交易输入
*/
private TXInput[] inputs;
/**
- 交易输出
*/
private TXOutput[] outputs;
/**
- 设置交易ID
*/
总结
现在新技术层出不穷,如果每次出新的技术,我们都深入的研究的话,很容易分散精力。新的技术可能很久之后我们才会在工作中用得上,当学的新技术无法学以致用,很容易被我们遗忘,到最后真的需要使用的时候,又要从头来过(虽然上手会更快)。
我觉得身为技术人,针对新技术应该是持拥抱态度的,入了这一行你就应该知道这是一个活到老学到老的行业,所以面对新技术,不要抵触,拥抱变化就好了。
Flutter 明显是一种全新的技术,而对于这个新技术在发布之初,花一个月的时间学习它,成本确实过高。但是周末花一天时间体验一下它的开发流程,了解一下它的优缺点、能干什么或者不能干什么。这个时间,并不是我们不能接受的。
如果有时间,其实通读一遍 Flutter 的文档,是最全面的一次对 Flutter 的了解过程。但是如果我们只有 8 小时的时间,我希望能关注一些最值得关注的点。
(跨平台开发(Flutter)、java基础与原理,自定义view、NDK、架构设计、性能优化、完整商业项目开发等)
[外链图片转存中…(img-0WV7680A-1715377676308)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!