我正在Swing内部实现一个程序,我已经读过Nirmal在Swing中对这个模式的实现,它似乎展示了对整个“职责分离”概念相当优雅的处理。
但是,由于我正在开发一个比Nirml发布的程序更复杂的程序,它由一个JFrame容器组成,因此我希望得到如何正确实现MVC的指导。
我的程序将由子容器等组成。我很好奇控制器应该如何实现定义和分配视图的所有侦听器背后的逻辑。或者为每个视图组件定义侦听器的控制器是否实用?
首先,Swing已经实现了MVC的一种形式,尽管是VC-M的形式。这意味着您不应该直接尝试将Swing限制为纯MVC,因为您会非常失望,并花费大量时间尝试在不该出现的地方进行修改。
相反,您可以将MVC封装在Swing周围,允许它围绕API工作。
在我看来,控制器不需要知道,也不应该关心视图或模型是如何实现的,但它只应该关心如何与它们一起工作(我已经有太多的开发人员掌握了UI组件,并对它们做了他们不应该做的事情,并且在我们更改实现时破坏了API。最好隐藏这种细节)
在这种情况下,您可以将视图视为自包含的实体--它具有控件,并且独立于控件进行操作。控制器不关心实现细节。它所关心的是在合同描述的某些事件发生时获得信息并被告知。它不应该关心它是如何生成的。
例如,假设您有一个登录视图。控制器只想知道用户输入的用户名和密码以及何时验证该信息。
假设您实现了视图/控制器,以公开JTextField
和JPasswordField
,但稍后,您的用户希望用户名选择被限制在特定的列表(可能由模型提供)中。现在,控制器中的实现细节不再适用,您必须手动更改或为这个新的用例创建一个新的MVC。
关于你问题的更大方面。
我的程序将由子容器等组成。我很好奇控制器应该如何实现定义和分配视图的所有侦听器背后的逻辑。或者为每个视图组件定义侦听器的控制器是否实用?
我似乎需要在视图的顶级容器中使用一个方法,以允许控制器调用视图,为所讨论的组件添加一个监听器?所以我需要一个方法链,每个方法都从顶级容器向下传递监听器,然后传递到包含组件的直接容器。容器在其上调用addActionListener()。
这里需要记住的重要一点是,一个视图可以充当其他视图的控制器,尽管您可以选择有一系列控制器,允许视图管理这些控制器。
想象一下像“巫师”一样的东西。它有一组从用户那里收集各种信息的步骤,每一个步骤都必须是有效的,然后才能进入下一个步骤。
现在,您可能会想直接将导航集成到其中,但更好的想法是将导航细节作为自己的MVC分离。
让我们用一个被问到很多地方的问题来试一个例子,一个小测验!
一个测验有问题,每个问题有一个提示,一个正确的答案,一系列可能的答案,我们还想存储从用户得到的结果答案。
所以,下面我们有一个MVC测验的基本大纲,我们有一个问题,它是由一个模型管理的,有一个控制器,一个视图和一系列观察者(监听器)
public interface Question {
public String getPrompt();
public String getCorrectAnswer();
public String getUserAnswer();
public String[] getOptions();
public boolean isCorrect();
}
/**
* This is a deliberate choice to separate the update functionality
* No one but the model should ever actually -apply- the answer to the
* question
*/
public interface MutableQuestion extends Question {
public void setUserAnswer(String userAnswer);
}
public interface QuizModel {
public void addQuizObserver(QuizModelObserver observer);
public void removeQuizObserver(QuizModelObserver observer);
public Question getNextQuestion();
public Question getCurrentQuestion();
public int size();
public int getScore();
public void setUserAnswerFor(Question question, String answer);
}
public interface QuizModelObserver {
public void didStartQuiz(QuizModel quiz);
public void didCompleteQuiz(QuizModel quiz);
public void questionWasAnswered(QuizModel model, Question question);
}
public interface QuizView extends View {
public void setQuestion(Question question);
public boolean hasAnswer();
public String getUserAnswer();
public void addQuizObserver(QuizViewObserver observer);
public void removeQuizObserver(QuizViewObserver observer);
}
public interface QuizViewObserver {
public void userDidChangeAnswer(QuizView view);
}
public interface QuizController {
public QuizModel getModel(); // This is the model
public QuizView getView();
public void askNextQuestion();
}
如果您仔细观察,您会注意到视图或模型实际上彼此都没有任何关系。这都是通过控制器来控制的
我在这里做的一件事是为控制器提供一个AskNextQuestion
,因为控制器不知道什么时候会发生这种情况(您可能会考虑使用UserDidChangeAnswer
,但这意味着用户只能尝试一次回答问题,有点意思)
现在,通常情况下,我喜欢使用一些abstract
实现来填充“common”功能,我已经放弃了大部分,直接使用默认实现,这主要是为了演示目的。
public class DefaultQuestion implements MutableQuestion {
private final String prompt;
private final String correctAnswer;
private String userAnswer;
private final String[] options;
public DefaultQuestion(String prompt, String correctAnswer, String... options) {
this.prompt = prompt;
this.correctAnswer = correctAnswer;
this.options = options;
}
@Override
public String getPrompt() {
return prompt;
}
@Override
public String getCorrectAnswer() {
return correctAnswer;
}
@Override
public String getUserAnswer() {
return userAnswer;
}
@Override
public String[] getOptions() {
List<String> list = new ArrayList<>(Arrays.asList(options));
Collections.shuffle(list);
return list.toArray(new String[list.size()]);
}
public void setUserAnswer(String userAnswer) {
this.userAnswer = userAnswer;
}
@Override
public boolean isCorrect() {
return getCorrectAnswer().equals(getUserAnswer());
}
}
public abstract class AbstractQuizModel implements QuizModel {
private List<QuizModelObserver> observers;
public AbstractQuizModel() {
observers = new ArrayList<>(25);
}
@Override
public void addQuizObserver(QuizModelObserver observer) {
observers.add(observer);
}
@Override
public void removeQuizObserver(QuizModelObserver observer) {
observers.remove(observer);
}
protected void fireDidStartQuiz() {
for (QuizModelObserver observer : observers) {
observer.didStartQuiz(this);
}
}
protected void fireDidCompleteQuiz() {
for (QuizModelObserver observer : observers) {
observer.didCompleteQuiz(this);
}
}
protected void fireQuestionWasAnswered(Question question) {
for (QuizModelObserver observer : observers) {
observer.questionWasAnswered(this, question);
}
}
}
public class DefaultQuizModel extends AbstractQuizModel {
private List<MutableQuestion> questions;
private Iterator<MutableQuestion> iterator;
private MutableQuestion currentQuestion;
private boolean completed;
private int score;
public DefaultQuizModel() {
questions = new ArrayList<>(50);
}
public void add(MutableQuestion question) {
questions.add(question);
}
public void remove(MutableQuestion question) {
questions.remove(question);
}
@Override
public Question getNextQuestion() {
if (!completed && iterator == null) {
iterator = questions.iterator();
fireDidStartQuiz();
}
if (iterator.hasNext()) {
currentQuestion = iterator.next();
} else {
completed = true;
iterator = null;
currentQuestion = null;
fireDidCompleteQuiz();
}
return currentQuestion;
}
@Override
public Question getCurrentQuestion() {
return currentQuestion;
}
@Override
public int size() {
return questions.size();
}
@Override
public int getScore() {
return score;
}
@Override
public void setUserAnswerFor(Question question, String answer) {
if (question instanceof MutableQuestion) {
((MutableQuestion) question).setUserAnswer(answer);
if (question.isCorrect()) {
score++;
}
fireQuestionWasAnswered(question);
}
}
}
public class DefaultQuizController implements QuizController {
private QuizModel model;
private QuizView view;
public DefaultQuizController(QuizModel model, QuizView view) {
this.model = model;
this.view = view;
}
@Override
public QuizModel getModel() {
return model;
}
@Override
public QuizView getView() {
return view;
}
@Override
public void askNextQuestion() {
Question question = getModel().getCurrentQuestion();
if (question != null) {
String answer = getView().getUserAnswer();
getModel().setUserAnswerFor(question, answer);
}
question = getModel().getNextQuestion();
getView().setQuestion(question);
}
}
public class DefaultQuizViewPane extends JPanel implements QuizView {
private final JLabel question;
private final JPanel optionsPane;
private final ButtonGroup bg;
private final List<JRadioButton> options;
private String userAnswer;
private final List<QuizViewObserver> observers;
private final AnswerActionListener answerActionListener;
private final GridBagConstraints optionsGbc;
protected DefaultQuizViewPane() {
setBorder(new EmptyBorder(4, 4, 4, 4));
question = new JLabel();
optionsPane = new JPanel(new GridBagLayout());
optionsPane.setBorder(new EmptyBorder(4, 4, 4, 4));
answerActionListener = new AnswerActionListener();
optionsGbc = new GridBagConstraints();
optionsGbc.gridwidth = GridBagConstraints.REMAINDER;
optionsGbc.weightx = 1;
optionsGbc.anchor = GridBagConstraints.WEST;
options = new ArrayList<>(25);
bg = new ButtonGroup();
observers = new ArrayList<>(25);
setLayout(new BorderLayout());
add(question, BorderLayout.NORTH);
add(optionsPane);
}
protected void reset() {
question.setText(null);
for (JRadioButton rb : options) {
rb.removeActionListener(answerActionListener);
bg.remove(rb);
optionsPane.remove(rb);
}
options.clear();
}
@Override
public void setQuestion(Question question) {
reset();
if (question != null) {
this.question.setText(question.getPrompt());
for (String option : question.getOptions()) {
JRadioButton rb = makeRadioButtonFor(option);
options.add(rb);
optionsPane.add(rb, optionsGbc);
}
optionsPane.revalidate();
revalidate();
repaint();
}
}
@Override
public void addQuizObserver(QuizViewObserver observer) {
observers.add(observer);
}
@Override
public void removeQuizObserver(QuizViewObserver observer) {
observers.remove(observer);
}
protected void fireUserDidChangeAnswer() {
for (QuizViewObserver observer : observers) {
observer.userDidChangeAnswer(this);
}
}
protected JRadioButton makeRadioButtonFor(String option) {
JRadioButton btn = new JRadioButton(option);
btn.addActionListener(answerActionListener);
bg.add(btn);
return btn;
}
@Override
public boolean hasAnswer() {
return userAnswer != null;
}
@Override
public String getUserAnswer() {
return userAnswer;
}
@Override
public JComponent getViewComponent() {
return this;
}
protected class AnswerActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
userAnswer = e.getActionCommand();
fireUserDidChangeAnswer();
}
}
}
这里真的没什么特别的。这里唯一感兴趣的是控制器如何管理模型和视图之间的事件
导航API非常基本。它允许您控制用户是否可以实际导航到下一个或上一个元素(如果操作应该对用户可用),以及在任何时候禁用任何一个操作
(同样,我关注的是一个简单的设计,实际上,如果能够控制修改模型的状态以改变导航可以工作的方向,那将是很好的,但我故意把它省略了,以保持简单)
public enum NavigationDirection {
NEXT, PREVIOUS;
}
public interface NavigationModel {
public boolean canNavigate(NavigationDirection direction);
public void addObserver(NavigationModelObserver observer);
public void removeObserver(NavigationModelObserver observer);
public void next();
public void previous();
}
public interface NavigationModelObserver {
public void next(NavigationModel view);
public void previous(NavigationModel view);
}
public interface NavigationController {
public NavigationView getView();
public NavigationModel getModel();
public void setDirectionEnabled(NavigationDirection navigationDirection, boolean b);
}
public interface NavigationView extends View {
public void setNavigatable(NavigationDirection direction, boolean navigtable);
public void setDirectionEnabled(NavigationDirection direction, boolean enabled);
public void addObserver(NavigationViewObserver observer);
public void removeObserver(NavigationViewObserver observer);
}
public interface NavigationViewObserver {
public void next(NavigationView view);
public void previous(NavigationView view);
}
public static class DefaultNavigationModel implements NavigationModel {
private Set<NavigationDirection> navigatableDirections;
private List<NavigationModelObserver> observers;
public DefaultNavigationModel() {
this(true, true);
}
public DefaultNavigationModel(boolean canNavigateNext, boolean canNavigatePrevious) {
navigatableDirections = new HashSet<>(2);
observers = new ArrayList<>(25);
setCanNavigate(NavigationDirection.NEXT, canNavigateNext);
setCanNavigate(NavigationDirection.PREVIOUS, canNavigatePrevious);
}
public void setCanNavigate(NavigationDirection direction, boolean canNavigate) {
if (canNavigate) {
navigatableDirections.add(direction);
} else {
navigatableDirections.remove(direction);
}
}
@Override
public boolean canNavigate(NavigationDirection direction) {
return navigatableDirections.contains(direction);
}
@Override
public void addObserver(NavigationModelObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(NavigationModelObserver observer) {
observers.remove(observer);
}
protected void fireMoveNext() {
for (NavigationModelObserver observer : observers) {
observer.next(this);
}
}
protected void fireMovePrevious() {
for (NavigationModelObserver observer : observers) {
observer.previous(this);
}
}
@Override
public void next() {
fireMoveNext();
}
@Override
public void previous() {
fireMovePrevious();
}
}
public static class DefaultNavigationController implements NavigationController {
private final NavigationModel model;
private final NavigationView view;
public DefaultNavigationController(NavigationModel model, NavigationView view) {
this.model = model;
this.view = view;
view.setNavigatable(NavigationDirection.NEXT, model.canNavigate(NavigationDirection.NEXT));
view.setNavigatable(NavigationDirection.PREVIOUS, model.canNavigate(NavigationDirection.PREVIOUS));
view.addObserver(new NavigationViewObserver() {
@Override
public void next(NavigationView view) {
if (getModel().canNavigate(NavigationDirection.NEXT)) {
getModel().next();
}
}
@Override
public void previous(NavigationView view) {
if (getModel().canNavigate(NavigationDirection.PREVIOUS)) {
getModel().previous();
}
}
});
}
@Override
public NavigationView getView() {
return view;
}
@Override
public NavigationModel getModel() {
return model;
}
@Override
public void setDirectionEnabled(NavigationDirection navigationDirection, boolean enabled) {
getView().setDirectionEnabled(navigationDirection, enabled);
}
}
public static class DefaultNavigationViewPane extends JPanel implements NavigationView {
private final List<NavigationViewObserver> observers;
private final JButton btnNext;
private final JButton btnPrevious;
public DefaultNavigationViewPane() {
btnNext = new JButton("Next >");
btnNext.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireMoveNext();
}
});
btnPrevious = new JButton("< Previous");
btnPrevious.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireMovePrevious();
}
});
setLayout(new FlowLayout(FlowLayout.RIGHT));
add(btnPrevious);
add(btnNext);
observers = new ArrayList<>();
}
@Override
public void addObserver(NavigationViewObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(NavigationViewObserver observer) {
observers.remove(observer);
}
protected void fireMoveNext() {
for (NavigationViewObserver observer : observers) {
observer.next(this);
}
}
protected void fireMovePrevious() {
for (NavigationViewObserver observer : observers) {
observer.previous(this);
}
}
@Override
public JComponent getViewComponent() {
return this;
}
@Override
public void setNavigatable(NavigationDirection direction, boolean navigtable) {
switch (direction) {
case NEXT:
btnNext.setVisible(navigtable);
break;
case PREVIOUS:
btnPrevious.setVisible(navigtable);
break;
}
}
@Override
public void setDirectionEnabled(NavigationDirection direction, boolean enabled) {
switch (direction) {
case NEXT:
btnNext.setEnabled(enabled);
break;
case PREVIOUS:
btnPrevious.setEnabled(enabled);
break;
}
}
}
public interface QuizMasterController {
public QuizController getQuizController();
public NavigationController getNavigationController();
public QuizMasterView getView();
}
public interface QuizMasterView extends View {
public NavigationController getNavigationController();
public QuizController getQuizController();
public void showScoreView(int score, int size);
public void showQuestionAndAnswerView();
}
好吧,所以你可能在问自己一个明显的问题,模型在哪里?嗯,它不需要一个,它只是导航和测验API之间的桥梁,它不管理自己的任何数据...
public class DefaultQuizMasterController implements QuizMasterController {
private QuizController quizController;
private NavigationController navController;
private QuizMasterView view;
public DefaultQuizMasterController(QuizController quizController, NavigationController navController) {
this.quizController = quizController;
this.navController = navController;
view = new DefaultQuizMasterViewPane(quizController, navController);
// Setup the initial state
quizController.askNextQuestion();
navController.getModel().addObserver(new NavigationModelObserver() {
@Override
public void next(NavigationModel view) {
getQuizController().askNextQuestion();
getNavigationController().setDirectionEnabled(NavigationDirection.NEXT, false);
}
@Override
public void previous(NavigationModel view) {
// NOOP
}
});
quizController.getView().addQuizObserver(new QuizViewObserver() {
@Override
public void userDidChangeAnswer(WizeQuiz.QuizView view) {
getNavigationController().setDirectionEnabled(NavigationDirection.NEXT, true);
}
});
quizController.getModel().addQuizObserver(new QuizModelObserver() {
@Override
public void didStartQuiz(QuizModel quiz) {
getView().showQuestionAndAnswerView();
}
@Override
public void didCompleteQuiz(QuizModel quiz) {
getView().showScoreView(quiz.getScore(), quiz.size());
getNavigationController().setDirectionEnabled(NavigationDirection.NEXT, false);
}
@Override
public void questionWasAnswered(QuizModel model, Question question) {
}
});
navController.setDirectionEnabled(NavigationDirection.NEXT, false);
}
@Override
public QuizController getQuizController() {
return quizController;
}
@Override
public NavigationController getNavigationController() {
return navController;
}
@Override
public QuizMasterView getView() {
return view;
}
}
public class DefaultQuizMasterViewPane extends JPanel implements QuizMasterView {
private QuizController quizController;
private NavigationController navController;
private QuestionAndAnswerView qaView;
private ScoreView scoreView;
private CardLayout cardLayout;
public DefaultQuizMasterViewPane(QuizController quizController, NavigationController navController) {
this.quizController = quizController;
this.navController = navController;
quizController.getModel().addQuizObserver(new QuizModelObserver() {
@Override
public void didStartQuiz(QuizModel quiz) {
}
@Override
public void didCompleteQuiz(QuizModel quiz) {
}
@Override
public void questionWasAnswered(QuizModel model, Question question) {
qaView.updateScore();
}
});
scoreView = new ScoreView();
qaView = new QuestionAndAnswerView();
qaView.updateScore();
cardLayout = new CardLayout();
setLayout(cardLayout);
add(qaView, "view.qa");
add(scoreView, "view.score");
}
@Override
public JComponent getViewComponent() {
return this;
}
@Override
public NavigationController getNavigationController() {
return navController;
}
@Override
public QuizController getQuizController() {
return quizController;
}
@Override
public void showScoreView(int score, int size) {
scoreView.updateScore();
cardLayout.show(this, "view.score");
}
@Override
public void showQuestionAndAnswerView() {
cardLayout.show(this, "view.qa");
}
protected class QuestionAndAnswerView extends JPanel {
private JLabel score;
public QuestionAndAnswerView() {
setLayout(new BorderLayout());
add(getQuizController().getView().getViewComponent());
JPanel south = new JPanel(new BorderLayout());
south.add(getNavigationController().getView().getViewComponent(), BorderLayout.SOUTH);
score = new JLabel();
score.setHorizontalAlignment(JLabel.RIGHT);
south.add(score, BorderLayout.NORTH);
add(south, BorderLayout.SOUTH);
}
protected void updateScore() {
score.setText(getQuizController().getModel().getScore() + "/" + getQuizController().getModel().size());
}
}
protected class ScoreView extends JPanel {
private JLabel score;
public ScoreView() {
setLayout(new GridBagLayout());
score = new JLabel("You scored:");
add(score);
}
protected void updateScore() {
score.setText("You scored: " + getQuizController().getModel().getScore() + "/" + getQuizController().getModel().size());
}
}
}
现在,实现有趣了,它实际上有两个“状态”或“视图”,“问答”视图和“分数视图”。这也是故意的,因为我真的不想要另一个MVC。Q&A视图已经在以任何方式管理两个MVC:p
基本上,它所做的是监视测验API当用户更改一个问题的答案时,它然后告诉导航API它可以移动到下一个问题。它还监视开始事件和完成事件,为这些状态呈现所需的视图。
现在,我选择了单独构建每个部分,可以想象,您可以让quizmastercontroller
构建navigation
API本身,因为它知道quiz API只允许向前导航,同样,我们可以更改导航API以允许通过模型或模型更改来修改这些状态,这些都是可行的解决方案,我只是给出了一个直接的示例。
NavigationModel navigationModel = new DefaultNavigationModel(true, false);
NavigationView navigationView = new DefaultNavigationViewPane();
NavigationController navigationController = new NavWiz.DefaultNavigationController(navigationModel, navigationView);
DefaultQuizModel quizModel = new DefaultQuizModel();
quizModel.add(new DefaultQuestion(
"Which pop duo was the first western band to play in The Peoples Republic of China?",
"Wham",
"Wham", "Simon and Garfunkel", "Chas and Dave", "Right Said Fred"));
quizModel.add(new DefaultQuestion(
"Timber selected from how many fully grown oak trees were needed to build a large 3 decker Royal Navy battle ship in the 18th century?",
"3,500",
"50", "500", "1,500", "3,500"));
quizModel.add(new DefaultQuestion(
"Speed skating originated in which country?",
"Netherlands",
"Russia", "Netherlands", "Canada", "Norway"));
quizModel.add(new DefaultQuestion(
"Off the coast of which country did the Amoco Cadiz sink?",
"France",
"South Africa", "France", "USA", "Spain"));
quizModel.add(new DefaultQuestion(
"The song 'An Englishman in New York' was about which man?",
"Quentin Crisp",
"Quentin Crisp", "Sting", "John Lennon", "Gordon Sumner"));
QuizView quizView = new DefaultQuizViewPane();
QuizController quizController = new DefaultQuizController(quizModel, quizView);
QuizMasterController quizMasterController = new DefaultQuizMasterController(quizController, navigationController);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(quizMasterController.getView().getViewComponent());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
最后我们的结局是...
如果不是粗略的话,这并不是什么,但它旨在为您如何完成复杂的复合MVC提供一些想法
我已经使用Spring Kafka创建了一个Kafka消费者,并将其部署在云铸造中。该主题有10个分区。我计划将应用程序扩展到10个实例,以便每个实例可以使用来自一个分区的消息。Spring Kafka支持并发消息侦听器容器,我猜它支持从每个分区创建多个线程来使用。例如,如果我有5个消费者实例,每个消费者实例可能有2个线程从分区消耗。因为我计划为每个分区创建一个应用实例,所以使用并发消费者有什么好
问题内容: 有人知道在python中跟踪字典对象更改的任何简便方法吗?我的工作水平很高,所以我有一些方法可以处理更改字典的操作,如果字典发生更改,我想调用一个函数来基本上执行Observer / Notify。 我要避免的是所有跟踪(设置布尔值)代码。希望有一种更轻松的方式来跟踪更改。这是一个简单的情况,但是可能存在更复杂的逻辑,这将导致我不得不设置更改的标志。 问题答案: 您可以从该类派生并在任
问题内容: 每当用户在其android设备上插入错误的锁定模式时,我都试图找到一种接收消息的方法。老实说,我对如何实现这一目标一无所知,但我想我应该在注册为侦听器的后台提供某种服务。但是我应该在哪个广播员上注册我的收听者? 很抱歉,我没有合适的术语,可能我的文字没有多大意义……但是基本上,我需要构建一个小类,每当插入错误的锁定模式时,该类就执行一个动作。 我并不需要完整的代码,我只需要一个小例子t
我有一个控制器类,它需要为列表中的每个项目执行各种异步操作: 对于每个A 播放音频并等待完成 等t秒 对于A的每个子部分B: 播放音频并等待 等待t2秒 当我的方法被激发。我试着回复听众: 但最后我还是无法摆脱内部循环,这让我觉得整个想法都行不通。我已经查看了未来的界面,但这似乎也不是我想要的。 我知道我可以用一堆变量来解决这个问题,以跟踪我在状态流中的位置,但是带有一些框架的循环会更干净。谢谢:
嘿,当我运行应用程序时,它会给出一个错误java.lang.IllegalArgumentException:Invalid listener:null,这说明侦听器是空的。我是初学者,所以请大家帮忙解决这个问题。在这一行中出现错误:LocationManager.RequestLocationUpdates(provider,2000,0,locationListener);//这里是我的示例代
问题内容: 我正在使用https://github.com/gin-gonic/gin编写http服务,但是当我部署它时,它会继续部署在tcp6上(根据netstat) 问题答案: 文档说明 运行将路由器连接到http.Server并开始侦听和处理HTTP请求。这是http.ListenAndServe(addr,router)的快捷方式 您可以使用与软件包相同的方式直接启动服务器