是否可以使用 BeanShell 在 java 中创建(不导入)新的 class?

Is it possible to create (NOT import) a new class in java using BeanShell?

我使用 BeanShell 动态管理我的 android 应用程序中的界面和内容。今天遇到如下问题:需要为应用程序处理一个结构未知的class,即服务器发代码,应用程序处理

下面是我的代码处理程序之一。错误报告给“public class LastNews{”:

    import android.widget.LinearLayout;
    import android.widget.ScrollView;
    import android.widget.TextView;
    import android.graphics.Color;
    import android.view.ViewGroup;
    import android.util.TypedValue;
    import android.graphics.Typeface;
    import java.util.Random;
    import android.view.View;
    import android.widget.Toast;
    import apppackege.ViewCreator;
    import apppackege.Calculate;
    import android.widget.GridView;
    import android.widget.BaseAdapter;
    import apppackege.AdapterCreator;
    import apppackege.IconName;
    import com.google.gson.Gson;
    import com.google.gson.reflect.TypeToken;
    import apppackege.WrappingGridView;
    import java.util.ArrayList;
    import apppackege.Decoder;
    import apppackege.LoadImage;
    import android.widget.ImageView;

    LinearLayout topPanel = ViewCreator.linearLayout(context, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
            Calculate.dpToPixel(context, 40)), LinearLayout.HORIZONTAL);
    topPanel.setBackgroundColor(Color.parseColor("#1E88E5"));

    LinearLayout bottomPanel = ViewCreator.linearLayout(context, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
            Calculate.dpToPixel(context, 40)), LinearLayout.HORIZONTAL);
    bottomPanel.setBackgroundColor(Color.parseColor("#EFEFEF"));

    ScrollView scrollView = ViewCreator.scrollView(context, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT, 1.0f));
    scrollView.setBackgroundColor(Color.WHITE);

    parent.addView(topPanel);
    parent.addView(scrollView);
    parent.addView(bottomPanel);

    LinearLayout scrollLayout = ViewCreator.linearLayout(context,
            new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT), -1);
    scrollView.addView(scrollLayout);

    ImageView topImage = ViewCreator.imageView(context, null, null);
    new LoadImage(topImage, "https://url.com/9tVFtRoOCIQ.jpg");
    scrollLayout.addView(topImage);

    WrappingGridView wrappingGridView = ViewCreator.wrappingGridView(context, null, 3);
    scrollLayout.addView(wrappingGridView);
    String jsonIconName = "[{\"icon\":\"https://url.com/thumb-up.png\",\"name\":\"ГОЛОС\",\"iconColor\":\"#1E88E5\"},{\"icon\":\"https://url.com/news.png\",\"name\":\"НОВОСТИ\",\"iconColor\":\"#1E88E5\"},{\"icon\":\"https://url.com/camping-tent.png\", \"name\": \"ТУРИЗМ\",\"iconColor\":\"#1E88E5\"},{\"icon\":\"https://url.com/image.png\",\"name\":\"ФОТО\",\"iconColor\":\"#1E88E5\"},{\"icon\":\"https://url.com/cafe.png\",\"name\":\"МЕСТА\",\"iconColor\":\"#1E88E5\"},{\"icon\":\"https://url.com/user.png\",\"name\":\"АККАУНТ\",\"iconColor\":\"#1E88E5\"},{\"icon\":\"https://url.com/cinema-.png\",\"name\":\"АФИША\",\"iconColor\":\"#1E88E5\"},{\"icon\":\"https://url.com/focal-length.png\",\"name\":\"КОНТРОЛЬ\",\"iconColor\":\"#1E88E5\"},{\"icon\":\"https://url.com/chase.png\",\"name\":\"ОХОТА\",\"iconColor\":\"#1E88E5\"}]";
    BaseAdapter baseAdapter = AdapterCreator.baseAdapterIconName(context, Decoder.fromJsonArray(jsonIconName, IconName.class));
    wrappingGridView.setAdapter(baseAdapter);
    ((BaseAdapter)wrappingGridView.getAdapter()).notifyDataSetInvalidated();

    TextView lineActual = ViewCreator.textView(context, "АКТУАЛЬНО",
            null, 10, 14, "#1E88E5", new View.OnClickListener() {
        public void onClick(View v) {
            Toast.makeText(context, "АКТУАЛЬНО", Toast.LENGTH_SHORT).show();
        }
    });
    scrollLayout.addView(lineActual);

    TextView lineNews = ViewCreator.textView(context, "СВЕЖИЕ НОВОСТИ",
            null, 10, 14, "#1E88E5", new View.OnClickListener() {
        public void onClick(View v) {
            Toast.makeText(context, "СВЕЖИЕ НОВОСТИ", Toast.LENGTH_SHORT).show();
        }
    });
    scrollLayout.addView(lineNews);
    String jsonNews = " [ { \"id\": \"89e94f07-2939-11ec-a3ee-e145f6ad5670\", \"head\": \"Белорус сделал предложение Бузовой в прямом эфире X Factor\", \"icon\": \"http://url.com/73fde9f1-a72f-43c0-a6e7-10957e216a9b.jpg\", \"type\": \"http://url.com/info.png\", \"location\": \"Беларусь\" }, { \"id\": \"c2833cf1-2928-11ec-a3ee-e145f6ad5670\", \"head\": \"Жители Приморья стоят в очередях за белорусскими продуктами - Богданов\", \"icon\": \"http://url.com/77e75355-4255-4cae-9962-fdb7394986c5.jpg\", \"type\": \"http://url.com/info.png\", \"location\": \"Беларусь\" }, { \"id\": \"c2833cf0-2928-11ec-a3ee-e145f6ad5670\", \"head\": \"Помолодел и стал жестким – глава Минздрава о четвертой волне коронавируса\", \"icon\": \"http://url.com/f28b5349-e639-4250-9391-7ad88fcf4946.jpg\", \"type\": \"http://url.com/info.png\", \"location\": \"Беларусь\" }, { \"id\": \"c2833cef-2928-11ec-a3ee-e145f6ad5670\", \"head\": \"Так было или не было? Серега ответил о своем романе с Бузовой\", \"icon\": \"http://url.com/10-2021/60cc9d51-f570-4e68-bada-4ba4aab92bcb.jpg\", \"type\": \"http://url.com/info.png\", \"location\": \"Минск\" }, { \"id\": \"6088c966-2920-11ec-a3ee-e145f6ad5670\", \"head\": \"Работа ТЭЦ в Минске восстановлена - Минэнерго\", \"icon\": \"http://url.com/01b6db5f-8305-4e86-9d9a-f59cc2be2787.jpg\", \"type\": \"http://url.com/info.png\", \"location\": \"Минск\" } ]";

    public class LastNews{
        @SerializedName("id")
        public String id;
        @SerializedName("head")
        public String head;
        @SerializedName("icon")
        public String icon;
        @SerializedName("type")
        public String type;
        @SerializedName("location")
        public String location;
    }

    LinearLayout listNews = ViewCreator.linearLayout(context, null, -1);
    ArrayList<Object> news = Decoder.fromJsonArray(jsonNews, LastNews.class);
    for(int i = 0; i < news.size(); i++){

        LastNews lastNews = (LastNews) news.get(i);

        //crate view and add to "listNews"

    }

