1

我正在尝试使用 android 中的库连接到终端仿真器,这将连接到串行设备并应该向我显示发送/接收的数据。在这两种情况下,我应该能够通过终端下方的文本框或通过输入终端本身并在键盘上按 Enter 来通过连接发送数据。

当我发送数据时,对我的命令的回复会从库中返回到方法中onDataReceived(int id, byte[] data)。每次我取回数据时,它都会自动运行。我可以看到到达日志的数据。

我的问题是,每当数据从串行设备返回时,屏幕不会更新,直到我将文本框置于焦点,或按下键盘上的 Enter 按钮。每当收到任何内容时,如何进行屏幕更新。我知道它正在接收数据,当我按下文本框或输入按钮时,将显示所有接收到的数据。

这是我收到数据时调用的内容,aByteArrayInputStream用新数据更新。 appendToEmulator直接向终端仿真器写入内容。notifyUpdate让模拟器知道屏幕已经改变。

public void onDataReceived(int id, byte[] data)
{
    String str = new String(data);
    Log.d(TAG, "in data received " + str);
    ((MyBAIsWrapper)bis).renew(data);

    mSession.appendToEmulator(data, 0, data.length);
    mSession.notifyUpdate();
}

这是我发送一些东西的地方:

mEntry = (EditText) findViewById(R.id.term_entry);
mEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() {

        @Override
        public boolean onEditorAction(TextView v, int actionId,
                KeyEvent event) {

            /* Ignore enter-key-up events. */
            if (event != null && event.getAction() == KeyEvent.ACTION_UP) {

                return false;
            }


            /* Don't try to send something if we are not connected yet. */
            TermSession session = mSession;

            if (mSession == null) {
            Log.d(TAG, "null session ");
            return true;

             }

            Log.d(TAG, "in click event ");
            Editable e = (Editable) v.getText();

            // call original sendData to send data over serial
            String data = e.toString() + "\r\n";
            Log.d(TAG, "edittext data is  " + data);
            //overridden sendData
            sendData(data.getBytes());

            // send data over serial using original sendData() method
            mSelectedAdapter.sendData(data.getBytes());
            TextKeyListener.clear(e);
            return true;
        }
    });

我可以做些什么来实时更新屏幕,而不仅仅是更新用户操作?

编辑:我认为 invalidate() 是我需要的,但它不起作用。我在 emulatorView 上调用它。emulatorView 显示终端模拟器的屏幕,是库中的一个类,继承自视图http://pastebin.com/MNJ0Zf8P

编辑:所以使整个视图无效没有任何作用,但我注意到当我使 mEmulatorView 无效时,我解锁它时屏幕会更新。也是第一次更新后,我可以点击终端本身,它会更新。进一步的水龙头什么也不做。很奇怪,所以它显然是在改变行为,但是当我调用它时它并没有自动做任何事情。

编辑:处理程序如何工作而调用它却没有?

编辑,我的全部活动:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;

import slickdevlabs.apps.usb2seriallib.AdapterConnectionListener;
import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial;
import slickdevlabs.apps.usb2seriallib.USB2SerialAdapter;
import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial.BaudRate;
import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial.DataBits;
import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial.ParityOption;
import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial.StopBits;
import slickdevlabs.apps.usb2seriallib.USB2SerialAdapter.DataListener;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.method.TextKeyListener;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import jackpal.androidterm.emulatorview.EmulatorView;
import jackpal.androidterm.emulatorview.TermSession;

