Introduce DefaultContainer

This commit is contained in:
topjohnwu 2019-02-03 09:30:47 -05:00
parent 92a1538ad8
commit 9f15a6d879
6 changed files with 74 additions and 91 deletions

View File

@ -18,27 +18,8 @@ package com.topjohnwu.superuser;
import android.app.Application;
import androidx.annotation.Nullable;
/**
* A subclass of {@link Application} with a {@link Shell.Container} injected.
* @deprecated
*/
public class ContainerApp extends Application implements Shell.Container {
private volatile Shell mShell;
public ContainerApp() {
Shell.Config.setContainer(this);
}
@Nullable
@Override
public Shell getShell() {
return mShell;
}
@Override
public void setShell(@Nullable Shell shell) {
mShell = shell;
}
}
@Deprecated
public class ContainerApp extends Application {}

View File

@ -18,6 +18,7 @@ package com.topjohnwu.superuser;
import android.content.Context;
import com.topjohnwu.superuser.internal.DefaultContainer;
import com.topjohnwu.superuser.internal.Factory;
import com.topjohnwu.superuser.internal.InternalUtils;
import com.topjohnwu.superuser.internal.UiThreadHandler;
@ -42,24 +43,17 @@ import androidx.annotation.Nullable;
/**
* A class providing an API to an interactive (root) shell.
* <p>
* Sharing shells means that the {@code Shell} instance would need to be stored somewhere.
* Generally, most developers would want to have the {@code Shell} instance shared globally
* across the application. In that case, the developer can directly use or subclass the the readily
* available {@link com.topjohnwu.superuser.ContainerApp} and the setup is all done. If you already
* overridden {@link android.app.Application}, and it is impossible to change the base class,
* or for some reason one would want to store the {@code Shell} instance somewhere else, check the
* documentation of {@link Container} for more info. Once a global {@link Container} is registered,
* use {@link #getShell()} or {@link #getShell(GetShellCallback)} to get/construct {@code Shell}.
* <p>
* However in most cases, developers do not need to deal with a {@code Shell} instance.
* Use these high level APIs:
* In most cases, developers do not need to directly access a {@code Shell} instance.
* Use these high level APIs instead:
* <ul>
* <li>{@link #sh(String...)}</li>
* <li>{@link #su(String...)}</li>
* <li>{@link #sh(InputStream)}</li>
* <li>{@link #su(InputStream)}</li>
* </ul>
* These methods uses the global shell and are more convenient to use.
* These methods not only uses the global shell instance but are also more convenient to use.
* The global shell instance is constructed on-demand and cached.
* To get the instance directly, call {@link #getShell()} or {@link #getShell(GetShellCallback)}.
* <p>
* Developers can check the example that came along with the library, it demonstrates many features
* the library has to offer.
@ -182,6 +176,12 @@ public abstract class Shell implements Closeable {
}
}
@NonNull
private static Container getContainer() {
Container container = weakContainer.get();
return container == null ? DefaultContainer.CONTAINER : container;
}
/**
* Get a {@code Shell} instance from the global container, return {@code null} if no active
* shell is stored in the container or no container is assigned.
@ -190,24 +190,17 @@ public abstract class Shell implements Closeable {
*/
@Nullable
public static Shell getCachedShell() {
Shell shell = null;
Container container = weakContainer.get();
if (container != null)
shell = container.getShell();
Shell shell = getContainer().getShell();
if (shell != null && !shell.isAlive())
shell = null;
return shell;
}
static void setCachedShell(Shell shell) {
private static void setCachedShell(Shell shell) {
if (isInitGlobal) {
// Set the global shell
Container container = weakContainer.get();
if (container != null)
container.setShell(shell);
getContainer().setShell(shell);
}
}
@ -458,17 +451,10 @@ public abstract class Shell implements Closeable {
private Config() {}
/**
* Set the container to store the global {@code Shell} instance.
* <p>
* Future shell commands using static method APIs will automatically obtain a {@code Shell}
* from the container with {@link #getShell()} or {@link #getShell(GetShellCallback)}.
* <p>
* A {@link WeakReference} of the registered container would be saved statically
* so the container could be garbage collected to prevent memory leak if you decide to
* store {@code Shell} in places like {@link android.app.Activity}.
* @param container the container to store the global {@code Shell} instance.
* @deprecated
*/
public static void setContainer(@Nullable Container container) {
@Deprecated
public static void setContainer(@NonNull Container container) {
weakContainer = new WeakReference<>(container);
}

View File

@ -0,0 +1,25 @@
package com.topjohnwu.superuser.internal;
import com.topjohnwu.superuser.Shell;
import androidx.annotation.Nullable;
public class DefaultContainer implements Shell.Container {
public static final DefaultContainer CONTAINER = new DefaultContainer();
private volatile Shell shell;
private DefaultContainer() {}
@Nullable
@Override
public Shell getShell() {
return shell;
}
@Override
public void setShell(@Nullable Shell s) {
shell = s;
}
}

View File

@ -1,15 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.libsuexample">
<application
android:name=".ExampleApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -1,34 +0,0 @@
package com.topjohnwu.libsuexample;
import android.content.Context;
import android.util.Log;
import com.topjohnwu.superuser.BusyBoxInstaller;
import com.topjohnwu.superuser.ContainerApp;
import com.topjohnwu.superuser.Shell;
import androidx.annotation.NonNull;
public class ExampleApp extends ContainerApp {
public static final String TAG = "EXAMPLE";
static {
// Use internal busybox
Shell.Config.addInitializers(BusyBoxInstaller.class);
// Configuration
Shell.Config.setFlags(Shell.FLAG_REDIRECT_STDERR);
Shell.Config.verboseLogging(BuildConfig.DEBUG);
Shell.Config.addInitializers(ExampleInitializer.class);
}
// Demonstrate Shell.Initializer
private static class ExampleInitializer extends Shell.Initializer {
@Override
public boolean onInit(Context context, @NonNull Shell shell) {
Log.d(TAG, "onInit");
return true;
}
}
}

View File

@ -1,6 +1,7 @@
package com.topjohnwu.libsuexample;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
@ -9,19 +10,42 @@ import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import com.topjohnwu.superuser.BusyBoxInstaller;
import com.topjohnwu.superuser.CallbackList;
import com.topjohnwu.superuser.Shell;
import java.io.IOException;
import java.util.List;
import androidx.annotation.NonNull;
public class MainActivity extends Activity {
public static final String TAG = "EXAMPLE";
private TextView console;
private EditText input;
private ScrollView sv;
private List<String> consoleList;
static {
// Configuration
Shell.Config.setFlags(Shell.FLAG_REDIRECT_STDERR);
Shell.Config.verboseLogging(BuildConfig.DEBUG);
// Use internal busybox
Shell.Config.addInitializers(BusyBoxInstaller.class, ExampleInitializer.class);
}
// Demonstrate Shell.Initializer
static class ExampleInitializer extends Shell.Initializer {
@Override
public boolean onInit(Context context, @NonNull Shell shell) {
Log.d(TAG, "onInit");
return true;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -48,7 +72,7 @@ public class MainActivity extends Activity {
async_cmd.setOnClickListener(v -> {
Shell.sh(input.getText().toString())
.to(consoleList)
.submit(out -> Log.d(ExampleApp.TAG, "async_cmd_result: " + out.getCode()));
.submit(out -> Log.d(TAG, "async_cmd_result: " + out.getCode()));
input.setText("");
});