使用j2v8执行js,填充打印数据.实现后端动态配置打印模板

最近在参与公司的餐厅点餐系统开发,涉及到打印小票功能时,公司希望实现小票后端配置模板方式,最后采取如下方式实现

打印功能实现流程

目前获取打印信息是采用极光推送(这种方式其实有很多隐患)
用户下单-->极光-->主设备-->打印

 @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mVm = obtainViewModel(this, PrinterViewModel.class);
        mVm.getPrimaryPrinter();//1:进入主页,先拉取主设备id,判断当前设备是否是主设备
        observeData();
        JPushReceiver.NotificationHandler.execPendingTask();
        appUpdateController = new AppUpdateController(this);
        appUpdateController.checkUpdate();
        mMediaPlayer = MediaPlayer.create(BaseApplication.getInstance(), R.raw.new_order);
    }

    private void observeData() {
        mVm.loading.observe(this, aBoolean -> {
            if (aBoolean != null) {
                if (aBoolean)
                    showLoading("");
                else
                    hideLoading();
            }
        });
        mVm.isPrimaryDevice.observe(this, aBoolean -> {
            if (aBoolean != null) {
                if (aBoolean) {
                  //2:如果是主设备,再获取打印的模板信息,和打印机地址
                    mVm.getTemplateList();
                    mVm.getAllPrinters();
                }
            }
        });

        mVm.allPrintList.observe(this, printers -> {

            UserModelManager.getInstance().setPrinterInfo(printers);
            //auto connect when app start up
            if (checkNonNull(printers)) {
                //3:当拿到打印机时,如果打印机是未连接状态,先主动连接
                for (Printer printer : printers) {

                    if (printer.getType() == Constants.PRINTER_BLE) {
                        if (!printer.isConnected()) {
                            PrinterManager.getInstance().connectBle(printer.getSign(), printer.getName());
                        }
                    }
                    else if (printer.getType() == Constants.PRINTER_NET) {
                        DeviceConnFactoryManager deviceManager = PrinterManager.getInstance().getNetManager(printer.getSign());
                        if (deviceManager == null || !deviceManager.getConnState()) {
                            new DeviceConnFactoryManager.Build().setId(printer.getSign())
                                                                .setConnMethod(DeviceConnFactoryManager.CONN_METHOD.WIFI)
                                                                .setIp(printer.getSign())
                                                                .setPort(9100)
                                                                .setName("网络打印机")
                                                                .build();
                        }
                    }
                }
            } else {
                Log.e(TAG, "getAllPrinters  is empty ");
            }

        });

        mVm.template.observe(this, template -> {
            if (checkNonNull(template)) {
                UserModelManager.getInstance().setTemplateList(template.getTpl());
                //4:当拿到模板信息时,将模板的版本和sp中存的version进行对比
                int jsVersion = SharedPreferencesUtil.getInt(this, Constants.KEY_JS_VERSION, 0);
                //5:如果网络version大于本地的version,下载新模板(也就是js文件)
                if (template.getJs() != null && template.getJs().getVersion() > jsVersion) {
                    Data        data       = new Data.Builder().putString(Constants.KEY_JS_URL, template.getJs().getUrl()).build();
                    WorkRequest downloadJs = new OneTimeWorkRequest.Builder(DowloadJsWorker.class).setInputData(data).build();
                    WorkManager.getInstance().enqueue(downloadJs);
                    WorkManager.getInstance().getWorkInfoByIdLiveData(downloadJs.getId())
                               .observe(this, workInfo -> {
                    //6:js文件下载成功后,更新本地version号
                                   if (workInfo!=null&&workInfo.getState() == WorkInfo.State.SUCCEEDED) {
                                        SharedPreferencesUtil.putInt(this,Constants.KEY_JS_VERSION,template.getJs().getVersion());
                                   }
                               });
                }
            }
        });


    }

接下来就是实现打印操作

  1. 接收到推送时,判断是通知还是具体的打印数据

            String data = bundle.getString(JPushInterface.EXTRA_MESSAGE);//自定义消息中的推送内容
            try {
                NotificationInfo notificationInfo = new Gson().fromJson(data
                        , NotificationInfo.class);
                LogUtils.e("Notification data is :\n" + data);
                if (APP_NOTICE.equals(notificationInfo.channel)) {
                    NotificationHandler.notificationDatas.put(msgId, notificationInfo);
                    EventUtils.post(new NotificationEvent(msgId));
                } else if (PRINT_NOTICE.equals(notificationInfo.channel)) {
                    NotificationHandler.print(notificationInfo);
                }
            } catch (Exception e) {
                LogUtils.e(e);
            }

根据指示,判断打印什么单据

  public static void print(NotificationInfo notificationInfo) {
            JsonElement data = notificationInfo.msg.get("data");
            PrintData printData = new Gson().fromJson(data, PrintData.class);
            //并没有将打印数据解析成一个类,而是用String来接收打印数据,这样的好处是,无所谓后端如何更改打印数据的类型,前端不关心,直接丢给js解析成前端需要的格式就好了!
            String body = data.getAsJsonObject().get("body").toString();
            //supportRules是个int数据,1代表客看单,2 结账单,3制作单 可以动态配置打什么单据!
            JsonArray supportRules = notificationInfo.msg.get("supportRules").getAsJsonArray();


            for (JsonElement rule : supportRules) {
                PrintTools.PrintData pt=new PrintTools.PrintData();
                pt.rule=rule.getAsInt();
                pt.printer_id=printData.printer_id;
                pt.body=body;
                pt.store_name=printData.store_name;
                pt.u_t=printData.u_t;
          //打印操作
                PrintTools.print(pt);
            }
        }
    }
  
    private class NotificationInfo {
        private String channel;
        private String title;
      
        private Map<String, JsonElement> msg;
    }

