Installing APKs on Multiple Android Devices with ADB
A small JavaFX and Ant tool for installing selected APK files across connected Android devices
20-12-2015
This archived utility combines a JavaFX file picker with an Ant task that invokes adb once per connected device. The task submits installs to a thread pool, so a collection of emulators or test phones can receive the same APK at roughly the same time.
The code reflects an older Android tooling stack and is not ready to run unchanged. Device-list parsing is fragile, the zero-device case creates a thread pool with no workers, and cancellation in the file chooser is not handled. The Ant task is also credited to Daniel Dyer in its source; that attribution should remain with the code.
importjava.io.File;importjava.io.IOException;importjava.util.List;importjavafx.application.Application;importjavafx.collections.FXCollections;importjavafx.collections.ObservableList;importjavafx.event.ActionEvent;importjavafx.geometry.Insets;importjavafx.scene.Scene;importjavafx.scene.control.Button;importjavafx.scene.control.ListView;importjavafx.scene.layout.HBox;importjavafx.scene.layout.VBox;importjavafx.stage.FileChooser;importjavafx.stage.Stage;importorg.apache.tools.ant.BuildException;/**
* The main graphical user interface
* @author Tommaso Gragnato
*/publicclassInstallerextendsApplication{@Overridepublicvoidstart(StageprimaryStage)throwsIOException{InstallAPKinstall=newInstallAPK();// NodesListView<File>table=newListView<>();ButtonselectADB=newButton();ButtonopenFileButton=newButton();Buttonbtn=newButton();ListView<String>logger=newListView<>();// Select the ADB executableselectADB.setText("Select ADB executable");selectADB.setOnAction((ActionEventevent)->{FileChooserfileChooser=newFileChooser();install.setADB(fileChooser.showOpenDialog(primaryStage));});// Select APK files for the installation listopenFileButton.setText("Select APKs");openFileButton.setOnAction((ActionEventevent)->{FileChooserfileChooser=newFileChooser();List<File>list=fileChooser.showOpenMultipleDialog(primaryStage);ObservableList<File>filtered=FXCollections.observableArrayList();for(Filefile:list){if(file.isFile()&&file.getPath().toLowerCase().endsWith(".apk")){filtered.add(file);}}table.setItems(filtered);});// Install APKsbtn.setText("Install");btn.setOnAction((ActionEventevent)->{for(Filefile:table.getItems()){install.setAPK(file);try{install.execute();ObservableList<String>log=logger.getItems();log.add(file.getPath()+" installed on every device(s)...");logger.setItems(log);}catch(BuildExceptionex){ObservableList<String>log=logger.getItems();log.add(ex.toString());logger.setItems(log);}}});// LayoutsHBoxroot=newHBox();VBoxleftColumn=newVBox();root.getChildren().addAll(table,leftColumn);leftColumn.getChildren().addAll(selectADB,openFileButton,btn,logger);// Size and alignmentleftColumn.setPadding(newInsets(20,20,20,20));table.setMinWidth(500);selectADB.setMinWidth(460);openFileButton.setMinWidth(460);btn.setMinWidth(460);logger.setMinWidth(460);Scenescene=newScene(root,1000,500);primaryStage.setTitle("myAPKs");primaryStage.setScene(scene);primaryStage.setResizable(false);primaryStage.show();}/**
* @param args the command line arguments
*/publicstaticvoidmain(String[]args){launch(args);}}
importjava.io.BufferedReader;importjava.io.File;importjava.io.IOException;importjava.io.InputStream;importjava.io.InputStreamReader;importjava.io.PrintStream;importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.Callable;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Future;importjava.util.concurrent.TimeUnit;importorg.apache.tools.ant.BuildException;importorg.apache.tools.ant.Task;/**
* Custom task that uses the ADB tool to install a specified APK on all
* connected devices and emulators.
* @author Daniel Dyer
*/publicclassInstallAPKextendsTask{privateFileapkFile;publicvoidsetAPK(FileapkFile){this.apkFile=apkFile;}@Overridepublicvoidexecute()throwsBuildException{if(apkFile==null){thrownewBuildException("APK file must be specified");}try{List<String>devices=getDeviceIdentifiers();System.out.printf("Installing %s on %d device(s)...%n",apkFile,devices.size());ExecutorServiceexecutor=Executors.newFixedThreadPool(devices.size());List<Future<Void>>futures=newArrayList<Future<Void>>(devices.size());for(finalStringdevice:devices){futures.add(executor.submit(newCallable<Void>(){publicVoidcall()throwsIOException,InterruptedException{installOnDevice(device);returnnull;}}));}for(Future<Void>future:futures){future.get();}executor.shutdown();executor.awaitTermination(60,TimeUnit.SECONDS);}catch(Exceptionex){thrownewBuildException(ex);}}privatevoidinstallOnDevice(Stringdevice)throwsIOException,InterruptedException{String[]command=newString[]{"adb","-s",device,"install","-r",apkFile.toString()};Processprocess=Runtime.getRuntime().exec(command);consumeStream(process.getInputStream(),System.out,device);if(process.waitFor()!=0){consumeStream(process.getErrorStream(),System.err,device);thrownewBuildException(String.format("Installing APK on %s failed.",device));}}privatevoidconsumeStream(InputStreamin,PrintStreamout,Stringtag)throwsIOException{BufferedReaderreader=newBufferedReader(newInputStreamReader(in));try{for(Stringline=reader.readLine();line!=null;line=reader.readLine()){out.println(tag!=null?String.format("[%s] %s",tag,line.trim()):line);}}finally{reader.close();}}privateList<String>getDeviceIdentifiers()throwsIOException,InterruptedException{Processprocess=Runtime.getRuntime().exec("adb devices");Listdevices=newArrayList<String>(10);BufferedReaderreader=newBufferedReader(newInputStreamReader(process.getInputStream()));try{for(Stringline=reader.readLine();line!=null;line=reader.readLine()){if(line.endsWith("device")){devices.add(line.split("s")[0]);}}if(process.waitFor()!=0){consumeStream(process.getErrorStream(),System.err,null);thrownewBuildException("Failed getting list of connected devices/emulators.");}}finally{reader.close();}returndevices;}}