Android 位置更新最佳实践

监测所选位置提供器的状态,当其不可用时,动态切换到一个新的提供器,当有更适合的提供器可用时,切换到这个更适合的位置提供器

	private void unregisterAllListeners() {
		locationManager.removeUpdates(bestProviderListener);
		locationManager.removeUpdates(bestAvailableProviderListener);
	}
	
	private void registerListener() {
		unregisterAllListeners();
		String bestProvider = locationManager.getBestProvider(criteria, false);
		String bestAvailableProvider = locationManager.getBestProvider(criteria, true);
		
		if(bestProvider == null) {
			Log.d(TAG, "No Location Provider exist on device");
		} else if(bestProvider.equals(bestAvailableProvider)) {
			locationManager.requestLocationUpdates(bestAvailableProvider, minTime, minDistance, bestAvailableProviderListener);
		} else {
			locationManager.requestLocationUpdates(bestProvider, minUpdateTime, minUpdateDistance, bestProviderListener);
		}
		
		if(bestAvailableProvider != null) {
			locationManager.requestLocationUpdates(bestAvailableProvider, minTime, minDistance, bestAvailableProviderListener);
		} else {
			List<String> allProviders = locationManager.getAllProviders();
			for(String provider : allProviders)
				locationManager.requestLocationUpdates(provider, minTime, minDistance, bestProviderListener);
			Log.d(TAG, "No Location Providers currently available.");
		}
	}
	
	private LocationListener bestProviderListener = new LocationListener() {

		@Override
		public void onProviderEnabled(String provider) {
			registerListener();
		}
	}
	
	private LocationListener bestAvailableProviderListener = new LocationListener() {
			
		@Override
		public void onProviderEnabled(String provider) {
			registerListener();
		}
	}

ref: 《Android 4高级编程》 p.474

反编译Android APK

呃~~~ 反编译一般都是作学习用的-_-# OS:Android 4.4.2 HOST: OS X

需要的工具

baksmali / smali: 把odex转换成dex dex2jar: 把dex转换成jar jd-gui: 反编译jar

具体步骤

这里我尝试反编译SystemUI,先使用adb pull把SystemUI.apk和SystemUI.odex从手机中取出到本地的tmp目录中(什么是odex可以google一下)。 整个反编译的过程如下:

odex ----> dex ----> jar ----> java
  • odex —-> dex

    java -jar baksmali-2.0.3.jar -a 19 -x SystemUI.odex -d .
    

    参数说明:

    -a : API Level, 这里4.4.2的API Level为19
    -x : 反编译
    -d : 依赖包目录
    

    上面的命令会产生错误,是因为缺少依赖包,可以根据错误信息一次从手机中把依赖包pull到本地目录中。 当所以依赖都完整后,上面的命令会生成一个out文件夹,可以通过这些文件来生成dex。

    java -jar smali-2.0.3.jar -a 19 -o SystemUI.dex  out
    

    如果上面的命令执行成功,那么现在目录中就有了SystemUI.dex。

  • dex —-> jar

    d2j-dex2jar.sh SystemUI.dex
    

    上面的命令完成后会生成SystemUI-dex2jar.jar,最后就差使用JD-GUI来查看java文件了。

  • jar —-> java 安装正确的JD-GUI,打开上面生成的SystemUI-dex2jar.jar。

反编译Android APK

使用 ext4_utils 解压 Android Image

OS: Linux

  • 下载ext4_utils: make
  • simg2img system.img system.img.ext4
  • mount -o loop -t ext4 system.img.ext4 /mnt

ext4_utils

在 Android Framework 中增加 Native 库

OS: Android 4.4.2

在framework层使用Native lib和在application层的使用有所不同,主要集中在如何正确书写Android.mk和各种依赖关系,要比直接ndk-build麻烦一些。

场景: 在SystemUI包中添加一个Native库,假设名字为libxx.so

处理步骤:

  1. 进到frameworks/base/packages/SystemUI下创建JNI文件夹,这里存放所有libxx.so需要的文件。

    假设源文件名为xx.c。建立Android.mk文件,文件内容如下:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    
    LOCAL_LDFLAGS := -llog -ljnigraphics
    LOCAL_MODULE    := libxx
    LOCAL_SRC_FILES := xx.c
    LOCAL_CFLAGS    =  -ffast-math -O3 -funroll-loops
    
    ifeq ($(TARGET_ARCH), arm)
            LOCAL_SDK_VERSION := 9
    endif
    
    ifeq ($(TARGET_ARCH), x86)
            LOCAL_SDK_VERSION := 9
    endif
    
    ifeq ($(TARGET_ARCH), mips)
            LOCAL_SDK_VERSION := 9
    endif
    
    include $(BUILD_SHARED_LIBRARY)
    

    注意,如果要将libxx.so文件编译到system image,需要修改LOCAL_MODULE_TAGS(如果有),这个值必须为user,而不是samples,或者干脆去掉这个变量 。因为如果没有指定LOCAL_MODULE_TAGS,该项默认为user

  2. 为了能在编译SystemUI的时候自动编译libxx.so,需要在SystemUI的Android.mk文件中加入如下内容:

    ifneq (,$(TARGET_BUILD_APPS))
      LOCAL_JNI_SHARED_LIBRARIES := libxx
    else
      LOCAL_REQUIRED_MODULES := libxx
    endif
    

    Android对上面语句的解释如下:

    If this is an unbundled build (to install seprately) then include
    the libraries in the APK, otherwise just put them in /system/lib and
    leave them out of the APK
    
  3. 可以用make systemimage和mm SystemUI来测试效果。

Android 获取历史 Application 的截图

Android Framework中的WindowManagerService提供了获取历史Application截图的方法getTaskTopThumbnail,下面演示的只获取了最近一个app的Thumbnail。需要注意的是framework只提供了缩略图,没有提供完整的截图,但是我们可以通过在WindowManagerService中修改getTaskTopThumbnail来提供完整的Application截图。 另:这个方法只能截取Window属性为TYPE_APPLICATION的窗口,对于像属性为TYPE_KEYGUARD等窗口不能使用这个方法,可以通过Surface来截图。 这种方法的意义在于: 你可以获取一个处于pause或stop(可能被另一个窗口遮挡)状态的avtivity的截图,而使用Surface截图只能获取当前显示的画面(也就是framebuffer中的内容)。

 ActivityManager am = (ActivityManager) getContext().getSystemService(
                Context.ACTIVITY_SERVICE);

    private Bitmap takeScreenshot() {
        Bitmap bmp = null;

        final List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasksForUser(
                1, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
                UserHandle.CURRENT.getIdentifier());
        if (recentTasks.size() > 0) {
            ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(0);
            bmp = am.getTaskTopThumbnail(recentInfo.persistentId);
        }

        return bmp;
    }

ref: frameworks/base/services/java/com/android/server/wm/WindowManagerService.java