在一个项目中,我需要使用自定义类加载器来扩展标准类加载器。其主要动机是在运行时扩展类路径。 (有关此问题的解决方案,请参见:https://stackoverflow.com/a/51584718/231397)。
通过...序列化对象
api
工作正常,同时通过以下方式反序列化对象
public class BakingAppWidgetProvider extends AppWidgetProvider implements JsonTask.JsonToBookConverter {
private final String LOG_TAG = this.getClass().getSimpleName();
private Recipe currentRecipe;
//THE API DATA SHOULD BE STORED HERE AFTER IT'S BEEN CONVERTED TO THIS DATATYPE
private RecipeBook book;
private static final String ACTION_UPDATE_CLICK_NEXT = "action.UPDATE_CLICK_NEXT";
private static final String ACTION_UPDATE_CLICK_PREVIOUS = "action.UPDATE_CLICK_PREVIOUS";
public BakingAppWidgetProvider() {
String jsonString = null;
////THE DATA COMES FROM THIS TASK (OWN CLASS IMPLEMENTATION OF ASYNCTASK)
new JsonTask(jsonString, this).execute();
}
////THIS IS THE METHOD FROM THE INTERFACE THAT IS DECLARED IN THE ASYNCTASK IMPLEMENTATION; IT'S CALLED IN onPostExecuted AND CONVERTS THE JSON TO MY BOOK DATA TYPE
public void convertJson(String jsonString) {
Gson gson = new Gson();
String preparedForGson = "{recipes:" + jsonString + "}";
/// I have access to 'private RecipeBook book; '
book = gson.fromJson(preparedForGson, RecipeBook.class);
Log.v(LOG_TAG, "book is ready");
// WORKS
Log.v(LOG_TAG, book.toString());
}
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.v(LOG_TAG, "Widget onUpdate");
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.baking_appwidget);
views.setOnClickPendingIntent(R.id.rightBtn, getPendingSelfIntent(context, ACTION_UPDATE_CLICK_NEXT));
int appWidgetId = appWidgetIds[i];
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
PendingIntent getPendingSelfIntent(Context context, String action) {
Intent intent = new Intent(context, getClass());
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
//PROBLEM IS HERE
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Log.v(LOG_TAG, "onReceive");
Log.v(LOG_TAG, intent.getAction());
// you should simply go back and forth in the recipe collection
if (ACTION_UPDATE_CLICK_NEXT.equals(intent.getAction())) {
Log.v(LOG_TAG, "nextClicked");
//THIS LEADS TO A NULLPOINTEREXEPTION!!!!!
Log.v(LOG_TAG, book.toString());
} else if (ACTION_UPDATE_CLICK_PREVIOUS.equals(intent.getAction())) {
Log.v(LOG_TAG, "prevClicked");
}
失败,发生ObjectOutputStream out;
try {
FileOutputStream fos = new FileOutputStream(filename);
out = new ObjectOutputStream(fos);
out.writeObject(object);
}
catch(FileNotFoundException e) {}
catch (IOException e) { }
finally {
out.close();
}
异常。原因是调用了标准类加载器,而该类加载器不知道自定义类加载器。
问:使用自定义类加载器反序列化对象的正确方法是什么?
答案 0 :(得分:3)
(注意:在网上搜索解决方案后,我回答了我自己的问题。人们在网上发现的一些解决方案“几乎是正确的”:不适用于内部类)。
解决方案是扩展ObjectInputStream
并覆盖方法resolveClass(ObjectStreamClass)
这可以通过匿名类来完成。然后将Class.forName
内的resolveClass
与自定义类加载器一起使用。那是
Object object = null;
try {
FileInputStream fis = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(fis) {
@Override
public Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
try {
return Class.forName(desc.getName(), true, customClassLoader);
} catch (Exception e) { }
// Fall back (e.g. for primClasses)
return super.resolveClass(desc);
}
};
object = in.readObject();
}
catch(FileNotFoundException e) {}
catch (ClassNotFoundException e) {}
finally {
in.close();
}
备注:您可能会找到建议使用的解决方案
customClassLoader.loadClass(desc.getName());
代替
Class.forName(desc.getName(), true, customClassLoader)
尽管这在许多情况下都可行,但可能会有问题。例如,我发现customClassLoader.loadClass(desc.getName())
不适用于内部类(可能是由于在解析为p.A.B或p.A $ B的程序包p中,对A类的内部B类的命名存在差异)。另请注意,loadClass
不会初始化该类。参见https://stackoverflow.com/a/7099453/231397