zoukankan      html  css  js  c++  java
  • Android学习 问题与答案 (2)

    APK 之间共享 SharedPreferences

    关于数据存储,使用 ContentProvider 比使用 SharedPreferences 共享数据更安全些。
    注意一下 SharedPreferences 的 apply() 与 commit() 的区别。
    先在另外一个应用程序 B 里创建,然后在自己的应用 A 里访问。

    SharedPreferences pref=getActivity().getSharedPreferences(Settings.PREFS_NAME,
    			Context.MODE_WORLD_READABLE|Context.MODE_WORLD_WRITEABLE);
    

    这里以可读可写的形式创建,否则 A 访问不到 B 中的 SharedPreferences。
    查看权限文件 busybox ls -l /data/data/com.android.settings/shared_prefs/custom_preferences.xml
    -rw-rw-rw-    1 1000     1000          321 May  6 02:10 /data/data/com.android.settings/shared_prefs/custom_preferences.xml

    public static SharedPreferences getPreferences(Context context)
    {
      try {
        Context settings=context.createPackageContext("com.android.settings", Context.MODE_WORLD_READABLE);
        return settings.getSharedPreferences(PREFS_NAME, 
                        Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS);
      }catch(NameNotFoundException e) {
        e.printStackTrace();
        Log.e(TAG,"404 Settings app not found");
                
        return PreferenceManager.getDefaultSharedPreferences(context);
      }
    }

    这里别忘记添加 Context.MODE_MULTI_PROCESS 属性(Gingerbread (Android 2.3)后需要显式声明)。如果没有的话,在 B 应用里修改 SharedPreferences 后将不会更新到 A 应用里,也就是获取的仍是旧值。

    没有 USB 线,使用串口通过网络连接 ADB

    在 Settings 应用里,进入以太网配置,点击 DHCP 获取动态 IP 地址。
    minicom 终端输入命令,绑定端口号 5555,可以设置其他大的值(小于1024的端口号保留使用)
      setprop service.adb.tcp.port 5555
      stop adbd
      start adbd
    然后在终端输入命令 netcfg,将会获得如下内容
    eth0 192.168.1.125
    wlan 192.168.1.105 *(If wifi is up)

    接着重启 adb 服务
    adb kill-server
    adb connect  192.1.168.125
    adb devices
    根据显示的结果,连接局域网使用 192.1.168.125,连接 Wi-Fi 使用 192.1.168.105
    终端输入 adb 查看 connect 或其他命令的用法
    然后就会显示所有的设备。

    SQLite 批量数据处理

    如果从网络下载 XML 解析后,可能需要向数据库增添大量数据。一条一条记录得添加会相当慢,不断的文件打开和读写。
    可以设置为一次事务(transaction),先写入内存,然后一次提交更改,这样会快很多。

    SQLiteDatabase db=this.getWritableDatabase();
    db.beginTransaction();
    db.insert(sql); // thousands of records
    db.setTransactionSuccessful();
    db.endTransaction();
    db.close();
    

    使用 Browser 打开某 URL,避免创建多个 TAB 页面

    这样是可行的,使用 EXTRA_APPLICATION_ID 标签可以保证 tab 的重用

    Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
    

    关于 Map 中 key/value 的遍历最有效的方式

    如果仅对 key 感兴趣:

    Map<String, Object> map = ...;
    for (String key : map.keySet()) {
        // ...
    }
    

    如果只需要使用 value:

    for (Object value : map.values()) {
        // ...
    }
    

    或者程序需要获取 key 和 value

    for (Map.Entry<String, Object> entry : map.entrySet()) {
        String key = entry.getKey();
        Object value = entry.getValue();
        // ...
    }
    

    警告:如果在遍历的过程中有增删操作,需要使用 Iterator 实现,上面的方法不可取。

    隐藏 Settings 应用中左边一列中的一项

    如果注释掉相关代码,会很繁琐。隐藏不让显示就行了。
    比如蓝牙功能没有实现,所以隐藏有关 Bluetooth 的设置。
    Packages/app/Settings/src/com/android/settings/Settings.java 中有这么一句

    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
        target.remove(header);
    }
    

    继续溯源,追 frameworks/base/core/java/android/content/pm/PackageManager.java
        public abstract boolean hasSystemFeature(String name);
    抽象方法,实现在哪里呢?
    frameworks/base/core/java/android/app/ApplicationPackageManager.java

    @Override
    public boolean hasSystemFeature(String name) {
        try {
            return mPM.hasSystemFeature(name);
        } catch (RemoteException e) {
            throw new RuntimeException("Package manager has died", e);
        }
    }

    mPM.hasSystemFeature(name) 经过 AIDL 实际上调用到文件 PackageManagerService.java

    public boolean hasSystemFeature(String name) {
        synchronized (mPackages) {
            return mAvailableFeatures.containsKey(name);
        }
    }
    
    else if ("feature".equals(name)) {
    	String fname = parser.getAttributeValue(null, "name");
    	if (fname == null) {
    		Slog.w(TAG, "<feature> without name at "
    		        + parser.getPositionDescription());
    	} else {
    		//Log.i(TAG, "Got feature " + fname);
    		FeatureInfo fi = new FeatureInfo();
    		fi.name = fname;
    		mAvailableFeatures.put(fname, fi);
    	}
    	XmlUtils.skipCurrentTag(parser);
    	continue;
    }
    

    if ("feature".equals(name)) mAvailableFeatures.put(fname, fi);
    mAvailableFeatures 里面的内容是通过读取 /system/etc/permissions/ 下面的文件。而这些文件是从源码路径
    frameworks/base/data/etc/ 复制过来的,配置都在里面了,grep 一下 bluetooth,
    然后注释掉其 feature 重新编译源码,或者先去掉 /system/etc/permissions/handheld_core_hardware.xml 中的
    <feature name="android.hardware.bluetooth" /> 重启设备,看是否生效。

    播放 assets 文件夹里音频的文件

    使用酷狗音乐软件时,打开都会听到一个女声"Hello Kugou"。设想我们的问候语文件 greetings.mp3 存放在 Android 工程下的 asset 文件夹里。打开播放的代码如下:

    AssetFileDescriptor afd = getAssets().openFd("grettings.mp3");
    player = new MediaPlayer();
    //player.setDataSource(afd.getFileDescriptor()); // PROBLEM: it starts playing all the audio files in the assets directory
    player.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
    player.prepare();
    player.start();
    

    稍微注意一下上面的注释,你只是需要播放一个文件,而不是所有的音频文件。

    关于 getprop 命令

    需要获取某个 key 对应的 value,比如我之前的文章 Android 第三方 APK 包的静默安装 就用到了 getprop 命令。
    getprop 命令的代码 grep 一下,在 system/core/ 目录,或者 locate getprop.c 来得更快。
    获取局域网内自己的主机名:  adb shell getprop("net.hostname") 返回 android-364366301cf266e6
    关于设备名称的代码在这里 frameworks/base/services/java/com/android/server/ConnectivityService.java

    // setup our unique device name
    if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
        String id = Settings.Secure.getString(context.getContentResolver(),
                Settings.Secure.ANDROID_ID);
        if (id != null && id.length() > 0) {
            String name = new String("android-").concat(id);
            SystemProperties.set("net.hostname", name);
        }
    }

    这里提供两种方法调用 getprop,虽然都不怎么好。
    1. 利用 Runtime 调用 Shell 命令获取执行结果。Runtime.exec(new String[]{"getprop","net.hostname"});
    2. Android 其实提供了相关的 API 的(对应 android.os.SystemProperties),出于某某原因,对外隐藏(@hide)。 
    然后就归结到如何调用隐藏的 API 问题上了。之前的文章#获取屏幕的分辨率 就用过,就是采用 Java 的反射机制。
    这样做有点危险,因为可能以后 Android 会移除该方法,然后程序异常中止,得到 NoSuchMethodException
    参阅 Using internal (com.android.internal) and hidden (@hide) APIs ,这里有更详细的介绍,分 5 部分。


    View 转位图

    Android 的 SDK 自带可以截图的工具 hierarchyviewer,但是如果想在程序中获取某个 View 的图片并存储,可以这么做:

    view.setDrawingCacheEnabled(true);
    
    bitmap = Bitmap.createBitmap(view.getDrawingCache());
    // Save the bitmap wherever...
    
    view.setDrawingCacheEnabled(false);
    

    使用 setDrawingCacheEnabled 方法打开/关闭来获取 drawing cache。

    UTF-8 编码中 BOM 字符问题

    UTF-8 编码中的 字节顺序标记 (BOM) 字符会引起的 gcc 编译出错,文件开头有无法识别的字符。
    BOM(Byte Order Mark)需要批量删除,使用 grep + sed 命令即可完成。

    martin@M2037:$ grep -rIlo $'^\xef\xbb\xf' . | xargs sed --in-place -e 's/\xef\xbb\xbf//'

    grep  -r 递归搜索 -I 排除二进制文件的搜索 -l 搜索符合条件的文件 -o 仅打印符合条件的行,分行打印
    sed   --in-place 编辑文件,提供扩展名的话会备份原文件 -e 执行的表达式。 s/\xef\xbb\xbf// 是将串 \xef\xbb\xbf 替换为空,也就是去掉。
    一些文本编辑器会默认添加 BOM 头,比如 Windows 自带的记事本 Notepad。
    用 Windows 上的 notepad 或者 Ubuntu 上的 gedit 打开是看不到的,用 Notepad++.exe 或者 vim 就可以查看得到。
    BOM 有什么用呢?它是用来标记多字节构成的字符串时的字节序,如同 CPU 架构的大/小端模式样。

    adb pull /data/app/some-app.apk <SOMEWHERE>
    Google Play 无法单独下载 Android APP 安装包,只能在线安装。APK 安装到手机后,Android 系统会保存一份文件在 /data/app目录,带有数字后缀,名为“APK的包名-1.apk”或者“APK的包名-2.apk”。
    在某个 Android 设备上安装后,很想分享到其他 Android 设备。我们需要提取出这个包,但是在没有root Android 设备时,是无法查看 /data/app/ 目录下的内容的。好在这个目录有other组可读权限的。

    Android 有两个很有用的命令,分别是 am(管理 Activity 的)和 pm(管理 Pakcage 的),不是所谓的上午和下午喔。 进入 adb shell 后,输入可以查看相信的使用信息。

    pm list packages: prints all packages, optionally only
    those whose package name contains the text in FILTER. Options:
    -f: see their associated file.
    -d: filter to only show disbled packages.
    -e: filter to only show enabled packages.
    -s: filter to only show system packages.
    -3: filter to only show third party packages.
    -i: see the installer for the packages.
    -u: also include uninstalled packages.

    使用 pm list package -f 可以获取所有包对应的文件名,用 grep 过滤一下,想要拽出的APK包的包名或关键字。
    $ adb shell pm list package -f | grep cloudream
    package:/data/app/com.cloudream.ishow-1/base.apk=com.cloudream.ishow
    $ adb pull /data/app/com.cloudream.ishow-1/base.apk

    就可以提取出来啦,当然,也可以提取 /system/app/ 下面的包哦。

    C:\Users\Administrator\Desktop>adb pull /system/app/Magnifier/Magnifier.apk
    [100%] /system/app/Magnifier/Magnifier.apk



  • 相关阅读:
    [转]WIBKIT技术资料
    WebKit学习要点
    提高IOS开发效率的常用网站、开源类库及工具
    【浏览器那些基础】Android平台有那些CPU类型
    深刻的理解
    Spring Boot 最流行的 16 条实践解读,值得收藏!
    MyBatis动态SQL(认真看看, 以后写SQL就爽多了)
    为什么很多 SpringBoot 开发者放弃了 Tomcat,选择了 Undertow?
    朋友,别告诉我你懂分布式事务!
    分布式锁用 Redis 还是 Zookeeper?
  • 原文地址:https://www.cnblogs.com/Martinium/p/android_learning2.html
Copyright © 2011-2022 走看看