@TryLoveCatch
2021-04-07T13:25:31.000000Z
字数 3799
阅读 1685
android android基础 项目问题
在RecyclerView的itemView里面,xml直接设置背景,和在代码里面setBackground(),效果竟然不是一样的。
<?xml version="1.0" encoding="utf-8"?><com.xxx.view.CardContainer xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_margin="@dimen/dp_4.5"android:background="@drawable/selector_item_movie"android:focusable="true"android:focusableInTouchMode="false"android:gravity="center"android:padding="@dimen/dp_3"android:id="@+id/cardContainer"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"><ImageViewandroid:id="@+id/card_imageview"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/default_image_color"android:scaleType="centerCrop" />....</RelativeLayout></com.xxx.view.CardContainer>
<?xml version="1.0" encoding="utf-8" ?><selector xmlns:android="http://schemas.android.com/apk/res/android"><!--item 背景文件--><item android:state_focused="true" android:drawable="@drawable/stroke" /></selector>


位置模拟是地图类应用十分常用的调试功能,哆啦A梦在实现位置模拟功能时主要尝试了两种方案。
第一个方案是Android系统提供的LocationManager类下面TestProvider相关API,这个方案的实现非常容易,只需要调用相关的系统API。
它mock的不仅限于应用本身,也可以影响到其他应用,所以很多位置模拟软件都是使用这个方案实现的。
location.isFromMockProvider();第二个方案是通过Hook系统Binder服务的方式,动态代理Location Service。
public class LocationHookHandler implements InvocationHandler {private static final String TAG = "LocationHookHandler";private Object mOriginService;@SuppressWarnings("unchecked")@SuppressLint("PrivateApi")public LocationHookHandler(IBinder binder) {try {Class iLocationManager$Stub = Class.forName("android.location.ILocationManager$Stub");Method asInterface = iLocationManager$Stub.getDeclaredMethod("asInterface", IBinder.class);this.mOriginService = asInterface.invoke(null, binder);} catch (Exception e) {LogHelper.e(TAG, e.toString());}}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {switch (method.getName()) {case "requestLocationUpdates":...break;case "getLastLocation":...return lastLocation;case "getLastKnownLocation":...return lastKnownLocation;default:break;}return method.invoke(this.mOriginService, args);}}
上面的代码就是Location服务的代理类,通过替换原有requestLocationUpdates、getLastLocation和getLastKnownLocation接口的实现就可以实现模拟定位,返回我们想要模拟的位置,主要利用的就是InvocationHandler动态代理机制。
Android对系统服务主要是通过ServiceManager去管理的,且服务的实例是保存在静态全局变量中的。
public final class ServiceManager {private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();...public static IBinder getService(String name) {try {IBinder service = sCache.get(name);if (service != null) {return service;} else {return Binder.allowBlocking(getIServiceManager().getService(name));}} catch (RemoteException e) {Log.e(TAG, "error in getService", e);}return null;}...
服务实例保存在HashMap中,key是Context中定义的常量。
所以可以在应用初始化的时候提前替换掉sCache中的实例,这样后面通过context.getSystemService获取到的Service实例就是被动态代理的实例.
Class serviceManager = Class.forName("android.os.ServiceManager");Method getService = serviceManager.getDeclaredMethod("getService", String.class);IBinder binder = (IBinder) getService.invoke(null, Context.LOCATION_SERVICE);ClassLoader classLoader = binder.getClass().getClassLoader();Class[] interfaces = {IBinder.class};BinderHookHandler handler = new BinderHookHandler(binder);IBinder proxy = (IBinder) Proxy.newProxyInstance(classLoader, interfaces, handler);Field sCache = serviceManager.getDeclaredField("sCache");sCache.setAccessible(true);Map<String, IBinder> cache = (Map<String, IBinder>) sCache.get(null);cache.put(Context.LOCATION_SERVICE, proxy);sCache.setAccessible(false);
替换实例的时机需要尽可能早,这样才能保证在context.getSystemService前替换掉对应实例,所以在应用初始化的时机执行替换是比较推荐的。
