I'm using the library bitcoinj to create a Android wallet. The case is that I want the blockchain to be downloaded in the background so that the user can start using the other features of the app, but when it starts the download and opens a new activity, the application freezes, you can't use anything, not even return to the previous screen. This is part of the code:
public class BitcoinService extends Service {
private final PeerDataEventListener blockchainDownloadListener = new AbstractPeerEventListener() {
private final long CALLBACK_TIME = 1000L;
private final AtomicLong lastMessageTime = new AtomicLong(0);
private int height;
private int blocksLeft;
private Block block;
@Override
public void onBlocksDownloaded(final Peer peer, final Block block, final FilteredBlock filteredBlock, final int blocksLeft) {
config.maybeIncrementBestChainHeightEver(blockChain.getChainHead().getHeight());
delayHandler.removeCallbacksAndMessages(null);
final long now = System.currentTimeMillis();
this.block = block;
this.height = (int) peer.getBestHeight() - blocksLeft;
this.blocksLeft = blocksLeft;
if (now - lastMessageTime.get() > CALLBACK_TIME) {
delayHandler.post(RUNNER);
} else {
delayHandler.postDelayed(RUNNER, CALLBACK_TIME);
}
}
private final Runnable RUNNER = new Runnable() {
@Override
public void run() {
notifyBlockchainProgress(height, (height + blocksLeft));
Log.e(TAG, "LAST_BLOCK=" + height + ", REMAINS=" + blocksLeft);
if (blocksLeft == 0) {
broadcastBlockchainDownloaded();
}
}
};
};
private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {
@SuppressLint("Wakelock")
@Override
public void onReceive(final Context context, final Intent intent) {
Log.d(TAG, "acquiring wakelock");
wakeLock.acquire();
// consistency check
final int walletLastBlockSeenHeight = wallet.getLastBlockSeenHeight();
final int bestChainHeight = blockChain.getBestChainHeight();
if (walletLastBlockSeenHeight != -1 && walletLastBlockSeenHeight != bestChainHeight) {
final String message = "wallet/blockchain out of sync: " + walletLastBlockSeenHeight + "/" + bestChainHeight;
Log.e(TAG, message);
}
Log.i(TAG, "starting peergroup");
peerGroup = new PeerGroup(Constants.WALLET.NETWORK_PARAMETERS, blockChain);
peerGroup.setDownloadTxDependencies(false);
peerGroup.setUserAgent("TestWallet", "0.0.1");
peerGroup.setMaxConnections(6);
peerGroup.setConnectTimeoutMillis(15000);
peerGroup.setPeerDiscoveryTimeoutMillis(10000);
// start peergroup
peerGroup.startAsync();
peerGroup.startBlockChainDownload(blockchainDownloadListener);
}
};
private final Handler delayHandler = new Handler();
private PeerGroup peerGroup;
private WakeLock wakeLock;
private PeerConnectivityListener peerConnectivityListener;
private NotificationManager nm;
private Configuration config;
private Wallet wallet;
@Override
public void onCreate() {
wallet = new Wallet(Constants.WALLET.NETWORK_PARAMETERS);
nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
final String lockName = getPackageName() + " blockchain sync";
final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName);
config = Configuration.getInstance();
broadcastPeerState(0);
blockChainFile = Constants.WALLET.BLOCKCHAIN_FILE;
final boolean blockChainFileExists = blockChainFile.exists();
if (!blockChainFileExists) {
new File(Constants.WALLET.WALLET_PATH).mkdirs();
}
try {
blockStore = new SPVBlockStore(Constants.WALLET.NETWORK_PARAMETERS, blockChainFile);
blockStore.getChainHead(); // detect corruptions as early as possible
final long earliestKeyCreationTime = wallet.getEarliestKeyCreationTime();
if (!blockChainFileExists && earliestKeyCreationTime > 0){
try {
config.setDeletingBlockchain(true);
final long start = System.currentTimeMillis();
final InputStream checkpointsInputStream = getAssets().open("bitcoin/" + Constants.WALLET.CHECKPOINTS_FILENAME);
CheckpointManager.checkpoint(Constants.WALLET.NETWORK_PARAMETERS, checkpointsInputStream, blockStore, earliestKeyCreationTime);
Log.i(TAG, String.format("checkpoints loaded from '%1$s', took %2$dms", Constants.WALLET.CHECKPOINTS_FILENAME, System.currentTimeMillis() - start));
} catch (final IOException x) {
Log.e(TAG, "problem reading checkpoints, continuing without", x);
}
}
} catch (final BlockStoreException x) {
blockChainFile.delete();
final String msg = "blockstore cannot be created";
Log.e(TAG, msg, x);
}
try {
blockChain = new BlockChain(Constants.WALLET.NETWORK_PARAMETERS, walletHelper.getMainWallet(), blockStore);
} catch (final BlockStoreException x) {
throw new Error("blockchain cannot be created", x);
}
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
registerReceiver(connectivityReceiver, intentFilter); // implicitly start PeerGroup
}
@Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
Log.e(TAG, "onStartCommand");
...
return START_NOT_STICKY;
}
private void notifyBlockchainProgress(int progress, int max) {
boolean isCompleted = progress == max;
if (config.isDeletingBlockchain()) {
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
long percent = Math.round(Math.floor((progress * 100) / max));
mBuilder.setContentTitle("Blockchain download")
.setContentText(" Synchronization in progress - " + percent + "%")
.setSmallIcon(R.drawable.ic_logo_splash)
.setProgress(max, progress, false)
.setAutoCancel(false);
if (isCompleted) {
mBuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setProgress(0, 0, false)
.setAutoCancel(true)
.setContentText(getString(R.string.synchronization_completed));
config.setDeletingBlockchain(false);
}
Notification notif = mBuilder.build();
if (isCompleted) {
notif.flags = Notification.FLAG_FOREGROUND_SERVICE;
} else {
notif.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE;
}
nm.notify(2, notif);
}
}
public void broadcastBlockchainDownloaded() {
Intent i = new Intent(UiRefreshReceiver.REFRESH_UI);
LocalBroadcastManager.getInstance(this).sendBroadcast(i);
}
}
I used part of the code of the official project of the Android wallet
Service in android runs in UI thread. You need to put all the job into different thread inside your Service