Execute shell commands and get output in a TextVie

2020-06-19 19:07发布

问题:

I want to execute some shell commands and get the output in a TextView. The command may have a continuous output like ping or logcat. Also, the TextView should scroll automatically as the command output is added in real-time. In order to do so, I have done this:

package com.example.rootapp;

import java.io.DataOutputStream;
import java.io.InputStream;

import android.app.Activity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.widget.TextView;

public class MainActivity extends Activity {
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String[] cmdArray={"logcat -b radio"};
        try {
            runAsRoot(cmdArray);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void runAsRoot(String[] cmds) throws Exception {
        tv=(TextView)findViewById(R.id.cmdOp);
        tv.setMovementMethod(new ScrollingMovementMethod());
        Process p = Runtime.getRuntime().exec("su");
        DataOutputStream os = new DataOutputStream(p.getOutputStream());
        InputStream is = p.getInputStream();
        for (String tmpCmd : cmds) {
            os.writeBytes(tmpCmd+"\n");
            int readed = 0;
            byte[] buff = new byte[4096];
            boolean cmdRequiresAnOutput = true;
            if (cmdRequiresAnOutput) {
                while( is.available() <= 0) {
                    try { Thread.sleep(5000); } catch(Exception ex) {}
                }

                while( is.available() > 0) {
                    readed = is.read(buff);
                    if ( readed <= 0 ) break;
                    String seg = new String(buff,0,readed);   
                    tv.append(seg);
                }
            }
        }        
    }
}

This works fine, but it does not update the TextView continuously. As you can see, I'm executing radio logcat as root user, the output is generated continuously (already checked in terminal emulator and adb shell). But in my case, the output is not added continuously. It stops after a few 100 lines. Here is my layout:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/cmdOp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="bottom"
        android:text="@string/hello_world" />
</ScrollView>

</RelativeLayout>

The output should keep scrolling the TextView. At least this is what I expect...... Any workaround for this please?

回答1:

You can run command and display command output into text as below :

public class MainActivity extends Activity {

    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv=(TextView)findViewById(R.id.cmdOp);
        tv.setText("Output :"+"\n"+runAsRoot());
    }

    public String runAsRoot() {

        try {
            // Executes the command.
            Process process = Runtime.getRuntime().exec("ls -l");

            // Reads stdout.
            // NOTE: You can write to stdin of the command using
            //       process.getOutputStream().
            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));

            int read;
            char[] buffer = new char[4096];
            StringBuffer output = new StringBuffer();
            while ((read = reader.read(buffer)) > 0) {
                output.append(buffer, 0, read);
            }
            reader.close();

            // Waits for the command to finish.
            process.waitFor();

            return output.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

Note : The "su" command does only run if the device is rooted. Otherwise it throws an exception.



回答2:

I think the problem is that you wait for 5s to start reading the output and when the program starts it reaches the end of the stream because it is faster than the speed the log is generated. Thus you need to wait again for some time and start reading the buffer again. The following code works for me. I use ArrayAdapter + ListView instead of TextView. If we set m_bTerminate to true then the thread dies next time it reaches the end of the input stream.

Process p = Runtime.getRuntime().exec(m_command);
InputStream is = p.getInputStream();

while(!m_bTerminate) {
    while(is.available() <= 0)
    {
        try{ Thread.sleep(1000, 0); } catch(Exception e) {m_log.e(e);}
    }

    byte[] buff = new byte[4096];
    int read = is.read(buff);
    if(0 < read) {
        m_List.add(new String(buff, 0, read));
        m_handler.sendEmptyMessage(Handler.MSG_RADIO_LOG_NOTIFY_DATASET_CHANGED);
        //if we call notify datasetchanged directly it raises
        //android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views
        //m_Adapter.notifyDataSetChanged();
    }
}

m_log.p(TAG, Log.DEBUG, "Command executed. Destroying thread.");
is.close();
p.destroy();


回答3:

Take this String function and store the result into your TextView or wherever you want

public String shell_exec(String cmd)
     {
     String o=null;
     try
       {
       Process p=Runtime.getRuntime().exec(cmd);
       BufferedReader b=new BufferedReader(new InputStreamReader(p.getInputStream()));
       String line;
       while((line=b.readLine())!=null)o+=line;
       }catch(Exception e){o="error";}
     return o;
     }

Example of usage:

mytextview.setText(shell_exec("ls -l"));