错误信息是:

Sourced file: inline evaluation of: ``import android.widget.LinearLayout;         import android.widget.ScrollView;    . . . '' unknown error: can't load this type of class file : at Line: 75 : in file: inline evaluation of: ``import android.widget.LinearLayout;         import android.widget.ScrollView;    . . . '' : public class LastNews { 

你能告诉我使用 BeanShell 是否可以实现这样的实现吗?如果是这样,是否可以提供此类功能的示例?

也许,但不是通过发送 class 文件的文本。 Java 是一种编译语言。您需要发送 .class 文件,或最新的 android Java 解释器使用的等效文件(还是 .dex 吗?)。然后您需要通过自定义 class 加载程序加载它。基本上,您将要施展黑魔法,并且确实需要了解 Java,它是 class 结构,以及解释器是如何工作的。

关于此的几个警告:

1) Google 播放时不允许。 Google 不允许在其商店的应用程序中动态加载代码。

2) 这是一个巨大的安全漏洞。这就是为什么#1 是这种情况的部分原因。

3) 随着时间的推移,随着代码的更改,它成为错误的主要来源。您现在不仅要担心“我的发布是否有效”,还要担心“我的发布是否适用于它可能需要与之交互的此下载代码的每个可能版本”。还有“如果跳过了一个版本,即使跳过了一个版本,数据结构是否仍然兼容”。这里还有可能存在安全漏洞。

根据提供给我的信息。我做了最方便的决定。我从处理中排除了 classes 本身。

因此,现在我将使用“对象”本身,对其进行进一步处理。如果将来有人需要类似的解决方案,那么在我看来最紧凑的就是使用“Object”和“LinkedTreeMap”这样的对象。