public class SerialTerminalActivity extends Activity implements
    OnClickListener, OnItemSelectedListener, AdapterConnectionListener,
    DataListener {

private static final String TAG = "SerialTerminalActivity";
private EditText mEntry;
private EmulatorView mEmulatorView;
private TermSession mSession;
private OutputStream bos;
private InputStream bis;
private InputStream in;
private OutputStream out;
private Spinner mBaudSpinner;
private Spinner mDataSpinner;
private Spinner mParitySpinner;
private Spinner mStopSpinner;
private Spinner mDeviceSpinner;
private Button mConnect;
private ArrayList<String> mDeviceOutputs;
private ArrayList<USB2SerialAdapter> mDeviceAdapters;
private ArrayAdapter<CharSequence> mDeviceSpinnerAdapter;
private USB2SerialAdapter mSelectedAdapter;
private TextView mCurrentSettings;
boolean attached = false;

private Button mUpdateSettings;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_serial_terminal);

    mConnect = (Button) findViewById(R.id.deviceConnect);
    mConnect.setOnClickListener(this);
    mUpdateSettings = (Button) findViewById(R.id.updateSettings);
    mUpdateSettings.setOnClickListener(this);

    mBaudSpinner = (Spinner) findViewById(R.id.baudSpinner);
    ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
            this, android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    mBaudSpinner.setAdapter(adapter);
    String[] tempArray = SlickUSB2Serial.BAUD_RATES;
    for (int i = 0; i < tempArray.length; i++) {
        adapter.add(tempArray[i]);
    }
    mBaudSpinner.setSelection(SlickUSB2Serial.BaudRate.BAUD_9600.ordinal());

    mDataSpinner = (Spinner) findViewById(R.id.dataSpinner);
    adapter = new ArrayAdapter<CharSequence>(this,
            android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    mDataSpinner.setAdapter(adapter);
    tempArray = SlickUSB2Serial.DATA_BITS;
    for (int i = 0; i < tempArray.length; i++) {
        adapter.add(tempArray[i]);

    }
    mDataSpinner
            .setSelection(SlickUSB2Serial.DataBits.DATA_8_BIT.ordinal());

    mParitySpinner = (Spinner) findViewById(R.id.paritySpinner);
    adapter = new ArrayAdapter<CharSequence>(this,
            android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    mParitySpinner.setAdapter(adapter);
    tempArray = SlickUSB2Serial.PARITY_OPTIONS;
    for (int i = 0; i < tempArray.length; i++) {
        adapter.add(tempArray[i]);

    }
    mParitySpinner.setSelection(SlickUSB2Serial.ParityOption.PARITY_NONE
            .ordinal());

    mStopSpinner = (Spinner) findViewById(R.id.stopSpinner);
    adapter = new ArrayAdapter<CharSequence>(this,
            android.R.layout.simple_spinner_item);
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    mStopSpinner.setAdapter(adapter);
    tempArray = SlickUSB2Serial.STOP_BITS;
    for (int i = 0; i < tempArray.length; i++) {
        adapter.add(tempArray[i]);

    }
    mStopSpinner
            .setSelection(SlickUSB2Serial.StopBits.STOP_1_BIT.ordinal());

    mDeviceAdapters = new ArrayList<USB2SerialAdapter>();
    mDeviceOutputs = new ArrayList<String>();

    mDeviceSpinner = (Spinner) findViewById(R.id.deviceSpinner);
    mDeviceSpinnerAdapter = new ArrayAdapter<CharSequence>(this,
            android.R.layout.simple_spinner_item);
    mDeviceSpinnerAdapter
            .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    mDeviceSpinner.setAdapter(mDeviceSpinnerAdapter);
    mDeviceSpinner.setOnItemSelectedListener(this);

    mCurrentSettings = (TextView) findViewById(R.id.currentSettings);

    SlickUSB2Serial.initialize(this);

    /*
     * Text entry box at the bottom of the activity. Note that you can also
     * send input (whether from a hardware device or soft keyboard) directly
     * to the EmulatorView.
     */
    mEntry = (EditText) findViewById(R.id.term_entry);
    mEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() {

        @Override
        public boolean onEditorAction(TextView v, int actionId,
                KeyEvent event) {

            /* Ignore enter-key-up events. */
            if (event != null && event.getAction() == KeyEvent.ACTION_UP) {

                return false;
            }


            /* Don't try to send something if we are not connected yet. */
            TermSession session = mSession;

            if (mSession == null) {
            Log.d(TAG, "null session ");
            return true;

             }

            Log.d(TAG, "in click event ");
            Editable e = (Editable) v.getText();

            // call original sendData to send data over serial
            String data = e.toString() + "\r\n";
            Log.d(TAG, "edittext data is  " + data);
            //doLocalEcho(data.getBytes());
            sendData(data.getBytes());

            // send data over serial using original sendData() method
            mSelectedAdapter.sendData(data.getBytes());

            /* Write to the terminal session. */
            session.write(e.toString());
            //Log.d(TAG, "edittext to string is  " + editText.toString());
            session.write('\r');
            TextKeyListener.clear(e);
            return true;
        }
    });

    /*
     * Sends the content of the text entry box to the terminal, without
     * sending a carriage return afterwards
     */
    Button sendButton = (Button) findViewById(R.id.term_entry_send);
    sendButton.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {

            /* Don't try to send something if we are not connected yet. */
            TermSession session = mSession;
            if (mSession == null) {
                Log.d(TAG, "mSession == NULLLLLLLLLLLLL ");
                return;
            }

            Editable editText = (Editable) mEntry.getText();
            session.write(editText.toString());
            Log.d(TAG, "edittext is " + editText.toString());
            TextKeyListener.clear(editText);
            Log.d(TAG, "send pressed ");
        }
    });

    /**
     * EmulatorView setup.
     */

    /* emulatorView from xml. */
    EmulatorView view = (EmulatorView) findViewById(R.id.emulatorView);
    mEmulatorView = view;

    /* Let the EmulatorView know the screen's density. */
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    view.setDensity(metrics);

    /* Create a TermSession. */
    // TermSession session = mSession;
    mSession = new TermSession();

    //byte[] a=new byte[]{1,1,1};
    byte[] a = new byte[]{'h','e', 'l', 'l', 'o'};
    byte[] b = new byte[4096];
    //bis = new ByteArrayInputStream(a);
    bis = new MyBAIsWrapper(b);
    bos = new ByteArrayOutputStream();
    mSession.write("testTWO");
    //bis = new ByteArrayInputStream(b);
    mSession.setTermIn(bis);
    mSession.setTermOut(bos);
    //session.setTermIn(in);
    //session.setTermOut(out);

    mSession.write("testONE");


    /* Attach the TermSession to the EmulatorView. */
    mEmulatorView.attachSession(mSession);

    //mSession = session;
