【问题标题】:Android: Create new object and manipulate/call new methods serially with SynchronizedAndroid:使用 Synchronized 串行创建新对象并操作/调用新方法
【发布时间】:2016-05-04 15:26:50
【问题描述】:

我正在尝试让用户创建一个新的 Flow 对象并将其添加到 ArrayList 以在他们按下工具栏上的 "+" 时进行跟踪。

我正在为 Java 的多线程而苦苦挣扎,因为我的方法需要对象及其属性,在对象实例化之前就已经运行,导致各种问题

我希望我的方法连续执行(即显示对话框、获取名称、使用对象构造函数、将新对象添加到列表),这就是为什么我尝试对我声明的对象使用 Synchronized 操作但没有实例化。

此策略似乎不起作用,因为锁定的对象不能为空。

java.lang.NullPointerException: Null reference used for synchronization (monitor-enter)

关于如何让我的方法像这个伪代码一样串行运行的任何想法:

private Flow newFlow; //Blank flow object declared.
private static List<Flow> flowsInStream = new ArrayList<Flow>();

synchronized (newFlow) {
  flowDialog(); 
     // presents user a dialog box to receive input. 
     // takes user input, invokes separate method to actually instantiate 
     // the newFlow object using the user input. 
     // Originally blank newFlow object now has: 
        // newFlow.name = userInput
     // --X END X--
  addToStream(newFlow);
     // adds the newly instantiated newFlow object to the flowsInStream
     // array to keep track of them. 
     // --X END X-- 
  executedCorrectly(); 
     // displays log message showing both the newFlow.name & the current 
     // elements in the flowsInStream array. 
     // --X END X-- 
 } // end of synchronized

TheStream.java

public class TheStream extends AppCompatActivity {

    private static final String TAG = TheStream.class.getName();
    private Toolbar streamToolbar;
    private Flow theFlow; //Blank flow object declared.

    private static List<Flow> flowsInStream = new ArrayList<Flow>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_the_stream);

        streamToolbar = (Toolbar) findViewById(R.id.streamToolbar);
        setSupportActionBar(streamToolbar);
    }

   @Override
    public boolean onPrepareOptionsMenu(final Menu menu) {
        getMenuInflater().inflate(R.menu.menu_thestream, menu);

        return super.onCreateOptionsMenu(menu);
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case R.id.action_settings:
                // User chose the "Settings" item, show the app settings UI...
                return true;

            case R.id.action_newFlow:

                    flowDialog();
                    addToStream(theFlow);
                    executedCorrectly();

                return true;

            default:
                // If we got here, the user's action was not recognized.
                // Invoke the superclass to handle it.
                return super.onOptionsItemSelected(item);

        }
    }

    public void flowDialog() {
        //Creates dialog box asking for name for the new flow
        AlertDialog.Builder newFlowDialog = new AlertDialog.Builder(TheStream.this);

        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT);
        params.setMarginStart(70);
        params.setMarginEnd(150);

        //Create edit text field for name entry
        final EditText nameInputET = new EditText(TheStream.this);

        //Sets maximum length of the EditText
        nameInputET.setFilters(new InputFilter[]{new InputFilter.LengthFilter(30)});

        //Adds the ET and params to the layout of the dialog box
        layout.addView(nameInputET, params);

        newFlowDialog.setTitle("Name your new Flow.");
        newFlowDialog.setIcon(R.drawable.new_flow);

        newFlowDialog.setView(layout);


        newFlowDialog.setPositiveButton("Lets Roll",
                new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int whichButton) {
                        if (nameInputET.getText().toString().equals("")) {

                            Toast.makeText(TheStream.this, "Every Flow deserves a good name :(", Toast.LENGTH_LONG).show();

                            flowDialog(); //Recall the dialog

                        } else {
                            // Sets name of flow object
                            theFlow = instantiateFlow(nameInputET.getText().toString());
                        }
                    }
                });

        newFlowDialog.setNegativeButton("Nevermind",
                new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int whichButton) {
                        dialog.dismiss();
                    }
                });

        //Display Alert
        newFlowDialog.show();

    }

    protected Flow instantiateFlow(String userInput) {
        //Instantiates (Constructor) the newFlow object.

        Flow newFlow = new Flow(userInput);
        Log.d(TAG, "Your flow's name is " + newFlow.getFlowName());
         /** Returns errors attached below */
        return newFlow;
    }

    public void addToStream(Flow flow) {
        flowsInStream.add(flow);
    }

    public void executedCorrectly() {
        Log.d(TAG, "The synchronized activity executed correctly because the new Flow object's name is " + theFlow.getFlowName());
        Log.d(TAG, "The new Flow list is also updated check it out: " + flowsInStream);
    }
}

