Android studio组合教程--做出一个类似于QQ的登录页面
之前我们学过了html与Android的开发,以及各种组件的学习,这次我们做一个完整向的登录页面,作为一次大作业。
注意
里面的一图片可以自由发挥,但要注意文件路径保持准确,这里给出参考路径:
背景路径: background: url(…/pic/图标.jpg.jpg) no-repeat center center fixed;
思路及代码
一.注册部分
(1).我们先创立user类
,表示使用者应该拥有的一些属性:
User.java:
package com.example.activity;import android.graphics.Bitmap;import java.io.Serializable;public class User implements Serializable {private int userId;//用户idprivate String userName;//用户名private String userPassword;//密码private int userPhone;//电话private int userAge;//年龄private String userAddress;//地址private byte[] data; // BLOB 类型存储图片private Bitmap bitmap;//图片jpgpublic byte[] getData() {return data;}public void setData(byte[] data) {this.data = data;}public Bitmap getBitmap() {return bitmap;}public void setBitmap(Bitmap bitmap) {this.bitmap = bitmap;}public int getUserId() {return userId;}public void setUserId(int usetId) {this.userId = usetId;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getUserPassword() {return userPassword;}public void setUserPassword(String userPassword) {this.userPassword = userPassword;}public int getUserPhone() {return userPhone;}public void setUserPhone(int userPhone) {this.userPhone = userPhone;}public int getUserAge() {return userAge;}public void setUserAge(int userAge) {this.userAge = userAge;}public String getUserAddress() {return userAddress;}public void setUserAddress(String userAddress) {this.userAddress = userAddress;}@Overridepublic String toString() {return "User{" +"userName='" + userName + '\'' +", userPassword='" + userPassword + '\'' +", userPhone=" + userPhone + '\'' +", userAge=" + userAge + '\'' +", userAddress='" + userAddress + '\'' +'}';}
}
(2)创建WeakRefHandler类
这段代码实现了一个 弱引用(WeakReference)封装的 Handler 类,主要目的是 解决 Android 开发中因 Handler 导致的内存泄漏问题。
package com.example.activity;import android.os.Handler;
import android.os.Looper;
import android.os.Message;import java.lang.ref.WeakReference;public class WeakRefHandler<T> extends Handler {private WeakReference<T> mWeakReference;private Callback mCallback;public WeakRefHandler(Looper looper, T t, Callback callback) {super(looper);mCallback = callback;mWeakReference = new WeakReference<>(t);}@Overridepublic void handleMessage(Message msg) {if (isAlive() && mCallback != null) {mCallback.handleMessage(msg);}}public T getReference() {return mWeakReference.get();}/*** 是否还存活** @return*/public boolean isAlive() {T t = mWeakReference.get();return t != null;}
}
(3).创立ZhuCe类接口
,使代码层次分明:
package com.example.activity;import com.example.activity.User;public interface ZhuceContract {interface IZhucePresenter {void saveuser(User user);void usernameyanzheng(String htmlusername);}interface IZhuceView {void usernameyanzheng(boolean user);}
}
(4)处理注册事件
,这里有两个逻辑:第一个方法表示寻找用户名是否已经创建过,第二是如果没有创建过,那我们创建一个并将它插入数据库中:
ZhuCepresenter.java:
package com.example.activity;import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;import androidx.annotation.NonNull;import com.example.activity.WeakRefHandler;
import com.example.activity.ZhuceContract;
import com.example.activity.User;public class ZhucePresenter implements ZhuceContract.IZhucePresenter {private static final int WHAT_USERNAME_MATCH_RESULT = 1;private ZhuceContract.IZhuceView mZhuceView;private Context mContext;public ZhucePresenter(ZhuceContract.IZhuceView ZhuceView, Context context) {mZhuceView = ZhuceView;mContext = context;}//查询username是否注册@SuppressLint("Range")public void usernameyanzheng(String htmlusername) {new Thread(() -> {Uri uri = Uri.parse("content://com.example.group5th.provider/username/" + htmlusername);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {try (Cursor cursor = mContext.getContentResolver().query(uri, null, null, null)) {if (cursor != null && cursor.moveToFirst()) {//找到用户时的处理情况Message message = Message.obtain();message.obj = true;message.what = WHAT_USERNAME_MATCH_RESULT;mHandler.sendMessage(message);} else {//找不到用户时的处理情况Message message = Message.obtain();message.obj = false;message.what = WHAT_USERNAME_MATCH_RESULT;mHandler.sendMessage(message);}}}}).start();}Handler mHandler = new WeakRefHandler<>(Looper.getMainLooper(), this, new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {if (msg.what == WHAT_USERNAME_MATCH_RESULT) {boolean isMatch = (boolean) msg.obj;mZhuceView.usernameyanzheng(isMatch);}return false;}});//注册插入数据库@Overridepublic void saveuser(User user) {new Thread(new Runnable() {@Overridepublic void run() {//商品插入ContentValues cv = new ContentValues();cv.put("user_name", user.getUserName());cv.put("user_password", user.getUserPassword());cv.put("user_phone", user.getUserPhone());cv.put("user_age", user.getUserAge());cv.put("user_address", user.getUserAddress());cv.put("user_touxiang", user.getData());Uri userUrl = Uri.parse("content://com.example.group5th.provider/user");Uri insertUri = mContext.getContentResolver().insert(userUrl, cv);//打印long newRowId = ContentUris.parseId(insertUri);Log.d("", "新插入的用户id ===== " + newRowId);}}).start();}
}
(5)接下来我们创建ZhuCeActivity
,我们为了页面更美观一些,我们这里使用html+Android的形式:
html代码:
(这里建议有一定基础的html+css+javascript)
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>注册</title><style>body {margin: 0;padding: 0;background: url(../pic/图标.jpg.jpg) no-repeat center center fixed;background-size: cover;font-family: Arial, sans-serif;color: #333;display: flex;justify-content: center;align-items: center;height: 100vh;}.div {width: 400px;max-width: 90%;height: auto;padding: 20px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);border-radius: 10px;background: rgba(255, 255, 255, 0.4);}.head-div {width: 100px;height: 100px;border: 1px solid #999;border-radius: 50%;margin: 20px auto 10px auto;background: url(../pic/背景.jpg) no-repeat;background-size: 100px 100px;}.sign-div {width: 100%;text-align: center;outline: none;border-radius: 8px;box-sizing: border-box;padding: 20px;}.sign-div h1 {margin-bottom: 20px;color: rgb(29, 26, 26);font-size: 24px;}input {width: 80%;height: 44px;border: none;outline: none;box-sizing: border-box;display: block;padding: 0 16px;margin: 10px auto;border-radius: 22px;background: rgba(255, 255, 255, 0.9);box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);}.input-text:hover {border: 1px solid rgb(76, 76, 233);transition: 0.3s;}.input-btn {width: 80%;margin: 30px auto 20px;border-radius: 22px;cursor: pointer;background-color: rgba(84, 175, 249, 0.8);color: #fff;font-size: 16px;font-weight: bold;}.input-btn:hover {background-color: rgba(10, 138, 243, 0.8);transition: 0.3s;}.sign-div a {width: 80%;margin: 20px auto 0;display: block;text-decoration: none;color: rgb(92, 61, 112);font-size: 14px;padding: 10px;transition: 0.3s;border-radius: 22px;background-color: rgba(84, 175, 249, 0.2);box-sizing: border-box;}.sign-div a:hover {color: #fff;background-color: rgba(84, 175, 249, 0.8);}.error {color: red;font-size: 14px;margin-top: 10px;}@media (max-width: 600px) {.div {width: 90%;padding: 10px;}.sign-div h1 {font-size: 20px;}input {width: 100%;}.input-btn {width: 100%;}.sign-div a {width: 100%;}#message {color: red;margin-top: 10px;}}</style>
</head><body>
<div class="div"><div class="head-div"></div><div class="sign-div"><form class="" action="#" method="POST"><h1>注册</h1><input class="input-text" type="text" name="username" placeholder="请输入用户名"><input class="input-text" type="password" name="password" placeholder="请输入密码"><input class="input-text" type="password" name="confirmPassword" placeholder="请确认密码"><input class="input-text input-btn" type="button" value="注册" onclick="submitForm()"><input class="input-text input-btn" type="button" value="已注册,去登录" onclick="navigateToLogin()"></form><div id="message"></div></div>
</div><script>function submitForm() {var username = document.getElementsByName("username")[0].value;var password = document.getElementsByName("password")[0].value;var confirmPassword = document.getElementsByName("confirmPassword")[0].value;if (!username || !password || !confirmPassword) {showMessage("请填写所有信息");return;}if (password !== confirmPassword) {showMessage("密码不匹配");return;}// 使用Android JS桥接zhuce.submitForm(username, password);}function navigateToLogin() {// 使用Android JS桥接zhuce.navigateToLogin();}function showMessage(message) {// 使用Android JS桥接zhuce.showMessage(message);}
</script>
</body>
</html>
xml的代码,就是简单的调用html:
<?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:padding="0dp"><WebViewandroid:id="@+id/zhuce"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>
最后是Java代码:
package com.example.activity;import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;import androidx.appcompat.app.AppCompatActivity;import com.example.activity.R;
import com.example.activity.ZhuceContract;
import com.example.activity.User;
import com.example.activity.ZhucePresenter;import java.io.ByteArrayOutputStream;public class ZhuCe extends Activity implements ZhuceContract.IZhuceView {private WebView webView;private ZhuceContract.IZhucePresenter zhucePresenter;private String htmlpassword;private String htmlusername;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_zhu_ce);webView = findViewById(R.id.zhuce);WebSettings webSettings = webView.getSettings();webSettings.setJavaScriptEnabled(true); // 启用JavaScript// 确保WebView在加载页面时不会调用其他浏览器webView.setWebViewClient(new WebViewClient());// 添加JavaScript接口webView.addJavascriptInterface(new WebAppInterface(), "zhuce");// 加载HTML页面String htmlPath = "file:///android_asset/html/注册.html";webView.loadUrl(htmlPath);// 初始化PresenterzhucePresenter = new ZhucePresenter(this, this);}@Overridepublic void usernameyanzheng(boolean user) {if (user) {showMessage("注册失败,账号已注册!");} else {// 创建User对象User zhuce = new User();zhuce.setUserName(htmlusername);zhuce.setUserPassword(htmlpassword);//插入默认头像// 首先获取默认头像的资源IDint defaultAvatarId = R.drawable.mine_selected;// 使用Resources获取默认头像的DrawableDrawable defaultAvatarDrawable = getResources().getDrawable(defaultAvatarId);// 将Drawable转换为BitmapBitmap defaultAvatarBitmap = ((BitmapDrawable) defaultAvatarDrawable).getBitmap();// 接下来是创建缩放后的Bitmap并压缩为JPEG格式ByteArrayOutputStream outputStream = new ByteArrayOutputStream();Bitmap resized = Bitmap.createScaledBitmap(defaultAvatarBitmap, (int) (defaultAvatarBitmap.getWidth() * 0.1), (int) (defaultAvatarBitmap.getHeight() * 0.1), true);resized.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);// 将压缩后的数据传递给zhuce对象zhuce.setData(outputStream.toByteArray());// 使用Presenter保存用户try {zhucePresenter.saveuser(zhuce);showMessage("注册成功!");} catch (Exception e) {showMessage("注册失败!");}}}private void showMessage(String message) {runOnUiThread(() -> {new AlertDialog.Builder(ZhuCe.this).setMessage(message).setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();}}).show();});}public class WebAppInterface {@JavascriptInterfacepublic void submitForm(String username, String password) {//验证username是否注册//把username值传给dengluPresenterhtmlusername = username;htmlpassword = password;zhucePresenter.usernameyanzheng(htmlusername);}@JavascriptInterfacepublic void navigateToLogin() {Intent intent = new Intent(ZhuCe.this, DengLu.class);startActivity(intent);}@JavascriptInterfacepublic void showMessage(String message) {runOnUiThread(() -> {new AlertDialog.Builder(ZhuCe.this).setMessage(message).setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();}}).show();});}}
}
调用html,并对接口内容实现,完成登录代码的逻辑实现,大部分逻辑都注释在里面了。
页面如下:
注册部分就结束了。
二、登录部分
(1)创立DengLu类接口
package com.example.activity;public interface DengluContract {interface IDengluPresenter {void Dengluyanzheng(String htmlusername, String htmlpassword);void UsernameGetId(String htmlusername);}interface IDengluView {void showUserInfo(int zhuantaima);void getuserid(int user_id);}
}
(2)创建DengLupresenter类
这段代码的主要逻辑为,通过输入的用户名从数据库中进行寻找,如果找到了就匹配密码,两个都匹配成功即可登录。
package com.example.activity;import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;import androidx.annotation.NonNull;import com.example.activity.WeakRefHandler;
import com.example.activity.DengluContract;
import com.example.activity.User;public class DengluPresenter implements DengluContract.IDengluPresenter {private static final int WHAT_PASSWORD_MATCH_RESULT = 1;private static final int WHAT_GET_ID = 2;private DengluContract.IDengluView mDengluView;private Context mContext;private int zhuantaima;private int user_id;public DengluPresenter(DengluContract.IDengluView DengluView, Context context) {mDengluView = DengluView;mContext = context;}//根据登录输入的username查询密码,返回状态码@SuppressLint("Range")public void Dengluyanzheng(String htmlusername, String htmlpassword) {new Thread(() -> {Uri uri = Uri.parse("content://com.example.group5th.provider/username/" + htmlusername);Log.d("DengluPresenter", "htmlusername== " + htmlusername);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {try (Cursor cursor = mContext.getContentResolver().query(uri, null, null, null)) {if (cursor != null && cursor.moveToFirst()) {String storedPassword = cursor.getString(cursor.getColumnIndex("user_password"));boolean isPasswordMatch = storedPassword != null && storedPassword.equals(htmlpassword);if(isPasswordMatch){zhuantaima=0;//登录成功}else {zhuantaima=1;//密码不正确}Message message = Message.obtain();message.obj = zhuantaima;message.what = WHAT_PASSWORD_MATCH_RESULT;mHandler.sendMessage(message);} else {zhuantaima=2;//用户未注册//找不到用户时的处理情况Message message = Message.obtain();message.obj = zhuantaima; //未找到用户message.what = WHAT_PASSWORD_MATCH_RESULT;mHandler.sendMessage(message);}}}}).start();}//根据传过来的登录的用户名category来查询用户对应的id@SuppressLint("Range")public void UsernameGetId(String htmlusername) {new Thread(() -> {Uri uri = Uri.parse("content://com.example.group5th.provider/username/" + htmlusername);Log.d("DENLU_Presenter", "当前登录用户名: " + htmlusername);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {try (Cursor cursor = mContext.getContentResolver().query(uri, null, null, null)) {if (cursor != null && cursor.moveToFirst()) {user_id = cursor.getInt(cursor.getColumnIndex("_id"));Log.d("DENLU_Presenter", "当前登录用户id: " + user_id);}}}Message message = Message.obtain();message.obj = user_id;message.what = WHAT_GET_ID;mHandler.sendMessage(message);}).start();}Handler mHandler = new WeakRefHandler<>(Looper.getMainLooper(), this, new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message msg) {if (msg.what == WHAT_PASSWORD_MATCH_RESULT) {mDengluView.showUserInfo(zhuantaima);} else if (msg.what == WHAT_GET_ID) {mDengluView.getuserid(user_id);}return false;}});
}
(3)创立spuitls类
这段代码是一个 Android SharedPreferences 工具类,用于简化应用中的轻量级数据存储操作核心功能
统一管理键值对存储:封装 SharedPreferences 的常用操作,提供类型安全的存取方法。
支持多种数据类型:包括基本类型、对象、集合等。
避免内存泄漏:通过合理使用 Context 和及时释放资源。
package com.example.activity;import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Base64;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.List;
import java.util.Set;public class SpUtils {private static final String SP_NAME = "sp_student_manager";public static final String USER_ID_KEY = "user_id_key";private Context mContext;private SharedPreferences mSharedPreferences;public SpUtils(Context context) {this(context, SP_NAME, Context.MODE_PRIVATE);}public SpUtils(Context context, String name, int mode) {mContext = context;mSharedPreferences = context.getSharedPreferences(name, mode);}// 保存用户ID的方法,使用int类型public void saveUserId(int userId) {SharedPreferences.Editor editor = mSharedPreferences.edit();editor.putInt(USER_ID_KEY, userId); // 使用putInt方法editor.apply();}// 获取用户ID的方法,返回int类型public int getUserId() {return mSharedPreferences.getInt(USER_ID_KEY, 0); // 使用getInt方法,可以设置默认值,这里使用0作为未设置时的默认值}// 清除用户ID的方法public void clearUserId() {SharedPreferences.Editor editor = mSharedPreferences.edit();editor.remove(USER_ID_KEY); // 清除键值对editor.apply();}// private static final String SP_NAME = "sp_student_manager";
// public static final String USERNAME_KEY = "username_key";
// private Context mContext;
// private SharedPreferences mSharedPreferences;
//
// public SpUtils(Context context) {
// this(context, SP_NAME, Context.MODE_PRIVATE);
// }
//
// public SpUtils(Context context, String name, int mode) {
// mContext = context;
// if (mSharedPreferences == null) {
// mSharedPreferences = context.getSharedPreferences(name, mode);
// }
// }
//
// // 保存登录用户名的方法
// public void saveUsername(String username) {
// SharedPreferences.Editor editor = mSharedPreferences.edit();
// editor.putString(USERNAME_KEY, username);
// editor.apply();
// }
//
// // 获取登录用户名的方法
// public String getUsername() {
// return mSharedPreferences.getString(USERNAME_KEY, null);
// }
//
// // 清除登录用户名的方法
// public void clearUsername() {
// SharedPreferences.Editor editor = mSharedPreferences.edit();
// editor.remove(USERNAME_KEY);
// editor.apply();
// }/*** 存入字符串** @param key 字符串的键* @param value 字符串的值*/public void putString(String key, String value) {//存入数据SharedPreferences.Editor editor = mSharedPreferences.edit();editor.putString(key, value);editor.apply();}/*** 获取字符串** @param key 字符串的键* @return 得到的字符串*/public String getString(String key) {return mSharedPreferences.getString(key, "");}/*** 获取字符串** @param key 字符串的键* @param value 字符串的默认值* @return 得到的字符串*/public String getString(String key, String value) {return mSharedPreferences.getString(key, value);}/*** 保存布尔值** @param key 键* @param value 值*/public void putBoolean(String key, boolean value) {SharedPreferences.Editor editor = mSharedPreferences.edit();editor.putBoolean(key, value);editor.apply();}/*** 获取布尔值** @param key 键* @param defValue 默认值* @return 返回保存的值*/public boolean getBoolean(String key, boolean defValue) {return mSharedPreferences.getBoolean(key, defValue);}/*** 保存long值** @param key 键* @param value 值*/public void putLong(String key, long value) {SharedPreferences.Editor editor = mSharedPreferences.edit();editor.putLong(key, value);editor.apply();}/*** 获取long值** @param key 键* @param defValue 默认值* @return 保存的值*/public long getLong(String key, long defValue) {return mSharedPreferences.getLong(key, defValue);}/*** 保存int值** @param key 键* @param value 值*/public void putInt(String key, int value) {SharedPreferences.Editor editor = mSharedPreferences.edit();editor.putInt(key, value);editor.apply();}/*** 获取long值** @param key 键* @param defValue 默认值* @return 保存的值*/public int getInt(String key, int defValue) {return mSharedPreferences.getInt(key, defValue);}/*** 保存对象** @param key 键* @param obj 要保存的对象(Serializable的子类)* @param <T> 泛型定义*/public <T extends Serializable> void putObject(String key, T obj) {try {put(key, obj);} catch (Exception e) {e.printStackTrace();}}/*** 获取对象** @param key 键* @param <T> 指定泛型* @return 泛型对象*/public <T extends Serializable> T getObject(String key) {try {return (T) get(key);} catch (Exception e) {e.printStackTrace();}return null;}/*** 存储List集合** @param key 存储的键* @param list 存储的集合*/public void putList(String key, List<? extends Serializable> list) {try {put(key, list);} catch (Exception e) {e.printStackTrace();}}/*** 获取List集合** @param key 键* @param <E> 指定泛型* @return List集合*/public <E extends Serializable> List<E> getList(String key) {try {return (List<E>) get(key);} catch (Exception e) {e.printStackTrace();}return null;}/*** 存储对象*/private void put(String key, Object obj)throws IOException {if (obj == null) {//判断对象是否为空return;}ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = null;oos = new ObjectOutputStream(baos);oos.writeObject(obj);// 将对象放到OutputStream中// 将对象转换成byte数组,并将其进行base64编码String objectStr = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT));baos.close();oos.close();putString(key, objectStr);}/*** 获取对象*/private Object get(String key)throws IOException, ClassNotFoundException {String wordBase64 = getString(key);// 将base64格式字符串还原成byte数组if (TextUtils.isEmpty(wordBase64)) { //不可少,否则在下面会报java.io.StreamCorruptedExceptionreturn null;}byte[] objBytes = Base64.decode(wordBase64.getBytes(), Base64.DEFAULT);ByteArrayInputStream bais = new ByteArrayInputStream(objBytes);ObjectInputStream ois = new ObjectInputStream(bais);// 将byte数组转换成product对象Object obj = ois.readObject();bais.close();ois.close();return obj;}/*** 保存String set集合** @param key 键* @param value 值*/public void putStringSet(String key, Set<String> value) {SharedPreferences.Editor editor = mSharedPreferences.edit();editor.putStringSet(key, value);editor.apply();}/*** 获取String set集合** @param key 键* @param defValue 默认值* @return 保存的值*/public Set<String> getStringSet(String key, Set<String> defValue) {return mSharedPreferences.getStringSet(key, defValue);}/*** 清除指定数据** @param key 键*/public void remove(String key) {SharedPreferences.Editor editor = mSharedPreferences.edit();editor.remove(key);editor.apply();}/*** 清除数据*/public void clear() {SharedPreferences.Editor editor = mSharedPreferences.edit();editor.clear();editor.apply();}
}
(4)创建DengLuActivity类
这里没什么好说的,跟zhuce的方法几乎一致
html代码:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登录</title><style>body {margin: 0;padding: 0;background: url(../pic/背景.jpg) no-repeat center center fixed;background-size: cover;font-family: Arial, sans-serif;color: #333;display: flex;justify-content: center;align-items: center;height: 100vh;}.div {width: 400px;max-width: 90%;height: auto;padding: 20px;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);border-radius: 10px;background: rgba(255, 255, 255, 0.4);}.head-div {width: 100px;height: 100px;border: 1px solid #999;border-radius: 50%;margin: 20px auto 10px auto;background: url(../pic/图标.jpg.jpg) no-repeat;background-size: 100px 100px;}.sign-div {width: 100%;text-align: center;outline: none;border-radius: 8px;box-sizing: border-box;padding: 20px;}.sign-div h1 {margin-bottom: 20px;color: rgb(29, 26, 26);font-size: 24px;}input {width: 80%;height: 44px;border: none;outline: none;box-sizing: border-box;display: block;padding: 0 16px;margin: 10px auto;border-radius: 22px;background: rgba(255, 255, 255, 0.9);box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);}.input-text:hover {border: 1px solid rgb(76, 76, 233);transition: 0.3s;}.input-btn {width: 80%;margin: 30px auto 20px;border-radius: 22px;cursor: pointer;background-color: rgba(84, 175, 249, 0.8);color: #fff;font-size: 16px;font-weight: bold;}.input-btn:hover {background-color: rgba(10, 138, 243, 0.8);transition: 0.3s;}.sign-div a {width: 80%;margin: 20px auto 0;display: block;text-decoration: none;color: rgb(92, 61, 112);font-size: 14px;padding: 10px;transition: 0.3s;border-radius: 22px;background-color: rgba(84, 175, 249, 0.2);box-sizing: border-box;}.sign-div a:hover {color: #fff;background-color: rgba(84, 175, 249, 0.8);}.error {color: red;font-size: 14px;margin-top: 10px;}@media (max-width: 600px) {.div {width: 90%;padding: 10px;}.sign-div h1 {font-size: 20px;}input {width: 100%;}.input-btn {width: 100%;}.sign-div a {width: 100%;}#message {color: red;margin-top: 10px;}}</style>
</head><body>
<div class="div"><div class="head-div"></div><div class="sign-div"><form class="" action="#" method="POST"><h1>登录</h1><input class="input-text" type="text" name="username" placeholder="请输入用户名"><input class="input-text" type="password" name="password" placeholder="请输入密码"><input class="input-text input-btn" type="button" value="登录" onclick="submitForm()"><input class="input-text input-btn" type="button" value="没有账号?去注册" onclick="navigateToLogin()"></form><div id="message"></div></div>
</div><script>function submitForm() {var username = document.getElementsByName("username")[0].value;var password = document.getElementsByName("password")[0].value;if (!username || !password) {showMessage("请填写所有信息");return;}// 使用Android JS桥接denglu.submitForm(username, password);}function navigateToLogin() {// 使用Android JS桥接denglu.navigateToLogin();}function showMessage(message) {// 使用Android JS桥接denglu.showMessage(message);}
</script>
</body>
</html>
xml代码:
<?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:padding="0dp"><WebViewandroid:id="@+id/denglu"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>
最后是java代码:
package com.example.activity;import static androidx.core.content.ContextCompat.startActivity;import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;import com.example.activity.R;
import com.example.activity.DengluContract;
import com.example.activity.DengluPresenter;
import com.example.activity.SpUtils;
import com.example.activity.MainActivity;//这里可有可无public class DengLu extends Activity implements DengluContract.IDengluView {private WebView webView;private DengluContract.IDengluPresenter mDengluPresenter;private String htmlpassword;private String htmlusername;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_deng_lu);// 检查是否已登录SpUtils spUtils = new SpUtils(this);int savedUserid = spUtils.getUserId();if (savedUserid != 0) {// 用户已登录,跳转到主页面Intent intent = new Intent(DengLu.this, MainActivity.class);startActivity(intent);finish();return;}webView = (WebView) findViewById(R.id.denglu);WebSettings webSettings = webView.getSettings();webSettings.setJavaScriptEnabled(true); // 如果需要JavaScript,启用它// 确保WebView在加载页面时不会调用其他浏览器webView.setWebViewClient(new WebViewClient());// 添加JavaScript接口webView.addJavascriptInterface(new DengLu.WebAppInterface(), "denglu");// 加载一个HTML页面String htmlPath = "file:///android_asset/html/登录.html";webView.loadUrl(htmlPath);//初始化DengluPresentermDengluPresenter = new DengluPresenter(this, this);}@Overridepublic void getuserid(int user_id) {//把查到的user_ID存到SpUtils实现持久化登录SpUtils spUtils = new SpUtils(getApplicationContext());spUtils.saveUserId(user_id);Log.d("TAG", "user_ID:"+user_id);}@Overridepublic void showUserInfo(int zhuantaima) {if (zhuantaima == 0) {Intent intent = new Intent(DengLu.this, MainActivity.class);startActivity(intent);Log.d("TGP", "登录成功");//根据登录成功的htmlusername查询对应的idmDengluPresenter.UsernameGetId(htmlusername);//把值存到SpUtils//SpUtils spUtils = new SpUtils(getApplicationContext());//spUtils.saveUsername(htmlusername);} else if (zhuantaima == 1) {showMessage("登录失败,密码错误!");Log.d("TGP", "登录失败,密码错误");} else if (zhuantaima == 2) {showMessage("登录失败,账号未注册!");Log.d("TGP", "登录失败,账号未注册!");}}private void showMessage(String message) {runOnUiThread(() -> {new AlertDialog.Builder(DengLu.this).setMessage(message).setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();}}).show();});}public class WebAppInterface {@JavascriptInterfacepublic void submitForm(String username, String password) {htmlusername = username;htmlpassword = password;//把username值传给dengluPresentermDengluPresenter.Dengluyanzheng(htmlusername, htmlpassword);}@JavascriptInterfacepublic void navigateToLogin() {Intent intent = new Intent(DengLu.this, ZhuCe.class);startActivity(intent);}@JavascriptInterfacepublic void showMessage(String message) {runOnUiThread(() -> {new AlertDialog.Builder(DengLu.this).setMessage(message).setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();}}).show();});}}
}
效果图:
这样,一个登录系统就做好了。
三、尾言
这里的代码是配合项目使用的,所以可能最后运行时会闪退,但可以通过一定量的删减达到想要的结果,本次的工程量巨大,建议先复制自己去运行一下,之后再去学习,我也会把资源发出来,感谢大家的支持。
相关文章:
Android studio组合教程--做出一个类似于QQ的登录页面
之前我们学过了html与Android的开发,以及各种组件的学习,这次我们做一个完整向的登录页面,作为一次大作业。 注意 里面的一图片可以自由发挥,但要注意文件路径保持准确,这里给出参考路径: 背景路径&…...
iPhone 16 Plus :凉凉了
大屏就是生产力,这句话就像思想钢印一样,深入人心。 但苹果用户是个例外,根据内行人的爆料,iPhone 16 Plus 彻底凉凉了,难怪它会是最后一代Plus。 根据知名博主数码闲聊站透露,截止3 月 9 号,i…...
【MySQL报错】:Column count doesn’t match value count at row 1
MySQL报错:Column count doesn’t match value count at row 1 意思是存储的数据与数据库表的字段类型定义不相匹配. 由于类似 insert 语句中,前后列数不等造成的 主要有3个易错点: 要传入表中的字段数和values后面的值的个数不相等。 由于类…...
2025 polarctf春季个人挑战赛web方向wp
来个弹窗 先用最基础的xss弹窗试一下 <script>alert("xss")</script>没有内容,猜测过滤了script,双写绕过一下 <scrscriptipt>alert("xss")</scscriptript>background 查看网页源代码 查看一下js文件 类…...
Midscene.js自然语言驱动的网页自动化全指南
一、概述 网页自动化在数据抓取、UI 测试和业务流程优化中发挥着重要作用。然而,传统工具如 Selenium 和 Puppeteer 要求用户具备编程技能,编写复杂的选择器和脚本维护成本高昂。Midscene.js 通过自然语言接口革新了这一领域,用户只需描述任…...
PDF与Markdown的量子纠缠:一场由VLM导演的文档界奇幻秀
缘起:当格式界的"泰坦尼克号"撞上"黑客帝国" 某个月黑风高的夜晚,在"二进制酒吧"的霓虹灯下: PDF(西装革履地晃着威士忌): “我的每一页都像瑞士手表般精密,连华尔街的秃鹫都为我倾倒!” Markdown(穿着带洞的拖鞋): “得了吧老古董!…...
Spring Boot JSON序列化深度管控:忽略指定字段+Jackson扩展策略破解双向实体循环引用问题
一、JsonIgnore的核心原理与工作机制 1. 注解作用原理 JsonIgnore是Jackson库的核心注解之一,其工作原理基于 Jackson的AnnotationIntrospector机制。在序列化/反序列化过程中,Jackson会扫描Java对象的所有字段和方法上的注解。当检测到JsonIgnore时&a…...
msvcp140.dll是什么文件?修复丢失msvcp140.dll的方法指南
当计算机显示"msvcp140.dll未找到"的报错信息时,这实际反映了Windows系统运行机制中的一个关键环节出现断链。作为Microsoft Visual C可再发行组件包的核心动态链接库,msvcp140.dll承担着程序与系统资源之间的桥梁作用,特别是在处理…...
ES集群的部署
实验步骤 实验目的: 验证ES集群的容错性、扩展性数据分布与查询性能优化。 环境准备 1、准备两台服务器 服务器 1、10.1.1.20 cpu 2核 内存:4G 硬盘100G 2、10.1.1.21 cpu 2核 内存:4G 硬盘100G 2、修改两台静态ip 3、关闭防…...
resetForm() 方法用于重置表单
resetForm() 方法是 Vue.js 中用于重置表单的一个常见操作。下面是对这段代码的详细解析: 1. 代码作用 resetForm() 方法的作用是重置表单,将表单中的所有输入字段恢复到初始状态(通常是清空或恢复到默认值)。 2. 代码解析 re…...
Java后端API限流秘籍:高并发的防护伞与实战指南
目录导航 📜 🛡️ 为什么需要API限流?🧠 主流限流算法大解析👩💻 阿里巴巴的限流实践📏 四大黄金定律🤼 限流策略组合拳🏆 限流场景实战💻 技术实现方案🌟 最佳实践分享📈 结语与展望📚 推荐阅读 1. 🛡️ 为什么需要API限流? 在高并发环境中,未…...
团体协作项目总结Git
使用Git开放时候发现本地, 有些代码并没有被拉取到本地仓库, 又不想再commit一次, 这时候我就想到了 git commit --amend 合并提交 git commit --amend 修改git提交记录用法详解 可以将本次提交记录合并到上一次合并提交 git commit --amendgit rebase -i master^^ // 假设我…...
mysql 入门
1.已经下载过却卸载不干净?注册表清理不到位? 使用greek绿色版 强力卸载,可以一键卸载注册表里的信息。 2.如何启动mysql服务? 以管理员方式启动cmd 输入 net start mysql80 如何停止? net stop mysql80 2.将mysql客…...
1.基于TCP的简单套接字服务器实现
目录 1. TCP通信流程 2. 服务器端的通信流程 2.1 创建用于监听的套接字 2.2 绑定本地IP地址和端口 2.3 设置监听 2.4 等待接受客户端的连接请求 2.5 与客户端进行通信 2.6 客户端连接服务器 3.代码实现 client.cpp server.cpp 运行方式 在本文中,我们将…...
MantisBT在Windows10上安装部署详细步骤
MantisBT 是一款基于 Web 的开源缺陷跟踪系统,以下是在 Windows 10 上安装部署 MantisBT 的详细步骤: 1. 安装必要的环境 MantisBT 是一个基于 PHP 的 Web 应用程序,因此需要安装 Web 服务器(如 Apache)、PHP 和数据…...
zookeepernacoskafka之间的联系
一、ZooKeeper与Kafka的协同工作原理 1. 核心关系:Kafka对ZooKeeper的依赖 在Kafka 2.8版本之前,ZooKeeper是Kafka集群的“大脑”,负责管理集群元数据、协调节点状态和故障恢复。两者的协同主要通过以下关键机制实现: Broker注册…...
【QT】 布局器
参考博客:https://blog.csdn.net/Fdog_/article/details/107522283 目录 布局管理器概念常见的布局管理器及特点🔵QHBoxLayout水平布局🔵QVBoxLayout垂直布局 🔵QGridLayout网格布局 🔵QFormLayout表单布局 QT 高级布…...
力扣45.跳跃游戏
45. 跳跃游戏 II - 力扣(LeetCode) 代码区: #include<vector> class Solution {public:int jump(vector<int>& nums) {int ans[10005] ;memset(ans,1e4,sizeof(ans));ans[0]0;for(int i0;i<nums.size();i){for(int j1;j…...
【蓝桥杯】真题 路径(数论+dp)
思路 求最小公倍数LCM问题很好求,这里看似是求图最短路径,实际上由于只有[i,i21]之间存在路径,所以用线性dp效率更高,当然用bfs,dijstra,floyed也可,毕竟是填空题。 code def gcd(a,b):if a …...
敏捷需求分析之INVEST原则
INVEST原则是什么 INVEST 是用户故事的六个核心标准,由敏捷教练 Bill Wake 提出,用于确保用户故事具备可执行性和价值导向性。 1. I - Independent(独立的) 含义:用户故事应独立于其他故事,避免依赖关系。问题:若故事 A 必须等待故事 B 完成才能开发,会导致进度阻塞。…...
Apache Flink技术原理深入解析:任务执行流程全景图
前言 本文隶属于专栏《大数据技术体系》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和参考文献请见大数据技术体系 思维导图 📌 引言 Apache Flink 作为一款高性能的分布式流处理引擎,其内部执行机制精妙而复杂。本文将…...
UE5小石子阴影在非常近距离才显示的问题
Unreal中采用LandscapeGrass生成的地形,在MovieRenderQueue中渲染时阴影显示距离有问题,在很近的时候才会有影子,怎么解决? 地面上通过grass生成的小石子的阴影只能在很近的时候才能显示出来,需要如下调整 r.Shadow.R…...
WebAssembly实践,性能也有局限性
个人博客原文地址 WebAssembly(简称 wasm) 是一种旨在突破 Web 性能瓶颈的技术方案。它由 W3C 官方推动,并且得到了主流浏览器的广泛支持。它的核心思想是通过运行其他高性能编程语言(比如 C、C、Rust 等)来实现复杂功…...
第一天学爬虫
阅读提示:我今天才开始尝试爬虫,写的不好请见谅。 一、准备工具 requests库:发送HTTP请求并获取网页内容。BeautifulSoup库:解析HTML页面并提取数据。pandas库:保存抓取到的数据到CSV文件中。 二、爬取步骤 发送请求…...
【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(11)
1.问题描述: 鸿蒙push右侧图表没有正常展示。 解决方案: .jpg格式文件,头信息必须是这个“jpg:ffd8”。 2.问题描述: 安卓端App在开发者平台申请了Android应用的通知消息自分类权益,鸿蒙应用的自分类权…...
Spring WebSecurityCustomizer 的作用
Spring WebSecurityCustomizer 是 Spring Security 框架中用来 自定义 Web 安全配置 的一个接口。 它的主要作用是在开发中我们能够 精细的控制哪些请求会被 Spring Security 完全忽略,不进行任何安全检查和过滤。 我们可以把它想象成是 Spring Security 大门上的一…...
uniapp动态循环表单校验失败:初始值校验
问题现象 💥 在实现动态增减的单价输入表单时(基于uv-form组件),遇到以下诡异现象: <uv-input>的v-model绑定初始值为数字类型时,required规则失效 ❌数字类型与字符串类型校验表现不一致 🔢技术栈背景 🛠️ 框架:Vue3 + uni-appUI库:uv-ui校验方案:计算属…...
线性代数核心概念与NumPy科学计算实战全解析
前言 学习方法: 思维导图,梳理 多记忆,函数名和功能,参数 学会应用,不要钻牛角尖 一、浅解线性代数 1.1标量 标量是一个只有大小没有方向的量。在数学上,标量通常表示为一个普通的数字,如质量…...
如何理解 Apache Iceberg 与湖仓一体(Lakehouse)?
一、什么是湖仓一体(Lakehouse)? 湖仓一体是一种融合了数据湖的灵活存储能力与数据仓库的高效分析功能的现代数据架构。它通过整合两者的优势,解决了传统架构的局限性,为企业数据处理提供了更全面的解决方案。 数据湖…...
若依框架二次开发——若依集成 JSEncrypt 实现密码加密传输方式
文章目录 一、问题场景二、相关技术介绍1. RSA 加密算法2. JSEncrypt三、实现步骤1. 前端加密处理2. 后端解密处理3. 登录逻辑处理四、测试流程1. 前端测试2. 后端测试3. 运行效果五、总结一、问题场景 在 RuoYi 系统中,默认情况下,用户在登录时会将明文密码直接传输到服务器…...
Rust Web 开发新选择:探索 Hyperlane 轻量级 HTTP 服务器框架
Rust Web 开发新选择:探索 Hyperlane 轻量级 HTTP 服务器框架 在 Web 开发领域,Rust 以其高性能和内存安全性逐渐受到关注。而在众多 Web 框架中,hyperlane 作为一款轻量级、高性能的 HTTP 服务器框架,正悄然成为 Rust 生态中的明…...
初识 模版 和 STL
前言 今天简单和大家分享一下C重要的两个内容,经过之前的学习我们已经了解了C的大致语法,接下来就是C相关的库和一些操作了,他们能极大地缩小我们C语言阶段的代码量,让写代码变得轻松起来。 1.关于模版 <1>泛型编程 我们学…...
加新题了,MySQL 8.0 OCP 认证考试 题库更新
MySQL 8.0 OCP 认证考试 题库更新 MySQL 8.0 Database Administrator 考试科目:1Z0-908 近期发现,MySQL OCP认证考试题库发生变化,出现了很多新题,对此,CUUG专门收集整理了最新版本的MySQL考试原题,并会给…...
26考研——树与二叉树_树、森林(5)
408答疑 文章目录 二、树、森林树的基本概念树的定义和特性树的定义树的特性 基本术语树的基本术语和概念祖先、子孙、双亲、孩子、兄弟和堂兄弟结点的层次、度、深度和高度树的度和高度分支结点和叶结点有序树和无序树路径和路径长度 森林的基本术语和概念森林的定义森林与树的…...
26考研——图_图的基本概念(6)
408答疑 文章目录 一、图的基本概念图的定义非空性非线性结构 顶点和边的表示顶点边 有向图 & 无向图有向图有向图 G 1 G_1 G1 的表示 无向图无向图 G 2 G_2 G2 的表示 简单图 & 多重图简单图多重图 顶点的度、入度和出度顶点的度有向图的度 路径、路径长度和回路…...
笔试面试01 c/c++
基础知识 什么是数据结构?请简要描述常见的数据结构类型。 数据结构是组织和存储数据的方式,以便于高效访问和修改。常见的数据结构包括: 数组:固定大小的线性数据结构,支持随机访问。 链表:由节点组成的线…...
2025清华大学:DeepSeek教程全集(PDF+视频精讲,共10份).zip
一、资料列表 第一课:Deepseek基础入门 第二课:DeepSeek赋能职场 第三课:普通人如何抓住DeepSeek红利 第四课:让科研像聊天一样简单 第五课:DeepSeek与AI幻觉 第六课:基于DeepSeek的AI音乐词曲的创造法 第…...
消息队列(Kafka及RocketMQ等对比联系)
目录 消息队列 一、为什么使用消息队列?消息队列有什么优点/缺点?介绍下Kafka、ActiveMQ、RabbitMQ、RocketMQ有什么优点缺点,如何取舍? 1.公司业务场景是什么,这个业务场景有什么挑战,如果不用MQ有什么麻…...
Go 语言 fmt 模块的完整方法详解及示例
以下是 Go 语言 fmt 模块的完整方法详解及示例,涵盖所有核心功能: 一、输出函数 将数据写入标准输出、文件或字符串。 1. Print / Println / Printf 功能 Print: 写入标准输出,不换行。Println: 写入标准输出并换行。Printf: 格式化写入标…...
Centos 7 安装VNC服务
Centos 7 安装VNC服务 1. 安装 TigerVNC2. 设置 VNC 密码3. 创建并配置 x0vncserver 服务4. 启用并启动服务5. 检查服务状态6. 配置防火墙7. 连接 VNC问题1:出现无法安装可能是镜像源导致的。手动配置镜像源清除 YUM 缓存并重新加载 1. 安装 TigerVNC 确保已安装 TigerVNC 服务…...
3.25-3 request断言
一.request断言 if断言 案例: import requests srequests.Session() url1"http://49.233.201.254:8080/cms/manage/loginJump.do" data1{userAccount:admin,loginPwd:123456} h1{"Content-Type":"application/x-www-form-urlencoded&…...
cmakelist中添加opencv
版本选择 qt的msvc,版本2019 opencv版本 4.5.3 配置了环境变量 x64下的v14中的bin 配置头文件 {"configurations": [{"name": "Win32","includePath": ["${workspaceFolder}","d:\\QT\\6.5.3\\msvc20…...
【003安卓开发方案调研】之ReactNative技术开发安卓
基于2025年最新行业动态和搜索资料,以下是针对国内使用React Native(RN)开发安卓应用的深度分析: 一、技术成熟度评估 1. 核心架构升级 新架构全面普及:2024年起,React Native的 新架构(Fabri…...
面试中如何回答性能优化的问题
性能问题和Bug不同,后者的分析和解决思路更清晰,很多时候从应用日志(文中的应用指分布式服务下的单个节点)即可直接找到问题根源,而性能问题,其排查思路更为复杂一些。 对应用进行性能优化,是一个系统性的工程,对工程师的技术广度和技术深度都有所要求。一个简单的应用…...
使用cursor开发java案例——springboot整合elasticsearch
安装elasticsearch 打开cursor,输入如下提示词 使用springboot整合elasticsearch。其中elasticsearch服务器ip:192.168.236.134 管理员用户名elastic 管理员密码 PdQy_xfR2yLhpok*MK_ 监听端口9200点Accept all 使用idea打开生成的项目 ࿰…...
CCF-CSP认证题目练习及其题解(4
【问题描述】 涛涛最近要负责图书馆的管理工作,需要记录下每天读者的到访情况。每位读者有一个编号,每条记录用读者的编号来表示。给出读者的来访记录,请问每一条记录中的读者是第几次出现。 【输入形式】 输入的第一行包含一个整数n&#x…...
Chrome 134 版本开发者工具(DevTools)更新内容
Chrome 134 版本开发者工具(DevTools)更新内容 一、隐私与安全面板 旧的 Security 面板已演变为隐私与安全面板,并新增了一个专注于隐私的部分。在该部分中,可以: 在 DevTools 打开时,临时限制第三方 Co…...
12届蓝桥杯—货物摆放
货物摆放 题目描述 小蓝有一个超大的仓库,可以摆放很多货物。 现在,小蓝有 nn 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。 小蓝希望所…...
oracle查询归档日志使用量
1.统计最近30天的数据 SELECT TRUNC(first_time, DD) "日期", SUM(blocks * block_size) / 1024 / 1024 / 1024 "大小(GB)" FROM v$archived_log WHERE first_time > SYSDATE - 30 -- 统计最近30天的数据 GROUP BY TRUNC(first_time, DD) ORDER BY 1 D…...
Redis 发布订阅
Redis 发布订阅 概述 Redis 发布订阅(Pub/Sub)是一种消息传递模式,允许应用在多个客户端之间进行通信。在Redis中,发布订阅允许客户端订阅一个或多个频道,并在这些频道上发布消息。其他订阅了相同频道的客户端会接收到这些消息。 核心概念 频道(Channels) 频道是发…...