//  mSession.write("abc");
    //session.write("test");
    try {
        bos.write(b);
        bos.flush();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    /* TODO Monday:  ByteArrayInputStream() can only be used once.  The data in it at creation is all that'll ever be in.
     *               Find a way to update what bis is pointing to without breaking the bind that bis has to the terminal.
     *               Recover from Saturday's hangover.
     */


    /*
     * That's all you have to do! The EmulatorView will call the attached
     * TermSession's initializeEmulator() automatically, once it can
     * calculate the appropriate screen size for the terminal emulator.
     */

}

public void sendData(byte[] data) {
    String str = new String(data);
    Log.d(TAG, "send data method value is: " + str);

    // this should echo what I send to the terminal in the correct format
    //bos = new ByteArrayOutputStream(data.length);
    mSession.write(data, 0, data.length);
    try {
        bos.write(data);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        Log.d(TAG, "EXCEPTION in data sent ");
    }
    // mSession.write(data, 0, data.length);
    // mSession.write('\r');

}

public void onDataReceived(int id, byte[] data) {

    String str = new String(data);
    Log.d(TAG, "in data received " + str);
      ((MyBAIsWrapper)bis).renew(data);

     mSession.appendToEmulator(data, 0, data.length);
     mSession.notifyUpdate();
     //mEmulatorView.invalidate();
     mEmulatorView.postInvalidate();
   /* bis = new ByteArrayInputStream(data);
    SerialTerminalActivity.this.runOnUiThread(new Runnable() {
        public void run() {

              serialSession();
        }
      });*/

     //cast added to keep original code structure 
    //I recommend defining the bis attribute as the MyBAIsWrapper type in this case
   // ((MyBAIsWrapper)bis).renew(data);


    //mSession.write(data, 0, data.length);
    //mSession.write('\r');

}

public void serialSession() {
    Log.d(TAG, "in serial session");
    mSession.setTermIn(bis);
    mSession.setTermOut(bos);
    /* Attach the TermSession to the EmulatorView. */
    mEmulatorView.attachSession(mSession);
}

@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
        long id) {
    // TODO Auto-generated method stub
    changeSelectedAdapter(mDeviceAdapters.get(position));
}

@Override
public void onNothingSelected(AdapterView<?> arg0) {
    // TODO Auto-generated method stub
}

public void changeSelectedAdapter(USB2SerialAdapter adapter) {
    Toast.makeText(this, "in changeselectedadapter", Toast.LENGTH_SHORT)
            .show();
    // if(mSelectedAdapter!=null){
    // mDeviceOutputs.set(mDeviceSpinnerAdapter.getPosition(mSelectedAdapter.getDeviceId()+""),mReceiveBox.getText().toString());

    mSelectedAdapter = adapter;
    mBaudSpinner.setSelection(adapter.getBaudRate().ordinal());
    mDataSpinner.setSelection(adapter.getDataBit().ordinal());
    mParitySpinner.setSelection(adapter.getParityOption().ordinal());
    mStopSpinner.setSelection(adapter.getStopBit().ordinal());

    updateCurrentSettingsText();

    // mReceiveBox.setText(mDeviceOutputs.get(mDeviceSpinner.getSelectedItemPosition()));
    Toast.makeText(this,
            "Adapter switched toooo: " + adapter.getDeviceId() + "!",
            Toast.LENGTH_SHORT).show();
}