打印操作

 public static void print(@NonNull PrintData printData) {
        Observable.create((ObservableOnSubscribe<String>) emitter -> {
            //1:读取本地js文件
            String jsContent = getFileContent(Constants.JS_FILE_PATH);
            if (TextUtils.isEmpty(jsContent)) {
                emitter.onError(new Exception("读取js文件出错或Js文件内容为空!"));
            }
            emitter.onNext(jsContent);
            emitter.onComplete();
        })
                  .flatMap((Function<String, ObservableSource<Wrapper>>) jsContent -> {
                    //2:读取成功后,获取打印机信息
                      Printer printer = UserModelManager.getInstance().getPrinter(printData.printer_id);
                      if (printer != null) {
                  //使用J2V8引擎来运行js
                          V8 v8 = V8.createV8Runtime();
                          v8.executeScript(jsContent);
                  //3:给js提供android日志回调,方便发现问题
                          v8.registerJavaMethod(new JavaCallback(), "log", "log", new Class<?>[]{String.class});

                          int paperWidth = UserModelManager.getInstance().getPrinter(printData.printer_id).getPaper_width();
                          //设置一行打印的字节数
                          JSONObject jo = new JSONObject();
                          if (paperWidth == 58) {
                              jo.put("pageWidth", LINE_BYTE_SIZE_58);
                          } else {
                              jo.put("pageWidth", LINE_BYTE_SIZE_88);
                          }
                          //打印数据
                          String data = printData.body;

                          //模板
                          String                               template     = "";
                          List<PrintTemplateBean.TemplateBean> templateList = UserModelManager.getInstance().getTemplateList();
                          if (!checkNonNull(templateList)) {
                              Log.e(TAG, "打印模板不存在!");
                          } else {
                              for (PrintTemplateBean.TemplateBean bean : templateList) {
                                  if (bean.getType() == printData.rule) {
                                      template = bean.getLayouts();
                                      break;
                                  }
                              }
                          }

                          if (!checkNonNull(template)) {
                              Log.e(TAG, "template error: templateData is empty");
                          }
                          //4:得到js解析后的打印数据
                          String printStr = (String) v8.executeJSFunction("run", jo.toString(), data, template);
                          Log.e(TAG, "print: \n" + printStr);
                          v8.release();
                          //5:将打印数据解析成PrintLineData数组,一个PrintLineData就是一行!
                          List<PrintLineData> datas   = GsonUtils.gsonToList(printStr, PrintLineData.class);
                          Wrapper             wrapper = new Wrapper();
                          wrapper.printer = printer;
                          wrapper.data = datas;
                          return Observable.just(wrapper);
                      } else {
                          Log.e(TAG, "未找到 id为" + printData.printer_id + "的打印机!");
                      }
                      return Observable.just(new Wrapper());
                  })
                  .subscribeOn(Schedulers.io())
                  .subscribe(wrapper -> {
                      if (checkNonNull(wrapper.data)) {
                          //6:开始连接打印机,进行打印
                          print(wrapper);
                      }
                  }, throwable -> Log.e(TAG, "print error ", throwable));
    }

    //打印!!!
    private static void print(Wrapper wrapper) {
        EscCommand esc = new EscCommand();
        esc.addInitializePrinter();

        for (PrintLineData line : wrapper.data) {
            if (checkNonNull(line.getColumns())) {
                if (line.getColumns().size() > 1) {//多列
                    for (PrintLineData.ColumnsBean column : line.getColumns()) {
                        //加粗
                        boolean isBold = column.getBold() == 1;
                        esc.addSelectPrintModes(EscCommand.FONT.FONTA, isBold ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);
                        //倍高倍宽
                        boolean isLarge = column.getFontSize().equals("large");
                        esc.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, isLarge ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, isLarge ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);

                        esc.addSelectJustification(column.getTextAlignment());
                        esc.addText(column.getContent());
                    }
                    esc.addText("\n");
                } else {
                    PrintLineData.ColumnsBean column = line.getColumns().get(0);
                    //加粗
                    boolean isBold = column.getBold() == 1;
                    esc.addSelectPrintModes(EscCommand.FONT.FONTA, isBold ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);
                    //倍高倍宽
                    boolean isLarge = column.getFontSize().equals("large");
                    esc.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, isLarge ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, isLarge ? EscCommand.ENABLE.ON : EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);
                    esc.addSelectJustification(column.getTextAlignment());
                    esc.addText(column.getContent() + "\n");
                }
            }
        }
        esc.addPrintAndFeedLines((byte) 1);
        esc.addCutPaper();

        Vector<Byte> datas = esc.getCommand();
        // 发送数据
        DeviceConnFactoryManager manager = PrinterManager.getInstance().getManager(wrapper.printer);
        if (manager != null) {
            manager.openPort();
            manager.sendDataImmediately(datas);
        }
    }

    public static class JavaCallback {
        public void log(String error) {
            Log.e(TAG, "print lllllll ," + error);
        }
    }

    public static class PrintData {
        public String printer_id;

        public String store_name;

        public String operator_name;

        public int rule;

        public String body;

        /**
         * 打印时间:【2018-09-23 17:55:09】
         */
        public String u_t;
    }

这是最开始的打印功能实现,还有很多bug和问题,之后会慢慢记录出来!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容