我正在为Cordova设计一个自定义插件作为教育演示。该插件与Android PdfRenderer类集成,并将API暴露给Cordova生态系统。
我的自定义插件正在编译而没有错误,我已将插件添加到我的测试项目中。但是,当我构建并运行测试项目时,应用程序在启动时崩溃,并且我遇到以下消息:
java.lang.ClassNotFoundException: com.dev.plugin.PdfRendererPlugin
我已经检查了我的platforms/android/
文件夹,并且按照预期在src/com/dev/plugin/PdfRenderPlugin.java
找到了插件类。
在这种情况下我还应该寻找什么呢?看起来我的应用程序现在应该正常工作,如果插件编译并添加到项目中没有错误。
以下是一些可以使用的代码:
的plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="cordova-android-pdf-renderer-plugin" version="0.2.3">
<name>PdfRendererPlugin</name>
<description>Cordova PDF Renderer Plugin</description>
<license>MIT</license>
<keywords>cordova,pdf,renderer</keywords>
<platform name="android">
<js-module src="www/js/PdfRendererPlugin.js" name="PdfRendererPlugin">
<runs/>
<clobbers target="PdfRendererPlugin" />
</js-module>
<config-file target="config.xml" parent="/*">
<feature name="com.dev.plugin.PdfRendererPlugin">
<param name="android-package" value="com.dev.plugin.PdfRendererPlugin"/>
<param name="onload" value="true" />
</feature>
</config-file>
<source-file src="src/android/PdfRendererPlugin.java" target-dir="src/com/dev/plugin/" />
</platform>
</plugin>
插件类(PdfRendererService)
package com.dev.plugin.PdfRendererService;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.pdf.PdfRenderer;
import android.graphics.pdf.PdfRenderer.Page;
import android.os.ParcelFileDescriptor;
import android.util.Log;
/**
* This class handles a pdf file called from JavaScript and converts a selected
page (default is first) to a byte array representing a bitmap.
*/
public class PdfRendererPlugin extends CordovaPlugin {
private static final String LOG_TAG = "PdfRendererPlugin";
private ParcelFileDescriptor fileDescriptor = null;
private PdfRenderer renderer = null;
private Page currentPage = null;
private int mWidth = 400, mHeight = 600;
private String mRenderMode = "display";
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView){
Log.d(LOG_TAG, "initialize");
super.initialize(cordova, webView);
}
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
Log.d(LOG_TAG, "execute");
//No Switch -> src 1.6
if(action.equals("open")){
return executeOpen(args, callbackContext);
}
else if(action.equals("renderPage")){
return executeRenderPage(args, callbackContext);
}
else if(action.equals("pageCount")){
callbackContext.success(this.getPageCount());
return true;
}
else if(action.equals("close")){
this.closeRenderer();
callbackContext.success();
return true;
}
return false;
}
private boolean executeOpen(JSONArray args, CallbackContext callbackContext){
Log.d(LOG_TAG, "executeOpen");
String filePath = "";
try{
if(args.length() < 1){
Log.e(LOG_TAG, "No arguments provided. Exiting process.");
callbackContext.error("No arguments provided. Exiting process.");
return true;
}
else if(args.length() < 2){
Log.e(LOG_TAG, "Insufficient arguments provided. Exiting process.");
callbackContext.error("Insufficient arguments provided. Exiting process.");
return true;
}
if(args.length() > 3){
mWidth = args.getInt(2);
mHeight = args.getInt(3);
}
filePath = args.getString(0);
mRenderMode = args.getString(1);
}
catch(JSONException je){
String msg = je.getMessage();
if(msg == null)
msg = "Unknown JSONException has occurred";
Log.e(LOG_TAG, msg);
}
this.initializeRenderer(filePath, callbackContext);
boolean isPageOpen = this.openPage(0, callbackContext);
if(isPageOpen){
Bitmap bitmap = getBitmap(mWidth, mHeight);
this.sendBitmapAsBytes(0, bitmap, callbackContext);
}
return true;
}
private boolean executeRenderPage(JSONArray args, CallbackContext callbackContext){
Log.d(LOG_TAG, "executeRenderPage");
int pageNo = -1;
try {
if (args.length() < 1) {
Log.e(LOG_TAG, "No arguments provided. Exiting process.");
callbackContext.error("No arguments provided. Exiting process.");
return true;
}
if (args.length() > 1) {
mRenderMode = args.getString(1);
}
if (args.length() > 3) {
mWidth = args.getInt(2);
mHeight = args.getInt(3);
}
pageNo = args.getInt(0);
}
catch(JSONException je){
String msg = je.getMessage();
if(msg == null)
msg = "Unknown JSONException has occurred";
Log.e(LOG_TAG, msg);
}
if(pageNo < 0)
return false;
boolean isPageOpen = this.openPage(pageNo, callbackContext);
if(isPageOpen) {
Bitmap bitmap = getBitmap(mWidth, mHeight);
this.sendBitmapAsBytes(pageNo, bitmap, callbackContext);
}
return true;
}
/*
// Requests the permission to read from external storage if not already available
private void validatePermissions(){
Log.d(LOG_TAG, "validatePermissions");
if(!cordova.hasPermission(READ_EXTERNAL_STORAGE)){
Log.i(LOG_TAG, "Requesting External Storage Read Permission...");
cordova.requestPermission(this, CODE_READ_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE);
}
}
*/
private int getPageCount() {
Log.d(LOG_TAG, "getPageCount");
if(renderer == null)
return 0;
return renderer.getPageCount();
}
private void initializeWriteFileDescriptor(String filePath, CallbackContext callbackContext) throws FileNotFoundException, FileFormatException {
Log.d(LOG_TAG, "initializeWriteFileDescriptor");
fileDescriptor = null;
if(filePath == null || filePath.length() < 1)
throw new FileNotFoundException("The file path provided is not a valid file path.");
String[] pathArr = filePath.split(".");
int numSections = pathArr.length;
String ext = pathArr[numSections - 1];
if(!ext.equals("pdf"))
throw new FileFormatException("Invalid File Extension provided to Pdf Render Service: " + ext);
fileDescriptor = getWriteFileDescriptor(filePath);
}
private void initializeRenderer(String filePath, CallbackContext callbackContext){
Log.d(LOG_TAG, "initializeRenderer");
renderer = null;
try {
initializeWriteFileDescriptor(filePath, callbackContext);
renderer = new PdfRenderer(fileDescriptor);
}
catch(IOException io){
String msg = io.getMessage();
if(msg == null)
msg = "An error has occurred while loading the requested file.";
Log.e(LOG_TAG, msg);
callbackContext.error(msg);
}
}
private void closeRenderer() {
Log.d(LOG_TAG, "closeRenderer");
if(renderer == null) {
Log.w(LOG_TAG, "Attempted to close null renderer. Skipping operation.");
return;
}
renderer.close();
}
private boolean openPage(int index, CallbackContext callbackContext){
Log.d(LOG_TAG, "openPage");
currentPage = null;
int pageCount = getPageCount();
if(pageCount < 1) {
Log.e(LOG_TAG, "Requested document has no pages to display.");
callbackContext.error("Requested document has no pages to display.");
return false;
}
if(index >= pageCount || index < 0) {
Log.e(LOG_TAG, String.format("No page was found at page number %d/%d", index, pageCount));
callbackContext.error(String.format("No page was found at page number %d/%d", index, pageCount));
return false;
}
currentPage = renderer.openPage(index);
return true;
}
private void sendBitmapAsBytes(int index, Bitmap bitmap, CallbackContext callbackContext){
Log.d(LOG_TAG, "sendBitmapAsBytes");
if(renderer == null) {
Log.e(LOG_TAG, "Renderer was not properly initialized.");
callbackContext.error("Renderer was not properly initialized.");
return;
}
if(currentPage == null) {
Log.e(LOG_TAG, "Requested page could not be rendered.");
callbackContext.error("Requested page could not be rendered.");
return;
}
int renderMode = mRenderMode.equals("print") ? Page.RENDER_MODE_FOR_PRINT : Page.RENDER_MODE_FOR_DISPLAY;
currentPage.render(bitmap, null, null, renderMode);
byte[] output = toByteArray(bitmap);
if(output == null || output.length < 1) {
Log.e(LOG_TAG, "Bitmap Error has occurred: Invalid Output Format Detected");
callbackContext.error("Bitmap Error has occurred: Invalid Output Format Detected");
}
else {
Log.i(LOG_TAG, "Bitmap Conversion Successful");
callbackContext.success(output);
}
}
private static byte[] toByteArray(Bitmap bitmap){
Log.d(LOG_TAG, "toByteArray");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
return stream.toByteArray();
}
private static Bitmap getBitmap(int width, int height){
Log.d(LOG_TAG, "getBitmap");
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
private static ParcelFileDescriptor getWriteFileDescriptor(String filePath) throws FileNotFoundException {
Log.d(LOG_TAG, "getWriteFileDescriptor");
File file = new File(filePath);
final int fileMode = ParcelFileDescriptor.MODE_TRUNCATE |
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_WRITE_ONLY;
return ParcelFileDescriptor.open(file, fileMode);
}
class FileFormatException extends IOException {
FileFormatException(String msg){
super(msg);
}
}
}
插件JS接口
var PLUGIN_NAME = "PdfRendererPlugin";
var SERVICE_OPEN = "open";
var SERVICE_CLOSE = "close";
var SERVICE_PAGE_COUNT = "pageCount";
var SERVICE_RENDER_PAGE = "renderPage";
var RENDER_MODE_DISPLAY = "display";
var RENDER_MODE_PRINT = "print";
var PdfRendererPlugin = {
display: function(filePath, callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_OPEN, [filePath, RENDER_MODE_DISPLAY]);
},
displayWithDimensions: function(filePath, width, height, callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_OPEN, [filePath, RENDER_MODE_DISPLAY, width, height]);
},
print: function(filePath, callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_OPEN, [filePath, RENDER_MODE_PRINT]);
},
printWithDimensions: function(filePath, width, height, callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_OPEN, [filePath, RENDER_MODE_PRINT, width, height]);
},
renderPage: function(pageNo, callback){
cordova.exec(callback, function(err){
//console.log(err);
}, PLUGIN_NAME, SERVICE_RENDER_PAGE, [pageNo]);
},
renderPageForDisplay: function(pageNo, callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_RENDER_PAGE, [pageNo, RENDER_MODE_DISPLAY]);
},
renderPageForDisplayWithDimensions: function(pageNo, width, height, callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_RENDER_PAGE, [pageNo, RENDER_MODE_DISPLAY, width, height]);
},
renderPageForPrint: function(pageNo, callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_RENDER_PAGE, [pageNo, RENDER_MODE_PRINT]);
},
renderPageForPrintWithDimensions: function(pageNo, width, height, callback){
cordova.exec(callback, function(err){
//console.log(err);
}, PLUGIN_NAME, SERVICE_RENDER_PAGE, [pageNo, RENDER_MODE_PRINT, width, height]);
},
close: function(callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_CLOSE, []);
},
getPageCount: function(callback){
cordova.exec(callback, function(err){
// console.log(err);
}, PLUGIN_NAME, SERVICE_PAGE_COUNT, []);
}
};
测试应用程序index.html
<!DOCTYPE html>
<html>
<head>
<title>Cordova PDF Generator Plugin Test</title>
<meta name="viewport" content="user-scalable=no, initial-scale=1,
maximum-scale=1, minimum-scale=1, width=device-width,
height=device-height" />
</head>
<body>
<div class="app">
<h1>Cordova PDF Generation Plugin Test</h1>
<div>
<button id="display-button" onclick="display()">Display (View)</button>
<button id="print-button" onclick="print()">Display (Print)</button>
</div>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html>
测试应用程序index.js
var testFilePath = 'assets/test-file.pdf';
var app = {
// Application Constructor
initialize: function() {
document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
},
// deviceready Event Handler
//
// Bind any cordova events here. Common events are:
// 'pause', 'resume', etc.
onDeviceReady: function() {
display();
}
};
var display = function(){
PdfRendererPlugin.display(testFilePath, function(data){
console.log('Bitmap Bytes');
console.log(data);
});
};
var print = function(){
PdfRendererPlugin.print(testFilePath, function(data){
console.log('Bitmap Bytes');
console.log(data);
});
};
app.initialize();
答案 0 :(得分:0)
我觉得很傻。问题出在包装声明上。我在没有考虑它的情况下将类名添加到包声明中(这一定是因为我从plugin.xml中复制并粘贴了包含类名的路径)。