这里是我草图的结果,下面我将展开修改后的示例代码:

    //...here is all the imported data

    //...the code from my first example

    String jsonNews = " [{\"id\":\"89e94f07-2939-11ec-a3ee-e145f6ad5670\",\"head\":\"Белорус сделал предложение Бузовой в прямом эфире X Factor\", \"icon\": \"http://45.147.199.147/IMAGES/FOTO/10-2021/73fde9f1-a72f-43c0-a6e7-10957e216a9b.jpg\", \"type\": \"http://45.147.199.147/IMAGES/FOTO/ICONS/info.png\", \"location\": \"Беларусь\" }, { \"id\": \"c2833cf1-2928-11ec-a3ee-e145f6ad5670\", \"head\": \"Жители Приморья стоят в очередях за белорусскими продуктами - Богданов\", \"icon\": \"http://45.147.199.147/IMAGES/FOTO/10-2021/77e75355-4255-4cae-9962-fdb7394986c5.jpg\", \"type\": \"http://45.147.199.147/IMAGES/FOTO/ICONS/info.png\", \"location\": \"Беларусь\" }, { \"id\": \"c2833cf0-2928-11ec-a3ee-e145f6ad5670\", \"head\": \"Помолодел и стал жестким – глава Минздрава о четвертой волне коронавируса\", \"icon\": \"http://45.147.199.147/IMAGES/FOTO/10-2021/f28b5349-e639-4250-9391-7ad88fcf4946.jpg\", \"type\": \"http://45.147.199.147/IMAGES/FOTO/ICONS/info.png\", \"location\": \"Беларусь\" }, { \"id\": \"c2833cef-2928-11ec-a3ee-e145f6ad5670\", \"head\": \"Так было или не было? Серега ответил о своем романе с Бузовой\", \"icon\": \"http://45.147.199.147/IMAGES/FOTO/10-2021/60cc9d51-f570-4e68-bada-4ba4aab92bcb.jpg\", \"type\": \"http://45.147.199.147/IMAGES/FOTO/ICONS/info.png\", \"location\": \"Минск\" }, { \"id\": \"6088c966-2920-11ec-a3ee-e145f6ad5670\", \"head\": \"Работа ТЭЦ в Минске восстановлена - Минэнерго\", \"icon\": \"http://45.147.199.147/IMAGES/FOTO/10-2021/01b6db5f-8305-4e86-9d9a-f59cc2be2787.jpg\", \"type\": \"http://45.147.199.147/IMAGES/FOTO/ICONS/info.png\", \"location\": \"Минск\" } ]";
    LinearLayout listNews = ViewCreator.linearLayout(context, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT), LinearLayout.VERTICAL);
    ArrayList news = Decoder.fromJsonArray(jsonNews, null);
    for(int i=0; i < news.size(); i++){
        TextView newsHead = ViewCreator.textView(context, news.get(i).get("head"),
        null, 10, 14, "#1E88E5", new View.OnClickListener() {
            public void onClick(View v) {
                Toast.makeText(context, news.get(i).get("id"), Toast.LENGTH_SHORT).show();
            }
        });
        listNews.addView(newsHead);
    }
    scrollLayout.addView(listNews);

请注意转换的方式json,转换成对象数组,其中每个对象是一个“LinkedTreeMap”:

public class Decoder {

    public static ArrayList<Object> fromJsonArray(String json, Class typeClass){
        try{
            if(typeClass != null) {
                return new Gson().fromJson(json, TypeToken.getParameterized(List.class, typeClass).getType());
            }
            return new Gson().fromJson(json, new TypeToken<List<Object>>() {}.getType());
        } catch (Exception ex){
            return new ArrayList<>();
        }
    }
}

最初,我的测试 class 有一个看起来像这样的任务:

public class LastNews{
    @SerializedName("id")
    public String id;
    @SerializedName("head")
    public String head;
    @SerializedName("icon")
    public String icon;
    @SerializedName("type")
    public String type;
    @SerializedName("location")
    public String location;
}

现在,在使用“LinkedTreeMap”元素时,要获取其变量,您可以使用以下内容(类似于“[]”括号):

news.get(i).get("head")

总而言之,我想说这个解决方案有一个更紧凑的形式,因为不需要传输和存储classes。 我想补充一点,应用程序的任务是尽可能安全,以尽可能排除其被黑客攻击的可能性。毕竟,应用程序将在状态级别。

也许此刻我的决定并不正确。我以后很可能会遇到很多问题。