雷电模拟器安装Xposed

1.下载工具包

2.解压工具包到D盘根目录

3.打开雷电模拟器,执行Shell命令

adb remount /
adb push D:\xposed /system
adb shell
cd /system/xposed
chmod 777 script.sh
sh script.sh

4.安装成功

**************************
Xposed framework installer
**************************
- Checking environment
 Xposed version: 89
- Placing files
cp: bad 'system/priv-app/XposedInstaller/XposedInstaller.apk': No such file or directory
chmod: /system/priv-app/XposedInstaller/XposedInstaller.apk: No such file or directory
chcon: /system/priv-app/XposedInstaller/XposedInstaller.apk: No such file or directory
- Done
```

参考链接:

雷电模拟器4.0(android 7.1 x86_64) xposed安装

雷电安卓模拟器解决Could not load available ZIP files.Pull down to try again问题

获取Android设备上的USB设备

import android.content.Context;
import android.os.storage.StorageManager;

import java.lang.reflect.Method;

public class USBUtil {

    public static String getUSBPath(Context context) {
        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
        try {
            // 通过反射调用getVolumeList,获取所有挂载的设备(内部sd卡、外部sd卡、挂载的U盘)
            Method getVolumeListMethod = StorageManager.class.getMethod("getVolumeList");
            Object[] volumes = (Object[]) getVolumeListMethod.invoke(storageManager);

            Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
            // 通过反射调用getPath、isRemovable
            Method getPathMethod = storageVolumeClazz.getMethod("getPath");
            Method isRemovableMethod = storageVolumeClazz.getMethod("isRemovable");
            if (volumes != null && volumes.length > 0) {
                for (Object volume : volumes) {
                    String path = (String) getPathMethod.invoke(volume); // 获取路径
                    boolean isRemovable = (boolean) isRemovableMethod.invoke(volume);// 是否可移除
                    if (isRemovable) {
                        return path;
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return "/";
    }
}

参考链接:
获取Android设备上的所有存储设备

Android Fragment 重叠问题

缘起

首先,笔者的主Activity中包含了多个Fragment,通过show、hide来切换展示。当笔者切换到其他的APP,操作一段时间后,返回到当前APP,有很大几率会出现Fragment重叠现象。

缘由

首先,在Activity的生命周期中,当Activity不在前台展示时,如果此时其他的APP需要使用内存,系统会杀掉该APP的进程,当用户重新进入该APP时,系统会重新创建Activity。

Activity不在前台展示时会保存Fragment的状态,当系统重新创建Activity时会恢复之前保存的Fragment的状态。

缘解

方案一:在Activity的onCreate方法中判断savedInstanceState是否为空,不为空则使用系统保存的Fragment。

public class MainActivity extends AppCompatActivity {
    Fragment fragment;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            fragment = getSupportFragmentManager().findFragmentByTag("fragment");
        } else {
            fragment = new Fragment();
            getSupportFragmentManager().beginTransaction().add(R.id.frameLayout, fragment, "fragment").commit();
        }
    }
}

方案二:在Activity的onCreate方法中,调用super方法时直接传null,不使用保存的状态。

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(null);
}

参考资料:

Android fragment 重叠问题——通过hide,show方式导致的解决方法

解决Activity重新刷新后Fragment的show、hide失效问题

深入理解Activity的生命周期

TouchDelegate相关文章

Android鲜为人知的TouchDelegate

Android使用TouchDelegate增加View的触摸范围

ListView Tips & Tricks #5: Enlarged Touchable Areas

TouchDelegateGroup

import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.TouchDelegate;
import android.view.View;

import androidx.annotation.NonNull;

import java.util.ArrayList;

public class TouchDelegateGroup extends TouchDelegate {
    private ArrayList<TouchDelegate> mTouchDelegates = new ArrayList<>();
    private TouchDelegate mCurrentTouchDelegate;

    public TouchDelegateGroup(@NonNull Rect bounds, @NonNull View delegateView) {
        super(bounds, delegateView);
        mTouchDelegates.add(this);
    }

    public void addTouchDelegate(@NonNull TouchDelegate touchDelegate) {
        mTouchDelegates.add(touchDelegate);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        TouchDelegate delegate = null;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                for (TouchDelegate touchDelegate : mTouchDelegates) {
                    if (onDelegateTouchEvent(touchDelegate, event)) {
                        mCurrentTouchDelegate = touchDelegate;
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                delegate = mCurrentTouchDelegate;
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                delegate = mCurrentTouchDelegate;
                mCurrentTouchDelegate = null;
                break;
        }
        if (delegate != null) {
            return onDelegateTouchEvent(delegate, event);
        }
        return false;
    }

    public boolean onSelfTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }

    public boolean onDelegateTouchEvent(TouchDelegate touchDelegate, MotionEvent event) {
        if (touchDelegate instanceof TouchDelegateGroup) {
            return ((TouchDelegateGroup) touchDelegate).onSelfTouchEvent(event);
        } else {
            return touchDelegate.onTouchEvent(event);
        }
    }
}