Trying to port Apache POI to Android
Well I was able to do most of what I was asking for here. That is importing the jar files. I had at least 2 kinds of problems:
- not enough RAM on Eclipse which made dexing my classes crash most of the time (fixed by adjusting the Xmx and xms values in Eclipse.ini)
- the 64k method limit for each DEX file made things complicated. I had to split all the required POI jars into several DEX files. (I did that by following the tutorial from the Android blog: http://android-developers.blogspot.com/2011/07/custom-class-loading-in-dalvik.html )
The real answer to my question is: "yes you need everything in the jar". I made it work for the basic "non open xml" files. My app does the conversion to html quite well, and it's fast enough too.
On a side note, I was also trying to do the same thing with "open XML" files, and it's much more complicated. My little project doesn't do what it's supposed to do, I've got some weird exception when initializing the XMLBeans class. Here's my trace (sorry for the ugliness):
12-19 12:07:10.790: W/dalvikvm(13385): Exception
Ljava/lang/RuntimeException; thrown while initializing
Lorg/apache/xmlbeans/impl/regex/SchemaRegularExpression;
12-19 12:07:10.790: W/dalvikvm(13385): Exception
Ljava/lang/ExceptionInInitializerError; thrown while initializing
Lorg/apache/xmlbeans/impl/schema/BuiltinSchemaTypeSystem;
12-19 12:07:10.790: D/dalvikvm(13385): Method.invoke() on bad class
Lorg/apache/xmlbeans/impl/schema/BuiltinSchemaTypeSystem; failed
12-19 12:07:10.790: W/dalvikvm(13385): Exception
Ljava/lang/ExceptionInInitializerError; thrown while initializing
Lorg/apache/xmlbeans/XmlBeans;
12-19 12:07:10.790: W/System.err(13385):
java.lang.reflect.InvocationTargetException
12-19 12:07:10.790: W/System.err(13385): at
java.lang.reflect.Method.invokeNative(Native Method)
12-19 12:07:10.790: W/System.err(13385): at
java.lang.reflect.Method.invoke(Method.java:491)
12-19 12:07:10.790: W/System.err(13385): at
t.fze.TestOfficeAndroidActivity.onCreate(TestOfficeAndroidActivity.java:55)
12-19 12:07:10.790: W/System.err(13385): at
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1048)
12-19 12:07:10.790: W/System.err(13385): at
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1712)
12-19 12:07:10.790: W/System.err(13385): at
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1764)
12-19 12:07:10.790: W/System.err(13385): at
android.app.ActivityThread.access$1500(ActivityThread.java:122)
12-19 12:07:10.790: W/System.err(13385): at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1002)
12-19 12:07:10.790: W/System.err(13385): at
android.os.Handler.dispatchMessage(Handler.java:99)
12-19 12:07:10.790: W/System.err(13385): at
android.os.Looper.loop(Looper.java:132)
12-19 12:07:10.790: W/System.err(13385): at
android.app.ActivityThread.main(ActivityThread.java:4025)
12-19 12:07:10.790: W/System.err(13385): at
java.lang.reflect.Method.invokeNative(Native Method)
12-19 12:07:10.790: W/System.err(13385): at
java.lang.reflect.Method.invoke(Method.java:491)
12-19 12:07:10.790: W/System.err(13385): at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
12-19 12:07:10.790: W/System.err(13385): at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
12-19 12:07:10.790: W/System.err(13385): at
dalvik.system.NativeStart.main(Native Method)
12-19 12:07:10.790: W/System.err(13385): Caused by:
org.apache.poi.POIXMLException:
java.lang.reflect.InvocationTargetException
12-19 12:07:10.790: W/System.err(13385): at
org.apache.poi.xssf.usermodel.XSSFFactory.createDocumentPart(XSSFFactory.java:62)
12-19 12:07:10.790: W/System.err(13385): at
org.apache.poi.POIXMLDocumentPart.read(POIXMLDocumentPart.java:414)
12-19 12:07:10.790: W/System.err(13385): at
org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:155)
12-19 12:07:10.790: W/System.err(13385): at
org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:174)
12-19 12:07:10.790: W/System.err(13385): at
org.apache.poi.ss.usermodel.WorkbookFactory.create(WorkbookFactory.java:63)
12-19 12:07:10.790: W/System.err(13385): at
org.apache.poi.ss.examples.html.ToHtml.create(ToHtml.java:139)
12-19 12:07:10.790: W/System.err(13385): at
org.apache.poi.ss.examples.html.ToHtml.create(ToHtml.java:123)
12-19 12:07:10.790: W/System.err(13385): ... 16 more
12-19 12:07:10.790: W/System.err(13385): Caused by:
java.lang.reflect.InvocationTargetException
12-19 12:07:10.790: W/System.err(13385): at
java.lang.reflect.Constructor.constructNative(Native Method)
12-19 12:07:10.790: W/System.err(13385): at
java.lang.reflect.Constructor.newInstance(Constructor.java:416)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.poi.xssf.usermodel.XSSFFactory.createDocumentPart(XSSFFactory.java:60)
12-19 12:07:10.800: W/System.err(13385): ... 22 more
12-19 12:07:10.800: W/System.err(13385): Caused by:
java.lang.ExceptionInInitializerError
12-19 12:07:10.800: W/System.err(13385): at
org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument$Factory.parse(ThemeDocument.java:71)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.poi.xssf.model.ThemesTable.<init>(ThemesTable.java:38)
12-19 12:07:10.800: W/System.err(13385): ... 25 more
12-19 12:07:10.800: W/System.err(13385): Caused by:
java.lang.ExceptionInInitializerError
12-19 12:07:10.800: W/System.err(13385): at
java.lang.reflect.Method.invokeNative(Native Method)
12-19 12:07:10.800: W/System.err(13385): at
java.lang.reflect.Method.invoke(Method.java:491)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.XmlBeans.getNoType(XmlBeans.java:856)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.XmlBeans.<clinit>(XmlBeans.java:881)
12-19 12:07:10.800: W/System.err(13385): ... 27 more
12-19 12:07:10.800: W/System.err(13385): Caused by:
java.lang.ExceptionInInitializerError
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.schema.BuiltinSchemaTypeSystem.fillInType(BuiltinSchemaTypeSystem.java:1025)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.schema.BuiltinSchemaTypeSystem.<clinit>(BuiltinSchemaTypeSystem.java:223)
12-19 12:07:10.800: W/System.err(13385): ... 31 more
12-19 12:07:10.800: W/System.err(13385): Caused by:
java.lang.RuntimeException: Installation Problem??? Couldn't load
messages: Can't find resource for bundle
'org.apache.xmlbeans.impl.regex.message_fr_FR', key ''
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.RegexParser.setLocale(RegexParser.java:88)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.RegexParser.<init>(RegexParser.java:78)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.ParserForXMLSchema.<init>(ParserForXMLSchema.java:28)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.RegularExpression.setPattern(RegularExpression.java:2996)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.RegularExpression.setPattern(RegularExpression.java:3009)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.RegularExpression.<init>(RegularExpression.java:2975)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.SchemaRegularExpression.<init>(SchemaRegularExpression.java:27)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.SchemaRegularExpression.<init>(SchemaRegularExpression.java:23)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.SchemaRegularExpression$1.<init>(SchemaRegularExpression.java:44)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.SchemaRegularExpression.buildKnownPatternMap(SchemaRegularExpression.java:43)
12-19 12:07:10.800: W/System.err(13385): at
org.apache.xmlbeans.impl.regex.SchemaRegularExpression.<clinit>(SchemaRegularExpression.java:38)
12-19 12:07:10.800: W/System.err(13385): ... 33 more
Does Android support Apache POI API
We can certainly port Apache POI API in Android. The example code that i made is to count the number of slides in a power point presentation with the help of Apache POI.
Read Excel in Android
For those application who need to work with full functional excel files (i.e. drawing, VBA etc... ), you should go with Apache POI, it's plain but still the best solution for that now.
However, if you just need to read the Excel, it maybe good to go with JavaScript solution. With js-xlsx library, you can transfer Excel files into JSON. And the library size is small, just 395KB (only include xlsx.core.min.js)
I believe this not the best solution:
- WebView
need to work with UI Thread
, it may block the UI when reading large Excel file.
- Performance issue
But you could change this to other JavaScript engine like Rhino
or V8
to fix these issues.
Here is the code
Interface for callback:
public interface ExcelReaderListener {
void onReadExcelCompleted(List<String> stringList);
}
MainActivity:
private ProgressDialog progressDialog;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new AlertDialog.Builder(MainActivity.this)
.setMessage("message")
.setTitle("title")
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
new FileChooser(MainActivity.this, new String[]{"xls", "xlsx"})
.setFileListener(new FileChooser.FileSelectedListener() {
@Override
public void fileSelected(File file) {
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setTitle("title");
progressDialog.setMessage("message");
progressDialog.setIndeterminate(true);
progressDialog.setCanceledOnTouchOutside(false);
Toast.makeText(MainActivity.this, file.getName(), Toast.LENGTH_SHORT).show();
String filePath = file.getAbsolutePath();
ExcelReaderListener excelReaderListener = MainActivity.this;
progressDialog.show();
try {
final WebView webView = new WebView(MainActivity.this);
new JSExcelReader(filePath, webView, excelReaderListener);
} catch (Exception ex) {
Log.e("Import excel error", ex.getMessage());
}
}
})
.showDialog();
}
})
.show();
}
@Override
public void onReadExcelCompleted(List<String> stringList) {
Toast.makeText(MainActivity.this, "Parse Completed", Toast.LENGTH_SHORT).show();
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
// Write into DB
...
}
Interface for user to select excel file:
https://rogerkeays.com/simple-android-file-chooser
JSExcelReader: (Core part to read excel and transform it to ArrayList)
public class JSExcelReader {
private ExcelReaderListener callback;
public JSExcelReader(String filePath, final WebView webView, ExcelReaderListener callback) {
this.callback = callback;
File file = new File(filePath);
try (InputStream is = new FileInputStream(file)) {
// convert file to Base64
if (file.length() > Integer.MAX_VALUE)
Log.e("File too big", "file too big");
byte[] bytes = new byte[(int) file.length()];
int offset = 0;
int numRead;
while (offset < bytes.length &&
(numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
if (offset < bytes.length)
throw new Exception("Could not completely read file");
final String b64 = Base64.encodeToString(bytes, Base64.NO_WRAP);
// feed the string into webview and get the result
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.loadUrl("file:///android_asset/AndroidParseExcel.html");
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
webView.evaluateJavascript("convertFile('" + b64 + "');", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
parseJSON(value);
}
});
}
});
} catch (Exception ex) {
Log.e("Convert Excel failure", ex.getMessage());
}
}
private void parseJSON(String jsonString) {
try {
// return value is something like "{\n\"Sheet1\":\n[\"title\"...
// you need to remove those escape character first
JSONObject jsonRoot = new JSONObject(jsonString.substring(1, jsonString.length() - 1)
.replaceAll("\\\\n", "")
.replaceAll("\\\\\"", "\"")
.replaceAll("\\\\\\\\\"", "'"));
JSONArray sheet1 = jsonRoot.optJSONArray("Sheet1");
List<String> stringList = new ArrayList<>();
JSONObject jsonObject;
for (int i = 0; i < sheet1.length(); i++) {
jsonObject = sheet1.getJSONObject(i);
stringList.add(jsonObject.optString("title"));
}
callback.onReadExcelCompleted(stringList);
} catch (Exception ex) {
Log.e("Error in parse JSON", ex.getMessage());
}
}
}
AndroidParseExcel.html: (You should put this and the JavaScript library into asset folder)
<html>
<script src="file:///android_asset/xlsx.core.min.js"></script>
<head></head>
<body>
</body>
<script type ="text/javascript">
"use strict";
var X = XLSX;
function convertFile(b64data) {
var wb = X.read(b64data, {type: 'base64',WTF: false});
var result = {};
wb.SheetNames.forEach(function(sheetName) {
var roa = X.utils.sheet_to_row_object_array(wb.Sheets[sheetName]);
if(roa.length > 0){
result[sheetName] = roa;
}
});
return JSON.stringify(result, 2, 2);
}
</script>
</html>
Related Topics
Sethinttextcolor() in Edittext
Listfragment Does Not Accept My Layout
Should I Use Getapplicationcontext or Activity.This in a Long Running Asynctask
Move Snackbar Above The Bottom Bar
How to Fix Android Studio Getting Stuck Executing Gradle Tasks
Android, How Do How to Get a List of All Files in a Folder
Detecting Webview Error and Show Message
Android and Symbian Nfc Mobile Development Questions and Answers (Faq)
Gradle Project Sync Failing After Google Announced The New Sdk Versioning System
Add Viewpagerindicator to Android Studio
Check for Access to Notifications Using Notificationlistenerservice
How to Replace The Activity's Fragment from The Fragment Itself
How to Get a List of Android Permissions
Google Map for Android My Location Custom Button
Android 4.3: Ble: Filtering Behaviour of Startlescan()