添加java控制台窗口
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Jan 14 03:09:08 GMT 2026
|
||||
#Wed Jan 14 03:45:26 GMT 2026
|
||||
stageCount=0
|
||||
libraryProject=libauthcenterapp
|
||||
baseVersion=15.0
|
||||
publishVersion=15.0.0
|
||||
buildCount=14
|
||||
buildCount=24
|
||||
baseBetaVersion=15.0.1
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="cc.winboll.studio.authcenterapp">
|
||||
<!-- 网络权限:允许监听端口+接收网络请求 -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<!-- API30+ 需添加网络访问权限 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!-- 若需外部设备访问,添加局域网权限(可选) -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
|
||||
|
||||
<!-- 拥有完全的网络访问权限 -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<!-- 查看网络连接 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<!-- 查看WLAN连接 -->
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:icon="@drawable/ic_winboll"
|
||||
@@ -40,6 +43,8 @@
|
||||
android:name="android.max_aspect"
|
||||
android:value="4.0"/>
|
||||
|
||||
<activity android:name="cc.winboll.studio.authcenterapp.activities.ConsoleActivity"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
@@ -3,6 +3,7 @@ package cc.winboll.studio.authcenterapp;
|
||||
import cc.winboll.studio.authcenterapp.BuildConfig;
|
||||
import cc.winboll.studio.libappbase.GlobalApplication;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
@@ -24,6 +25,7 @@ public class App extends GlobalApplication {
|
||||
super.onCreate(); // 调用父类初始化逻辑(如基础库配置、全局上下文设置)
|
||||
//setIsDebugging(false);
|
||||
setIsDebugging(BuildConfig.DEBUG);
|
||||
WinBoLLActivityManager.init(this);
|
||||
// 初始化 Toast 工具类(传入应用全局上下文,确保 Toast 可在任意地方调用)
|
||||
ToastUtils.init(getApplicationContext());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package cc.winboll.studio.authcenterapp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -10,60 +9,56 @@ import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Switch;
|
||||
import android.widget.Toolbar;
|
||||
import cc.winboll.studio.libauthcenterapp.auth.AuthCenterHttpService;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import cc.winboll.studio.authcenterapp.R;
|
||||
import cc.winboll.studio.authcenterapp.activities.BaseWinBoLLActivity;
|
||||
import cc.winboll.studio.authcenterapp.activities.ConsoleActivity;
|
||||
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||
import cc.winboll.studio.libappbase.LogActivity;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import cc.winboll.studio.libappbase.ToastUtils;
|
||||
import cc.winboll.studio.libauthcenterapp.auth.AuthCenterHttpService;
|
||||
|
||||
/**
|
||||
* @Author ZhanGSKen<zhangsken@qq.com>
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/14 02:03:00
|
||||
* @LastEditTime 2026/01/14 22:35:00
|
||||
* @LastEditTime 2026/01/14 23:18:45
|
||||
* @Describe 应用主界面 Activity(入口界面)
|
||||
* 包含功能测试按钮(崩溃测试、日志查看、Toast测试)、顶部工具栏(菜单功能)、AuthCenter服务启停开关,是应用交互的核心入口
|
||||
*/
|
||||
public class MainActivity extends Activity {
|
||||
public class MainActivity extends BaseWinBoLLActivity {
|
||||
|
||||
/** 当前 Activity 的日志 TAG(用于调试输出,标识日志来源) */
|
||||
public static final String TAG = "MainActivity";
|
||||
/** AuthCenter Http服务端口(固定8080,适配之前服务类配置) */
|
||||
private static final int AUTH_SERVICE_PORT = 8080;
|
||||
|
||||
/** 顶部工具栏(用于展示标题、菜单,绑定布局中的 Toolbar 控件) */
|
||||
private Toolbar mToolbar;
|
||||
/** AuthCenter服务启停开关 */
|
||||
private Switch mAuthServiceSwitch;
|
||||
/** AuthCenter Http服务实例(全局唯一,用于启停控制) */
|
||||
private AuthCenterHttpService mAuthHttpService;
|
||||
/** 服务运行状态标记,避免重复启停 */
|
||||
private boolean isServiceRunning = false;
|
||||
|
||||
/**
|
||||
* Activity 创建时回调(初始化界面)
|
||||
* 在 Activity 首次创建时执行,用于加载布局、初始化控件、设置事件监听
|
||||
* @param savedInstanceState 保存 Activity 状态的 Bundle(如屏幕旋转时的数据恢复)
|
||||
*/
|
||||
@Override
|
||||
public String getTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ToastUtils.show("onCreate"); // 显示 Activity 创建提示(调试用)
|
||||
setContentView(R.layout.activity_main); // 加载主界面布局
|
||||
ToastUtils.show("onCreate");
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
// 初始化 Toolbar 并设置为 ActionBar
|
||||
mToolbar = findViewById(R.id.toolbar);
|
||||
setActionBar(mToolbar); // 将 Toolbar 替代系统默认 ActionBar
|
||||
// 初始化服务开关并绑定监听
|
||||
setSupportActionBar(mToolbar);
|
||||
// 修正1:启用ActionBar显示图标(默认隐藏)
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||
getSupportActionBar().setDisplayUseLogoEnabled(true);
|
||||
}
|
||||
initAuthServiceSwitch();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化AuthCenter服务开关,绑定状态切换监听
|
||||
*/
|
||||
private void initAuthServiceSwitch() {
|
||||
mAuthServiceSwitch = findViewById(R.id.switch_auth_service);
|
||||
// 开关状态切换监听(控制服务启停)
|
||||
mAuthServiceSwitch.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(android.widget.CompoundButton buttonView, boolean isChecked) {
|
||||
@@ -76,11 +71,7 @@ public class MainActivity extends Activity {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动AuthCenter Http服务,子线程执行避免阻塞主线程
|
||||
*/
|
||||
private void startAuthHttpService() {
|
||||
// 关键新增:校验Android版本,仅API29(Android10)及以上支持原生HttpServer
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
ToastUtils.show("当前系统版本不支持,需Android 10及以上");
|
||||
mAuthServiceSwitch.setChecked(false);
|
||||
@@ -92,14 +83,12 @@ public class MainActivity extends Activity {
|
||||
mAuthServiceSwitch.setChecked(true);
|
||||
return;
|
||||
}
|
||||
// 子线程启动服务,Android主线程不能执行网络/服务阻塞操作
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mAuthHttpService = new AuthCenterHttpService(AUTH_SERVICE_PORT);
|
||||
mAuthHttpService.start();
|
||||
// 切换到主线程更新状态+提示,避免子线程操作UI
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -109,7 +98,6 @@ public class MainActivity extends Activity {
|
||||
}
|
||||
});
|
||||
} catch (final Exception e) {
|
||||
// 启动失败(端口占用等),主线程回滚状态
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -124,11 +112,7 @@ public class MainActivity extends Activity {
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止AuthCenter Http服务,执行优雅停机
|
||||
*/
|
||||
private void stopAuthHttpService() {
|
||||
// 关键新增:版本校验,避免低版本空指针
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
mAuthServiceSwitch.setChecked(false);
|
||||
return;
|
||||
@@ -149,9 +133,6 @@ public class MainActivity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity销毁时回调,确保服务随页面销毁停止,避免内存泄漏
|
||||
*/
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
@@ -160,96 +141,60 @@ public class MainActivity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建菜单时回调(加载工具栏菜单)
|
||||
* 初始化 ActionBar 菜单,加载自定义菜单布局
|
||||
* @param menu 菜单对象(用于承载菜单项)
|
||||
* @return true:显示菜单;false:不显示菜单
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// 加载菜单布局(R.menu.toolbar_main 为自定义菜单文件)
|
||||
getMenuInflater().inflate(R.menu.toolbar_main, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
// 修正2:遍历菜单项,强制显示图标(AppCompat默认隐藏菜单图标)
|
||||
for (int i = 0; i < menu.size(); i++) {
|
||||
MenuItem item = menu.getItem(i);
|
||||
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
}
|
||||
return true; // 修正3:返回true确保菜单正常显示
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单 item 点击时回调(处理菜单事件)
|
||||
* 响应 Toolbar 菜单项的点击事件,执行对应业务逻辑
|
||||
* @param item 被点击的菜单项
|
||||
* @return true:消费点击事件;false:不消费(传递给父类)
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.item_home:
|
||||
// 点击 "首页/官网" 菜单项,唤起浏览器打开指定网站
|
||||
openWebsiteInBrowser(this);
|
||||
break;
|
||||
// 可扩展其他菜单项(如设置、关于等)的处理逻辑
|
||||
case R.id.item_console:
|
||||
WinBoLLActivityManager.getInstance().startWinBoLLActivity(getApplicationContext(), ConsoleActivity.class);
|
||||
break;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 崩溃测试按钮点击事件(触发应用崩溃,用于调试异常捕获)
|
||||
* 故意执行非法操作(循环获取不存在的字符串资源),强制应用崩溃
|
||||
* @param view 触发事件的 View(对应布局中的崩溃测试按钮)
|
||||
*/
|
||||
public void onCrashTest(View view) {
|
||||
// 循环从 Integer.MIN_VALUE 到 Integer.MAX_VALUE,获取不存在的字符串资源 ID,触发崩溃
|
||||
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
|
||||
getString(i); // i 超出资源 ID 范围,抛出 Resources.NotFoundException 导致崩溃
|
||||
getString(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日志测试按钮点击事件(打开日志查看界面)
|
||||
* 启动 LogActivity,用于查看应用运行日志
|
||||
* @param view 触发事件的 View(对应布局中的日志测试按钮)
|
||||
*/
|
||||
public void onLogTest(View view) {
|
||||
// 启动日志查看 Activity(通过静态方法传入上下文,简化跳转逻辑)
|
||||
LogActivity.startLogActivity(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toast 工具测试按钮点击事件(测试全局 Toast 功能)
|
||||
* 测试主线程、子线程中 Toast 的显示效果,验证 ToastUtils 的可用性
|
||||
* @param view 触发事件的 View(对应布局中的 Toast 测试按钮)
|
||||
*/
|
||||
public void onToastUtilsTest(View view) {
|
||||
LogUtils.d(TAG, "onToastUtilsTest"); // 打印调试日志,标识进入 Toast 测试
|
||||
ToastUtils.show("Hello, WinBoLL!"); // 主线程显示 Toast
|
||||
|
||||
// 开启子线程,延迟 2 秒后显示 Toast(测试子线程 Toast 兼容性)
|
||||
LogUtils.d(TAG, "onToastUtilsTest");
|
||||
ToastUtils.show("Hello, WinBoLL!");
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(2000); // 线程休眠 2 秒
|
||||
// 若 ToastUtils 已处理主线程切换,此处可直接调用;否则需通过 Handler 切换到主线程
|
||||
Thread.sleep(2000);
|
||||
ToastUtils.show("Thread.sleep(2000);ToastUtils.show...");
|
||||
} catch (InterruptedException e) {
|
||||
// 捕获线程中断异常(如线程被销毁时),不做处理(测试场景)
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 唤起系统默认浏览器打开指定网站(跳转至应用官网)
|
||||
* 通过 Intent.ACTION_VIEW 隐式意图,触发浏览器打开目标 URL
|
||||
* @param context 上下文对象(如 Activity、Application,此处为 MainActivity)
|
||||
*/
|
||||
public void openWebsiteInBrowser(Context context) {
|
||||
String url = "https://www.winboll.cc"; // 目标网站 URL(应用官网)
|
||||
// 构建隐式意图:ACTION_VIEW 表示查看指定数据(Uri 为网站地址)
|
||||
String url = "https://www.winboll.cc";
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
// 设置标志:在新的任务栈中启动 Activity(避免与当前应用任务栈混淆)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
// 启动意图(唤起浏览器)
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package cc.winboll.studio.authcenterapp.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import cc.winboll.studio.libaes.interfaces.IWinBoLLActivity;
|
||||
import cc.winboll.studio.libaes.models.AESThemeBean;
|
||||
import cc.winboll.studio.libaes.utils.AESThemeUtil;
|
||||
import cc.winboll.studio.libaes.utils.WinBoLLActivityManager;
|
||||
|
||||
/**
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/14 11:32
|
||||
* BaseWinBollActivity 【继承AppCompatActivity,保留核心能力,不额外暴露方法】
|
||||
* 继承链路:BaseWinBoLLActivity → AppCompatActivity → FragmentActivity,AppCompat能力天然继承可用
|
||||
*/
|
||||
public abstract class BaseWinBoLLActivity extends AppCompatActivity implements IWinBoLLActivity {
|
||||
public static final String TAG = "BaseWinBoLLActivity";
|
||||
|
||||
protected volatile AESThemeBean.ThemeType mThemeType;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
mThemeType = getThemeType();
|
||||
setThemeStyle();
|
||||
super.onCreate(savedInstanceState);
|
||||
WinBoLLActivityManager.getInstance().add(this);
|
||||
//ToastUtils.show(getTag() + ": onCreate");
|
||||
}
|
||||
|
||||
AESThemeBean.ThemeType getThemeType() {
|
||||
/*SharedPreferences sharedPreferences = getSharedPreferences(
|
||||
SHAREDPREFERENCES_NAME, MODE_PRIVATE);
|
||||
return AESThemeBean.ThemeType.values()[((sharedPreferences.getInt(DRAWER_THEME_TYPE, AESThemeBean.ThemeType.DEFAULT.ordinal())))];
|
||||
*/
|
||||
return AESThemeBean.getThemeStyleType(AESThemeUtil.getThemeTypeID(getApplicationContext()));
|
||||
}
|
||||
|
||||
void setThemeStyle() {
|
||||
//setTheme(AESThemeBean.getThemeStyle(getThemeType()));
|
||||
setTheme(AESThemeUtil.getThemeTypeID(getApplicationContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
WinBoLLActivityManager.getInstance().registeRemove(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
// 子类必须实现getTag(),确保唯一标识
|
||||
@Override
|
||||
public abstract String getTag();
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package cc.winboll.studio.authcenterapp.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import cc.winboll.studio.authcenterapp.R;
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
|
||||
/**
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/14 11:20:00
|
||||
* @LastEditTime 2026/01/14 16:28:00
|
||||
* @Describe 控制台输出页面,支持命令输入提交与结果展示,提交后禁用控件,命令结束恢复可用
|
||||
*/
|
||||
public class ConsoleActivity extends BaseWinBoLLActivity {
|
||||
// 排序:常量 → 成员控件属性,符合Java编码规范
|
||||
public static final String TAG = "ConsoleActivity";
|
||||
private TextView tvConsoleOutput; // 控制台只读输出展示
|
||||
private EditText etCmdInput; // 命令输入框
|
||||
private Button btnSubmitCmd; // 命令提交按钮
|
||||
|
||||
// 重写父类方法,置顶于生命周期方法前
|
||||
@Override
|
||||
public String getTag() {
|
||||
String tag = TAG;
|
||||
LogUtils.d(TAG, "getTag() 调用,返回标签:" + tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
// 生命周期方法
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LogUtils.d(TAG, "onCreate() 生命周期执行,传入savedInstanceState:" + savedInstanceState);
|
||||
setContentView(R.layout.activity_console);
|
||||
initView();
|
||||
initListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化页面控件,绑定视图ID
|
||||
*/
|
||||
private void initView() {
|
||||
LogUtils.d(TAG, "initView() 开始初始化控件");
|
||||
tvConsoleOutput = findViewById(R.id.tv_console_output);
|
||||
etCmdInput = findViewById(R.id.et_cmd_input);
|
||||
btnSubmitCmd = findViewById(R.id.btn_submit_cmd);
|
||||
// 初始化输出框默认提示
|
||||
tvConsoleOutput.setText("控制台已就绪,请输入命令提交执行\n");
|
||||
LogUtils.d(TAG, "initView() 控件初始化完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化控件点击监听事件
|
||||
*/
|
||||
private void initListener() {
|
||||
LogUtils.d(TAG, "initListener() 开始绑定监听事件");
|
||||
btnSubmitCmd.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
LogUtils.d(TAG, "btnSubmitCmd 提交按钮被点击");
|
||||
submitCommand();
|
||||
}
|
||||
});
|
||||
LogUtils.d(TAG, "initListener() 监听事件绑定完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交命令核心处理逻辑,含参数校验与状态控制
|
||||
*/
|
||||
private void submitCommand() {
|
||||
final String cmd = etCmdInput.getText().toString().trim();
|
||||
LogUtils.d(TAG, "submitCommand() 调用,传入命令参数:" + cmd);
|
||||
|
||||
if (cmd.isEmpty()) {
|
||||
appendConsoleMsg("错误:请输入有效命令!\n");
|
||||
LogUtils.d(TAG, "submitCommand() 命令为空,执行拦截");
|
||||
return;
|
||||
}
|
||||
|
||||
// 提交后禁用输入框与按钮,防止重复提交
|
||||
setCmdViewEnable(false);
|
||||
appendConsoleMsg("执行命令:" + cmd + "\n");
|
||||
etCmdInput.setText("");
|
||||
|
||||
// 子线程执行命令,避免阻塞主线程
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LogUtils.d(TAG, "命令执行子线程启动,开始执行耗时操作");
|
||||
try {
|
||||
// 模拟命令执行耗时(后续可替换为真实命令执行逻辑)
|
||||
Thread.sleep(2000);
|
||||
// 模拟命令执行成功结果
|
||||
appendConsoleMsg("命令执行成功,结果:执行完成\n");
|
||||
LogUtils.d(TAG, "命令执行完成,执行结果:成功");
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.d(TAG, "submitCommand() 命令执行线程中断异常", e);
|
||||
appendConsoleMsg("命令执行被中断!\n");
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
// 命令结束后切回主线程恢复控件可用状态
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
setCmdViewEnable(true);
|
||||
LogUtils.d(TAG, "命令执行结束,恢复输入控件可用状态");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制台输出内容追加,确保UI操作在主线程执行
|
||||
* @param msg 要追加的控制台输出信息
|
||||
*/
|
||||
private void appendConsoleMsg(final String msg) {
|
||||
LogUtils.d(TAG, "appendConsoleMsg() 调用,追加输出信息:" + msg);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
tvConsoleOutput.append(msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入控件(输入框+提交按钮)可用状态
|
||||
* @param enable 可用状态标识 true-可用 false-禁用
|
||||
*/
|
||||
private void setCmdViewEnable(boolean enable) {
|
||||
LogUtils.d(TAG, "setCmdViewEnable() 调用,设置控件可用状态:" + enable);
|
||||
etCmdInput.setEnabled(enable);
|
||||
btnSubmitCmd.setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package cc.winboll.studio.authcenterapp.console;
|
||||
|
||||
import cc.winboll.studio.libappbase.LogUtils;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* @Author 豆包&ZhanGSKen<zhangsken@qq.com>
|
||||
* @Date 2026/01/14 11:16
|
||||
* @LastEditTime 2026/01/14 08:20:00
|
||||
* @Describe Java命令控制台执行工具,支持实时输出命令运行日志
|
||||
*/
|
||||
public class JavaCmdConsole {
|
||||
public static final String TAG = "JavaCmdConsole";
|
||||
private static final String JAVA_CMD = "java";
|
||||
|
||||
/**
|
||||
* 执行Java命令(通用入口)
|
||||
* @param cmdParams Java命令参数,如 [-version]、[xxx.MainClass]
|
||||
*/
|
||||
public void executeJavaCmd(String... cmdParams) {
|
||||
if (cmdParams == null || cmdParams.length == 0) {
|
||||
LogUtils.d(TAG, "执行失败:Java命令参数不能为空");
|
||||
System.out.println("Java命令参数不能为空,请传入有效参数");
|
||||
return;
|
||||
}
|
||||
|
||||
// 拼接完整命令数组
|
||||
String[] fullCmd = new String[cmdParams.length + 1];
|
||||
fullCmd[0] = JAVA_CMD;
|
||||
System.arraycopy(cmdParams, 0, fullCmd, 1, cmdParams.length);
|
||||
|
||||
LogUtils.d(TAG, "准备执行Java命令,完整指令:" + arrayToString(fullCmd));
|
||||
System.out.println("===== Java命令执行开始 =====");
|
||||
Process process = null;
|
||||
try {
|
||||
// 启动命令进程
|
||||
process = Runtime.getRuntime().exec(fullCmd);
|
||||
// 实时读取标准输出流
|
||||
new Thread(new StreamReader(process.getInputStream(), false)).start();
|
||||
// 实时读取错误输出流
|
||||
new Thread(new StreamReader(process.getErrorStream(), true)).start();
|
||||
// 等待进程执行完成,获取退出码
|
||||
int exitCode = process.waitFor();
|
||||
String result = exitCode == 0 ? "执行成功" : "执行失败";
|
||||
LogUtils.d(TAG, "Java命令执行结束,退出码:" + exitCode + ",结果:" + result);
|
||||
System.out.println("===== Java命令执行结束," + result + "(退出码:" + exitCode + ") =====");
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, "Java命令执行IO异常", e);
|
||||
System.out.println("执行异常:IO错误,无法启动命令进程");
|
||||
} catch (InterruptedException e) {
|
||||
LogUtils.d(TAG, "Java命令执行被中断", e);
|
||||
System.out.println("执行异常:进程等待被中断");
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
if (process != null) {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 流读取线程,实时捕获命令输出
|
||||
*/
|
||||
private static class StreamReader implements Runnable {
|
||||
private final BufferedReader reader;
|
||||
private final boolean isErrorStream;
|
||||
|
||||
public StreamReader(InputStream inputStream, boolean isErrorStream) {
|
||||
this.reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
this.isErrorStream = isErrorStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
String line;
|
||||
try {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (isErrorStream) {
|
||||
LogUtils.d(TAG, "命令错误输出:" + line);
|
||||
System.err.println(line);
|
||||
} else {
|
||||
LogUtils.d(TAG, "命令标准输出:" + line);
|
||||
System.out.println(line);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, "流读取异常", e);
|
||||
} finally {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
LogUtils.d(TAG, "流关闭异常", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数组转字符串,用于日志打印
|
||||
*/
|
||||
private String arrayToString(String[] array) {
|
||||
if (array == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : array) {
|
||||
sb.append(s).append(" ");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
}
|
||||
|
||||
11
authcenterapp/src/main/res/drawable/ic_console.xml
Normal file
11
authcenterapp/src/main/res/drawable/ic_console.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="#ff000000"
|
||||
android:pathData="M20,19V7H4V19H20M20,3A2,2 0,0 1,22 5V19A2,2 0,0 1,20 21H4A2,2 0,0 1,2 19V5C2,3.89 2.9,3 4,3H20M13,17V15H18V17H13M9.58,13L5.57,9H8.4L11.7,12.3C12.09,12.69 12.09,13.33 11.7,13.72L8.42,17H5.59L9.58,13Z"/>
|
||||
|
||||
</vector>
|
||||
56
authcenterapp/src/main/res/layout/activity_console.xml
Normal file
56
authcenterapp/src/main/res/layout/activity_console.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp">
|
||||
|
||||
<!-- 控制台输出区域(不可编辑,可滚动) -->
|
||||
<TextView
|
||||
android:id="@+id/tv_console_output"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@android:color/white"
|
||||
android:padding="8dp"
|
||||
android:textColor="@android:color/black"
|
||||
android:gravity="start|top"
|
||||
android:scrollbars="vertical"
|
||||
android:fadeScrollbars="false"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<!-- 底部控制栏 -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
android:background="@android:color/darker_gray"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:spacing="8dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_cmd_input"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:hint="请输入执行命令"
|
||||
android:inputType="text"
|
||||
android:textSize="14sp"
|
||||
android:padding="8dp"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_submit_cmd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="提交执行"
|
||||
android:textSize="14sp"
|
||||
android:paddingHorizontal="16dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.widget.Toolbar
|
||||
<cc.winboll.studio.libaes.views.ASupportToolbar
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/toolbar"/>
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
<item
|
||||
android:id="@+id/item_home"
|
||||
android:title="Home"
|
||||
android:icon="@drawable/ic_winboll"
|
||||
android:icon="@drawable/ic_winboll_logo"
|
||||
android:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/item_console"
|
||||
android:title="Console"
|
||||
android:icon="@drawable/ic_console"
|
||||
android:showAsAction="always"/>
|
||||
</menu>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#Created by .winboll/winboll_app_build.gradle
|
||||
#Wed Jan 14 03:09:08 GMT 2026
|
||||
#Wed Jan 14 03:45:26 GMT 2026
|
||||
stageCount=0
|
||||
libraryProject=libauthcenterapp
|
||||
baseVersion=15.0
|
||||
publishVersion=15.0.0
|
||||
buildCount=14
|
||||
buildCount=24
|
||||
baseBetaVersion=15.0.1
|
||||
|
||||
Reference in New Issue
Block a user