Flow.java

public class Flow {

    private String flowName;

    public Flow() {

    } // End of default constructor

    public Flow(String flowName) {
        this.flowName = flowName;
    } // End of constructor

    /** Getters & Setters **/
    public void setFlowName(String flowName) {
        this.flowName = flowName;
    }
    public String getFlowName() {
        return this.flowName;
    }

如果有任何其他代码有帮助,请告诉我,我很乐意发布一些代码。如果可能的话,请在您的回答中提及我在尝试此操作时缺乏技术理解的地方。

收到错误:

java.lang.NullPointerException: Attempt to invoke virtual method 
'java.lang.String nhacks16.flow.Main.Flow.getFlowName()' on a null object 
reference

【问题讨论】:

    标签: java android multithreading oop


    【解决方案1】:

    Yoy 正在使用synchronized (newFlow),而newFlow 仍然是null。您不能在 null 引用上使用 synchronized。如果您真的想同步,请创建一个不同的Object(任何Object 都可以)并在那个上同步,或者在this 上同步(只使用不带括号的synchronized {)。哪一个是正确的,取决于你要防范什么样的并行性,这就引出了下一点:

    我没有看到任何多线程,所以我不确定你是否需要同步。

    【讨论】:

    • 您好,masov,感谢您的回复。如果我不使用同步,如何使方法串行运行?我希望我的第二种方法(addToStream)等到第一种方法(flowDialog)完成后再执行。
    • @RobertSimoes:如果您不使用多线程,它们将“串行”执行而无需执行任何操作。是什么让您认为正在发生多线程?
    • 最初我只是将方法一个接一个地放置,没有同步块,我会得到日志消息:“同步活动正确执行,因为新 Flow 对象的名称是 null “我认为这意味着日志消息在使用 flowDialog() 实例化对象之前获取了 newFlow.name。
    • @RobertSimoes:我认为这与多线程无关。但是为了让我们能够弄清楚,为什么会这样,请发布相关代码。
    • @RobertSimoes:据我所知,theFlow 成为null 的唯一方法是点击“否定按钮”。在这种情况下,theFlow 永远不会被初始化,但方法executedCorrectly 无论如何都会执行,因此访问的是null 对象。
    【解决方案2】:

    @mastov 完全正确,因为代码中似乎没有任何多线程,而我的 newFlow 对象是null。但我只是想在阅读他的 cmets 后澄清我自己的技术错误,我的一个朋友指出了这一点,以防其他人发现有用!

    我的印象是对话框冻结所有活动(即方法:addToStream(theFlow);executedCorrectly();将等到flowDialog()finished 之前执行自己)。

    因此,由于这些方法似乎在对话框消失之前执行,我的印象是它们在不同的线程上运行。

    nullPointerException 的原因是在用户单击按钮并输入文本之前,对象没有实例化。所以一旦设置了flowDialog,下一个方法就会运行,但是因为流对象没有实例化它抛出了null异常!

    【讨论】:

    • +1 谢谢,我在分析您的代码时也遇到了同样的问题!对Dialog.show() 的非阻塞调用有点违反直觉,恕我直言。
    猜你喜欢
    • 2021-12-21
    • 2023-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多