如何防止Android SDK中的EditText被应用类外部访问
如何阻止外部应用访问SDK内EditText并限制监听
问题背景
我开发的SDK包含MYSDKActivity,其布局内有一个用于用户输入卡号的EditText。但集成该SDK的Android客户端可在自身Application类中通过ActivityLifecycleCallbacks,为该EditText注册TextWatcher以监听用户输入的卡号。需要实现仅允许SDK内部访问并操作此EditText,阻止外部应用对其调用或监听。
客户端监听示例代码
public class MyDemoApp extends Application implements Application.ActivityLifecycleCallbacks { @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(this); } @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {} @Override public void onActivityStarted(@NonNull Activity activity) {} @Override public void onActivityResumed(@NonNull Activity activity) { if (activity instanceof MYSDKActivity) { final TextInputLayout textInputLayout = activity.findViewById(R.id.my_sdk_edit_text); if (textInputLayout != null) { ViewGroup textInputLayoutViewGroup = ((ViewGroup) textInputLayout.getChildAt(0)); for (int i = 0; i < textInputLayoutViewGroup.getChildCount(); i++) { View child = textInputLayoutViewGroup.getChildAt(i); if (child instanceof TextInputEditText) { Log.v("USER Input", ((TextInputEditText) child).getText().toString()); ((TextInputEditText) child).addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { Log.v("USER Input", s.toString()); } @Override public void afterTextChanged(Editable s) {} }); } } } } } }
解决方案
1. 自定义EditText,限制外部调用权限
继承TextInputEditText,重写所有可能被外部操作的公开方法,通过检查调用栈或包名判断是否为SDK内部调用,仅允许内部执行逻辑:
public class MySDKEditText extends TextInputEditText { private static final String SDK_PACKAGE_PREFIX = "com.your.sdk.package"; public MySDKEditText(Context context) { super(context); } public MySDKEditText(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void addTextChangedListener(@Nullable TextWatcher watcher) { // 检查调用者是否属于SDK内部 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); boolean isInternalCall = false; for (StackTraceElement element : stackTrace) { if (element.getClassName().startsWith(SDK_PACKAGE_PREFIX)) { isInternalCall = true; break; } } if (isInternalCall) { super.addTextChangedListener(watcher); } // 外部调用则忽略,也可抛出SecurityException提示非法访问 } @Override public Editable getText() { // 同样的权限检查逻辑 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); boolean isInternalCall = false; for (StackTraceElement element : stackTrace) { if (element.getClassName().startsWith(SDK_PACKAGE_PREFIX)) { isInternalCall = true; break; } } if (isInternalCall) { return super.getText(); } return Editable.Factory.getInstance().newEditable(""); } }
布局中替换原EditText为这个自定义类,同时确保自定义类的访问修饰符为public但内部逻辑限制外部调用。
2. 隐藏EditText的ID并封装访问逻辑
- 不给EditText设置对外暴露的ID,使用内部私有ID(在SDK的
R.java中不对外公开,可通过混淆或不导出资源的方式实现)。 - 在
MYSDKActivity内部通过ViewBinding或私有方法持有EditText引用,仅对外提供必要的业务接口(比如设置卡号提示、获取加密后的卡号),而非直接暴露EditText实例。
3. 重写Activity的findViewById方法拦截外部访问
在MYSDKActivity中重写findViewById,阻止外部获取目标EditText:
public class MYSDKActivity extends AppCompatActivity { private static final int SDK_EDIT_TEXT_ID = R.id.my_sdk_edit_text; @Override public <T extends View> T findViewById(int id) { // 检查调用者是否为SDK内部 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); boolean isInternalCall = false; for (StackTraceElement element : stackTrace) { if (element.getClassName().startsWith("com.your.sdk.package")) { isInternalCall = true; break; } } if (id == SDK_EDIT_TEXT_ID && !isInternalCall) { return null; // 返回空阻止外部获取 } return super.findViewById(id); } }
4. 使用自定义ViewGroup封装EditText
创建一个自定义ViewGroup,将EditText作为内部子View,不对外提供任何获取子View的方法,SDK内部通过私有方法操作EditText:
public class CardInputContainer extends LinearLayout { private MySDKEditText cardEditText; public CardInputContainer(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.layout_card_input, this, true); cardEditText = findViewById(R.id.private_card_edit_text); } // 仅对外提供业务方法,比如设置卡号提示 public void setCardHint(String hint) { cardEditText.setHint(hint); } // SDK内部通过私有方法获取EditText @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) MySDKEditText getCardEditText() { return cardEditText; } }
布局中使用这个容器替代原TextInputLayout+EditText的组合,外部无法通过遍历子View找到内部的EditText。
内容的提问来源于stack exchange,提问作者Kishan Maurya
相关产品推荐
相关产品推荐