@Override
public void onAdapterConnected(USB2SerialAdapter adapter) {
    adapter.setDataListener(this);
    mDeviceAdapters.add(adapter);
    mDeviceOutputs.add("");
    mDeviceSpinnerAdapter.add("" + adapter.getDeviceId());
    mDeviceSpinner.setSelection(mDeviceSpinnerAdapter.getCount() - 1);

    Toast.makeText(this,
            "Adapter: " + adapter.getDeviceId() + " Connected!",
            Toast.LENGTH_SHORT).show();
    // Toast.makeText(this, "Baud: "+adapter.getBaudRate()+" Connected!",
    // Toast.LENGTH_SHORT).show();
}

@Override
public void onAdapterConnectionError(int error, String msg) {
    // TODO Auto-generated method stub
    if (error == AdapterConnectionListener.ERROR_UNKNOWN_IDS) {
        final AlertDialog dialog = new AlertDialog.Builder(this)
                .setIcon(0)
                .setTitle("Choose Adapter Type")
                .setItems(new String[] { "Prolific", "FTDI" },
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog,
                                    int optionSelected) {
                                if (optionSelected == 0) {
                                    SlickUSB2Serial
                                            .connectProlific(SerialTerminalActivity.this);
                                } else {
                                    SlickUSB2Serial
                                            .connectFTDI(SerialTerminalActivity.this);
                                }
                            }
                        }).create();
        dialog.show();
        return;
    }
    Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}

public void onClick(View v) {

    if (v == mConnect) {
        SlickUSB2Serial.autoConnect(this);
        if (mSelectedAdapter == null) {
            Toast.makeText(this, "no adapters detected", Toast.LENGTH_SHORT)
                    .show();
            return;
        }
    }

    else if (v == mUpdateSettings) {
        if (mSelectedAdapter == null) {
            return;
        }

        mSelectedAdapter.setCommSettings(BaudRate.values()[mBaudSpinner
                .getSelectedItemPosition()], DataBits.values()[mDataSpinner
                .getSelectedItemPosition()],
                ParityOption.values()[mParitySpinner
                        .getSelectedItemPosition()],
                StopBits.values()[mStopSpinner.getSelectedItemPosition()]);

        updateCurrentSettingsText();
        Toast.makeText(this, "Updated Settings", Toast.LENGTH_SHORT).show();

    }

}

private void updateCurrentSettingsText() {
    mCurrentSettings.setText("Current Settings Areeee: "
            + mBaudSpinner.getSelectedItem().toString() + ", "
            + mDataSpinner.getSelectedItem().toString() + ", "
            + mParitySpinner.getSelectedItem().toString() + ", "
            + mStopSpinner.getSelectedItem().toString());
}



/* Echoes local input from the emulator back to the emulator screen. */
private void doLocalEcho(byte[] data) {

        Log.d(TAG, "echoing " +
                Arrays.toString(data) + " back to terminal");
    //I added mSession, is it right?
    mSession.appendToEmulator(data, 0, data.length);
    mSession.notifyUpdate();
}



@Override
protected void onResume() {
    super.onResume();

    /*
     * You should call this to let EmulatorView know that it's visible on
     * screen.
     */
    mEmulatorView.onResume();

    mEntry.requestFocus();
}

@Override
protected void onPause() {
    /*
     * You should call this to let EmulatorView know that it's no longer
     * visible on screen.
     */
    mEmulatorView.onPause();

    super.onPause();
}

@Override
protected void onDestroy() {
    /**
     * Finish the TermSession when we're destroyed. This will free
     * resources, stop I/O threads, and close the I/O streams attached to
     * the session.
     * 
     * For the local session, closing the streams will kill the shell; for
     * the Telnet session, it closes the network connection.
     */
    if (mSession != null) {
        mSession.finish();
    }
    SlickUSB2Serial.cleanup(this);
    super.onDestroy();
}

}
4

2 回答 2

2

当您想要更新视图时,您需要使其无效。从 UI 线程使用invalidate(),从非 UI 线程使用postInvalidate()

于 2012-12-20T10:52:58.523 回答
0

我尝试了处理程序并且它有效:

Handler viewHandler = new Handler();
Runnable updateView = new Runnable(){
  @Override
  public void run(){
     mEmulatorView.invalidate();
     viewHandler.postDelayed(updateView, 10);
  }
};

viewHandler.post(updateView);然后打电话 onDataReceived()

这每 10 毫秒更新一次屏幕,并且有效,除非我将终端置于焦点,否则它不会更新,直到我点击发送或点击editText字段,这是为什么呢?

另外为什么处理程序工作但在方法本身中调用它却没有?我从来没有真正使用过处理程序,所以我不确定。

于 2012-12-20T16:30:57.607 回答