Cordova Android 自定义插件/应用程序 - 自定义插件上未找到类异常
Posted
技术标签:
【中文标题】Cordova Android 自定义插件/应用程序 - 自定义插件上未找到类异常【英文标题】:Cordova Android Custom Plugin/App - Class Not Found Exception on Custom Plugin 【发布时间】:2018-01-12 15:41:57 【问题描述】:我正在为 Cordova 设计一个自定义插件作为教育演示。该插件与 android PdfRenderer 类集成,并将 API 公开给 Cordova 生态系统。
我的自定义插件正在编译且没有错误,我已将该插件添加到我的测试项目中。但是,当我构建并运行测试项目时,应用程序在启动时崩溃,我收到以下消息:
java.lang.ClassNotFoundException: com.dev.plugin.PdfRendererPlugin
我检查了我的platforms/android/
文件夹,插件类如预期的那样位于src/com/dev/plugin/PdfRenderPlugin.java
。
在这种情况下我还应该寻找什么?如果插件编译并添加到项目中没有错误,我的应用程序现在应该可以正常工作了。
这里有一些代码可以使用:
插件.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();
【问题讨论】:
【参考方案1】:我觉得自己很傻。问题出在包声明上。我没有考虑就将类名添加到包声明中(一定是因为我从 plugin.xml 中复制并粘贴了包含类名的路径)。
【讨论】:
以上是关于Cordova Android 自定义插件/应用程序 - 自定义插件上未找到类异常的主要内容,如果未能解决你的问题,请参考以下文章
Cordova与现有框架的结合,Cordova插件使用教程,Cordova自定义插件,框架集成Cordova,将Cordova集成到现有框架中