question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Hi @topjohnwu

First of all, thanks for your hard work! Building a library of this kind is not easy and require a lot of time. As you ask, I posted here some thoughts about the library. I studied it when release (so before you left for your military training) and I had some concerns by the time. I did not want to bother you with it. But I am happy to see it received a lot of improvements since! 😄

I am the maintainer of AdAdaway, a root adblocker (https://github.com/AdAway/AdAway) and I had a quick look to see I could migrate from RootCommands (https://github.com/Free-Software-for-Android/RootCommands) to your library (I maintain a fork as a module of AdAway). So for my needs, I see some interesting additions:

  • I could create dedicated shells for long running tasks (I run web server and tcpdump processes in background) without blocking other shell commands,
  • I seems I can have a working shell without root (if a device is not rooted, I can still provide some features without crashing my app),
  • Busybox in included so device support should be good enough.

But compared to RootCommands, there is no concept of command itself: all commands are strings. It was useful to have it: to set parameter, chain them, get error code, etc…


So I read through the code of latest version and I have few questions and remarks. Keep in mind I am not here to point some bad things in the code, just to start a discussion about the question you ask me 😉

In my opinion Shell.Sync and Shell.Async methods looks weird. First, varargs and List are mixed. Then required parameter, commands, is not at the same place for each sh, su, loadScript variant. It could be the first parameter so it will be consistent to every other overloaded methods. The output parameters, output and error lists, deserved a dedicated structure as a return type. Doing so, List<String> su(String...), su(List<String>, String...) and su(List<String>, List<String>, String...) could be merged into only one method like Output su(String...) where Output contains twos List<String>, output and error, which may be empty.

Creating an Output class could also lead to move String cleanup like ShellUtils.isValidOutput to this class. Example: output or error list merged in one line, get the last line, etc…

And what about error code? Token is already used to detect the end of a command. It could be also printed and retrieved like it is done in boolean ShellUtils.fastCmdResult(Shell shell, String cmd).

About Shell.Initializer, onShellInit and onRootShellInit could be the same. Just look at the given shell in parameter to check if it is root or not (with getStatus() for example or a dedicated function like isRoot()). Once cleaned, we could have a functional interface as shell initializer (BiPredicate<Context, Shell>). Same from throwing an exception, the documentation said “it is the same as returning false”. As duplicate, it could be removed.

Same about Shell.Task, is throwing Exception redundant with stderr? Thrown exceptions in task look like to be related to I/O internals and implementation details. May be the user should not be aware of this kind of error and the exception message should be added to stderr instead? (while stack trace could stil be logged) Because if the user (ie the app developer) can’t do anything about this exception, it seems the library should in charge of handling it.

And, at last, it is usually recommended to return empty collections or arrays instead of null. See Item 54 of Josh Bloch’s Effective Java. About the “all the commands are String”, I would recommend to read the Item 62 (Avoid String where other types are more appropriate), and “varargs and list”, check Item 53 (Use varargs judiciously) because the API allow the call Shell.Sync.su() (without a command). Even if it should work, do we really want it?


I still have a lot of questions when I start reading the implementation details, specially about threading, locking, input/output stream encoding, etc…, but it is already late here and I guess this issue is already long enough! Anyway, thanks for your work! 👍

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:3
  • Comments:20 (15 by maintainers)

github_iconTop GitHub Comments

1reaction
topjohnwucommented, Jul 25, 2018

@victorlapin there are still bugs in the old versions and I don’t want to maintain two branches alongside. All methods and classes in the shim are annotated @Deprecated, so developers using a decent IDE will be notified to use the newer API.

The shim is fairly simple so it wouldn’t be too complicated to maintain, since the previous API is mostly a collection of method overrides and actually only rely on a few key methods. I just need to transform the few key methods and it’s good to go!

libsu is meant to have 0 dependencies itself so I kept it nice and clean. Creating RxJava2 wrapper is definitely very cool! I think it is also possible to wrap it with Support Library’s Lifecycle-Aware Components. I’ll leave these to other developers though, my main goal should be making this library as robust as possible!

1reaction
topjohnwucommented, Jul 23, 2018

@PerfectSlayer so I’ve made some changes after your suggestions.

  • enqueue is renamed to submit. The original idea is from JobIntentService, but I personally also prefer the Java Executor way
  • Removed Job.onResult and add an overload Job.submit(ResultCallback)

I added chaining operations, so the final Job class is like this:

public abstract static class Job {
    public abstract Job to(List<String> stdout);
    public abstract Job to(List<String> stdout, List<String> stderr);
    public abstract Job add(InputStream in);
    public abstract Job add(String... cmds);
    public abstract Result exec();
    public abstract void submit();
    public abstract void submit(ResultCallback cb);
}

Along with these changes two non-static APIs in Shell are replaced with a new one:

// Add
public abstract Job newJob();
// Remove
public abstract Job newJob(InputStream in);
public abstract Job newJob(String... cmds);

To directly interact with a Shell instance will be like shell.newJob().add(InputStream).add(cmds....).add(...).exec()

However, I did not touch the high level APIs, because in most cases people will just use Shell.su(cmds...) or Shell.su(in) (at least in my case). New operations can also be added to the returned Job from the high level APIs, so nothing is missing there.

Also, I personally do not like method names to be too verbose (contradict with common Java code naming conventions LOL), so I named my API very simple, like add and to. Also I would love to leave the to(stdout) and to(stdout, stderr) methods untouched too, because in the case of to(stdout), it will check the global flag FLAG_REDIRECT_STDERR to decide whether STDERR should be collected. To only collect STDERR, just use to(null, List).

Read more comments on GitHub >

github_iconTop Results From Across the Web

Discussions API - GitLab Docs
Discussions are a set of related notes on: Snippets; Issues; Epics; Merge requests; Commits. This includes comments and threads and system notes.
Read more >
Latest API Discussion topics - Blizzard Forums
Topic Replies Views Activity Character Reputations API · API Discussion 3 64 December 14, 2022 Solo Shuffle API Profile · API Discussion 40 4385 December...
Read more >
Using the GraphQL API for Discussions - GitHub Docs
The GitHub Discussions GraphQL API allows you to get, create, edit, and delete discussion posts. For more information about GitHub Discussions, ...
Read more >
Welcome to the API Discussion Forum - Intervals
With the API recently going into beta, we wanted to give our customers an arena to discuss and share ideas on how they...
Read more >
API Forum | Akana Documentation
You can use the API Forum to create a discussion about a specific API. Common discussion topics include questions, suggestions, feature requests, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found