如何定位 app is not installed 错误

这两天尝试着去了解下 Android 软件安全。我在 AVD 上一个 apk 无法被安装, 应该是与 apk 本身没有关系,因为这个 apk 就是我从豌豆荚上下载下来的,点击下一步后,直接提示 app is not installed ,在 Google 上搜索了一翻,为什么会出现这个问题,SO 上有提到一些原因,但是都不是我这里的情况。我重新搜索“app is not installed where is log”,这个问题 下面的一个回答提示了我使用

adb logcat

于是,使用上面命令,log 里面很清晰明显的暴露了问题所在。

感谢,logcat ,这是一只好猫。

Yii2 AssetBundle js 和 css 没有自动更新的问题

最近几天看了很多基于 Yii2 的一些开源项目。很多功能都在可以找到开源的以 Yii2 extension 的形式存在,集成到自己的项目中相当简单。这么说吧,基于这些组件,你可以写很少或者几乎不用写一行代码就可以搭建起来一套很好用的程序。常见的富文本编辑器、权限管理、导航栏、菜单等等这些都有很多不错的扩展可以直接集成。之前我一直以为PHP程序源码不含图片的话超过10M就很恐怖了,但最近几天接触的这些基于 Yii2 的项目,composer install 之后整个源码目录大小超过100M很常见。当然,这还没有 node 恐怖,npm install 之后,随随便便 200M+ 更常见。

以上是题外话。前段时间我就发现了如题的问题。那时没注意。今天仔细读了下相关源码,弄明白了这个问题的根源。

假设有如下 HelloAsset

<?php
namespace app\assets;

use yii\web\AssetBundle;

class HelloAsset extends AssetBundle
{

    public $sourcePath = '@app/hello_assets';

    public $js = [
        'hello.js',
        'js/hello2.js',
    ];

}

在视图文件里面注入这个 Bundle

<?php

\app\assets\HelloAsset::register($this);

?>

<h1>Hello, world!</h1>

问题表现为:当我们修改 hello.js 后,刷新页面,加载的是我们修改过后的最新的 js。但是当我们修改 js/hello2.js 的内容后,刷新页面,发现最新的 js 并没有出现在浏览器中。

一路追踪代码到 AssetManager.php 里面有个 hash 方法,这个方法会生成 bundle 对应的 assets 目录,代码如下:

    /**
     * Generate a CRC32 hash for the directory path. Collisions are higher
     * than MD5 but generates a much smaller hash string.
     * @param string $path string to be hashed.
     * @return string hashed string.
     */
    protected function hash($path)
    {
        if (is_callable($this->hashCallback)) {
            return call_user_func($this->hashCallback, $path);
        }
        $path = (is_file($path) ? dirname($path) : $path) . filemtime($path);
        return sprintf('%x', crc32($path . Yii::getVersion()));
    }

上面方法的参数 $path 就是 AssetBundle::$sourcePath 应用 Yii::getAlias 得到的路径。可以看到这个方法的逻辑很清晰,如果没有配置 hashCallback,默认生成 hash 与 $path 的最近一次修改时间相关。怀疑问题出现在这里。

linux 下用 stat 命令可以获取文件的最近访问更改以及改动时间:

$ stat hello_assets 
  文件:"hello_assets"
  大小:4096      	块:8          IO 块:4096   目录
设备:fc00h/64512d	Inode:1321721     硬链接:3
权限:(0775/drwxrwxr-x)  Uid:(  900/ vagrant)   Gid:(  900/ vagrant)
最近访问:2016-03-16 17:46:07.011451000 +0800
最近更改:2016-03-16 17:46:06.995443000 +0800
最近改动:2016-03-16 17:46:06.995443000 +0800
创建时间:-

当我更改 hello.js 的内容后再次运行 stat 命令:

$ stat hello_assets 
  文件:"hello_assets"
  大小:4096      	块:8          IO 块:4096   目录
设备:fc00h/64512d	Inode:1321721     硬链接:3
权限:(0775/drwxrwxr-x)  Uid:(  900/ vagrant)   Gid:(  900/ vagrant)
最近访问:2016-03-16 17:56:31.279429000 +0800
最近更改:2016-03-16 17:56:31.271425000 +0800
最近改动:2016-03-16 17:56:31.271425000 +0800
创建时间:-

很明显,最近改动时间已经变了,这点是没问题的。

当我更改 js/hello2.js 的内容后再运行 stat:

$ stat hello_assets            
  文件:"hello_assets"
  大小:4096      	块:8          IO 块:4096   目录
设备:fc00h/64512d	Inode:1321721     硬链接:3
权限:(0775/drwxrwxr-x)  Uid:(  900/ vagrant)   Gid:(  900/ vagrant)
最近访问:2016-03-16 17:56:31.279429000 +0800
最近更改:2016-03-16 17:56:31.271425000 +0800
最近改动:2016-03-16 17:56:31.271425000 +0800
创建时间:-

可以看到这时 stat hello_assets 的最近改动时间并没有变化。

所以得出结论,当我们更改 $sourcePath 下面再下一级目录下的文件时,$sourcePath 目录的 filemtime 时间并不会改变,因此 hash 函数生成的哈希值也就没有变化。进一步,AssetManager 就没有再进行 publish 资源文件。

解决这个问题的一个方案就是更换生成哈希的实现。我做了一个简单的实现,在配置文件里面 components 项配置 assetManager 的 hashCallback 属性

$config = [
    /*...*/
    'components' => [
        /*...*/
        'assetManager' => [
            'hashCallback' => function ($path) {

                if (!function_exists('_myhash_')) {
                    function _myhash_($path) {
                        if (is_dir($path)) {
                            $handle = opendir($path);
                            $hash = '';
                            while (false !== ($entry = readdir($handle))) {
                                if ($entry === '.' || $entry === '..') {
                                    continue;
                                }
                                $entry = $path . '/' . $entry;
                                $hash .= _myhash_($entry);
                            }
                            $result = sprintf('%x', crc32($hash . Yii::getVersion()));
                        } else {
                            $result = sprintf('%x', crc32(filemtime($path) . Yii::getVersion()));
                        }
                        return $result;
                    }
                }

                return _myhash_($path);
            }
        ],
        /*...*/
    ],
    /*...*/
];

 

Is this php’s bug?

I am reading yii2’s source code right now. I found that this method yii\di\Instance::ensure something werid。

I changed the code as following:

public static function ensure($reference, $type = null, $container = null)
{
// I added three lines
     $container = null;
     var_dump(get_class($container));
     die;
// original code
}

I got output as :

string(15) “yii\di\Instance”

Can anyone explain?

#Answer

Just now(2016-04-12 15:20:58),I asked Laruence about this questing in qq group.  He answered me that get_calss(null) will return current scope. Then I read the php documetation , It says:

If object is omitted when inside a class, the name of that class is returned.

So, RTFM si right.