jjzjj

android - 在 JNI/OpenGL ES 加载代码期间,非常规和狡猾的 Android 崩溃

coder 2023-12-15 原文

赏金

因为这对我来说是一个重要的问题,所以我一直悬赏。我不是在寻找确切的答案——任何能让我解决这个问题的答案都会得到赏金。请确保您已经看到下面的编辑。

编辑:我已经设法在 Gdb 死机时捕获它的崩溃(通过“adb shell setprop debug.db.uid 32767”),并注意到这与 this post 中提到的问题完全相同。在 Google 网上论坛上。显示的回溯与我的崩溃线程相同(精确地址除外)。我承认,我不是调试工具向导,所以如果您对我应该寻找什么有任何想法,请告诉我。

快速而肮脏的破败

我删除了大部分相当大的应用程序代码,以便应用程序执行以下操作:通过 JNI 包装器(来自 C++ --> Java)加载一堆纹理,以便 Java 库为我处理解码,使 OpenGL 纹理脱离它们,并将屏幕清除为相当漂亮但模拟的深蓝色。它正在 libc 中消亡,但只有十分之一。

更糟糕的是,它甚至看起来与我编写的任何代码都没有关系——它似乎以延迟的方式发生,但它似乎与一些方便指责的东西无关作为垃圾收集器。我自己的代码中没有发生崩溃的特定点 - 它似乎在每次运行的基础上转移。

更长的故事

我最终得到了一个带有堆栈的标准崩溃转储,它告诉我几乎什么都没有,因为它有两个条目,一个是 libc,一个是看起来像无效或空堆栈帧的条目。 libc 中解析的符号是 pthread_mutex_unlock。我什至不再自己使用这个函数,因为我已经去掉了对多线程的需求。 ( native 代码在表面 View 中调用并仅呈现。)

pthread_mutex_unlock 会导致段错误,通常在地址 0,但有时是一个小值(小于 0x200)而不是 0。Bionic 中的默认(也是最常见的)互斥锁只有一个可以段错误的指针,这就是指向pthread_mutex_t 结构本身。但是,更复杂的互斥体(有多个选项)可能会使用额外的指针。所以,很可能 libc 很好,而 libdvm 有问题(假设我可以信任我的堆栈跟踪,甚至那么远)。

让我注意这个问题似乎只有在我执行以下两件事之一时才会重现:禁用图像数据部分的加载(但仍读取格式/尺寸信息),并使我用于将纹理加载到 OpenGL 的缓冲区未初始化,或者通过仅禁用最终的 glTexImage2D 调用来禁用 OpenGL 纹理的创建。

请注意,上述用于将纹理加载到 OpenGL 的缓冲区仅创建一次并销毁一次。我试过扩大它并确定我没有受到特定于该缓冲区的缓冲区溢出问题的困扰。

我能想到的罪魁祸首是:

  • 我没有正确使用 JNI,它对堆栈做了一些讨厌的事情。
  • 我在破坏堆栈帧的地方有一个逐一错误。
  • 我正在传递 OpenGL ES 一些不好的东西,它正在做一些同样糟糕的事情(tm)。
  • 我的自定义滚动内存分配器运行不正常。

  • 几天来,我一直在为这些罪魁祸首(以及更多!)梳理我的代码。我对使用调试器犹豫不决,因为这次崩溃似乎对时间很敏感。但是,在启用调试选项的情况下,我仍然可以使用我自己的 native 代码完全未优化的崩溃。 (gdb 本身以爬行方式运行,应用程序连接时也是如此)

    我做过的事
  • 使用 CheckJNI。
  • 尽可能多地精简代码,直到它停止崩溃。
  • 编写了一个信号处理程序并编写了一个小型日志记录系统来转储在抛出信号之前完成的最后一件事。
  • 试图(但失败)加剧了问题。
  • 用金丝雀在两端填充 native 堆数组。他们从未改变。
  • 对代码路径中的代码进行了 100% 的审计。 (我只是没有看到问题。)
  • 当我修复一个小错误时,我认为问题神奇地消失了,运行了 50 次代码以确保是这样,然后第二天我第一次运行时就崩溃了。 (哦,我以前从未对错误如此生气!)

  • 以下是来自 LogCat 的常见 native 崩溃信息的 fragment :
    I/DEBUG   ( 5818): signal 11 (SIGSEGV), fault addr 00000000
    I/DEBUG   ( 5818):  r0 0000006e  r1 00000080  r2 fffffc5e  r3 100ffe58
    I/DEBUG   ( 5818):  r4 00000000  r5 00000000  r6 00000000  r7 00000000
    I/DEBUG   ( 5818):  r8 00000000  r9 8054f999  10 10000000  fp 0013e768
    I/DEBUG   ( 5818):  ip 3b9aca00  sp 100ffe58  lr afd10640  pc 00000000  cpsr 60000010
    I/DEBUG   ( 5818):  d0  643a64696f72646e  d1  6472656767756265
    I/DEBUG   ( 5818):  d2  8083297880832965  d3  8083298880832973
    I/DEBUG   ( 5818):  d4  8083291080832908  d5  8083292080832918
    I/DEBUG   ( 5818):  d6  8083293080832928  d7  8083294880832938
    I/DEBUG   ( 5818):  d8  0000000000000000  d9  0000000000000000
    I/DEBUG   ( 5818):  d10 0000000000000000  d11 0000000000000000
    I/DEBUG   ( 5818):  d12 0000000000000000  d13 0000000000000000
    I/DEBUG   ( 5818):  d14 0000000000000000  d15 0000000000000000
    I/DEBUG   ( 5818):  d16 0000000000000000  d17 3fe999999999999a
    I/DEBUG   ( 5818):  d18 42eccefa43de3400  d19 3fe00000000000b4
    I/DEBUG   ( 5818):  d20 4008000000000000  d21 3fd99a27ad32ddf5
    I/DEBUG   ( 5818):  d22 3fd24998d6307188  d23 3fcc7288e957b53b
    I/DEBUG   ( 5818):  d24 3fc74721cad6b0ed  d25 3fc39a09d078c69f
    I/DEBUG   ( 5818):  d26 0000000000000000  d27 0000000000000000
    I/DEBUG   ( 5818):  d28 0000000000000000  d29 0000000000000000
    I/DEBUG   ( 5818):  d30 0000000000000000  d31 0000000000000000
    I/DEBUG   ( 5818):  scr 80000012
    I/DEBUG   ( 5818): 
    I/DEBUG   ( 5818):          #00  pc 00000000  
    I/DEBUG   ( 5818):          #01  pc 0001063c  /system/lib/libc.so
    I/DEBUG   ( 5818): 
    I/DEBUG   ( 5818): code around pc:
    I/DEBUG   ( 5818): 
    I/DEBUG   ( 5818): code around lr:
    I/DEBUG   ( 5818): afd10620 e1a01008 e1a02007 e1a03006 e1a00005 
    I/DEBUG   ( 5818): afd10630 ebfff95d e1a05000 e1a00004 ebffff46 
    I/DEBUG   ( 5818): afd10640 e375006e 03a0006e 13a00000 e8bd81f0 
    I/DEBUG   ( 5818): afd10650 e304cdd3 e3043240 e92d4010 e341c062 
    I/DEBUG   ( 5818): afd10660 e1a0e002 e24dd008 e340300f e1a0200d 
    I/DEBUG   ( 5818): 
    I/DEBUG   ( 5818): stack:
    I/DEBUG   ( 5818):     100ffe18  00000000  
    I/DEBUG   ( 5818):     100ffe1c  00000000  
    I/DEBUG   ( 5818):     100ffe20  00000000  
    I/DEBUG   ( 5818):     100ffe24  ffffff92  
    I/DEBUG   ( 5818):     100ffe28  100ffe58  
    I/DEBUG   ( 5818):     100ffe2c  00000000  
    I/DEBUG   ( 5818):     100ffe30  00000080  
    I/DEBUG   ( 5818):     100ffe34  8054f999  /system/lib/libdvm.so
    I/DEBUG   ( 5818):     100ffe38  10000000  
    I/DEBUG   ( 5818):     100ffe3c  afd10640  /system/lib/libc.so
    I/DEBUG   ( 5818):     100ffe40  00000000  
    I/DEBUG   ( 5818):     100ffe44  00000000  
    I/DEBUG   ( 5818):     100ffe48  00000000  
    I/DEBUG   ( 5818):     100ffe4c  00000000  
    I/DEBUG   ( 5818):     100ffe50  e3a07077  
    I/DEBUG   ( 5818):     100ffe54  ef900077  
    I/DEBUG   ( 5818): #01 100ffe58  00000000  
    I/DEBUG   ( 5818):     100ffe5c  00000000  
    I/DEBUG   ( 5818):     100ffe60  00000000  
    I/DEBUG   ( 5818):     100ffe64  00000000  
    I/DEBUG   ( 5818):     100ffe68  00000000  
    I/DEBUG   ( 5818):     100ffe6c  00000000  
    I/DEBUG   ( 5818):     100ffe70  00000000  
    I/DEBUG   ( 5818):     100ffe74  00000000  
    I/DEBUG   ( 5818):     100ffe78  00000000  
    I/DEBUG   ( 5818):     100ffe7c  00000000  
    I/DEBUG   ( 5818):     100ffe80  00000000  
    I/DEBUG   ( 5818):     100ffe84  00000000  
    I/DEBUG   ( 5818):     100ffe88  00000000  
    I/DEBUG   ( 5818):     100ffe8c  00000000  
    I/DEBUG   ( 5818):     100ffe90  00000000  
    I/DEBUG   ( 5818):     100ffe94  00000000  
    I/DEBUG   ( 5818):     100ffe98  00000000  
    I/DEBUG   ( 5818):     100ffe9c  00000000  
    

    使用 ndk r6,Android 平台 2.2(API 级别 8),编译时使用 -Wall -Werror,仅限 ARM 模式。

    我正在研究任何想法,尤其是那些可以确定性方式验证的想法。如果更多信息有帮助,请发表评论(如果不能,请回答),我会尽快更新我的问题。感谢您阅读到这里!

    JNI接口(interface)

    有 j2n 和 n2j 调用。现在唯一的 j2n 调用在这里:
    private static class Renderer implements GLSurfaceView.Renderer {
        public void onDrawFrame(GL10 gl) {
            GraphicsLib.graphicsStep();
        }
    
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GraphicsLib.graphicsInit(width, height);
        }
    
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            // Do nothing.
        }
    }
    

    这段代码通过这个接口(interface):
    public class GraphicsLib {
    
         static {
             System.loadLibrary("graphicslib");
         }
    
         public static native void graphicsInit(int width, int height);
         public static native void graphicsStep();
    }
    

    在 native 方面,它看起来像:
    extern "C" {
        JNIEXPORT void JNICALL FN(graphicsInit)(JNIEnv* env, jobject obj,  jint width, jint height);
        JNIEXPORT void JNICALL FN(graphicsStep)(JNIEnv* env, jobject obj);
    };
    

    函数定义本身以原型(prototype)的副本开始。

    graphicsInit 只是存储了它传递的尺寸并稍微设置了 OpenGL,没有任何特别有趣的东西。 graphicsStep 将屏幕清除为漂亮的颜色并调用 LoadSprites(env) .

    更复杂的一面由 LoadSprites() 中使用的 n2j 调用组成,它每帧加载一个 Sprite 。不是一个优雅的解决方案,但它一直在工作,但这次崩溃除外。

    LoadSprites 的工作方式如下:
    GameAssetsInfo gai;
    void LoadSprites(JNIEnv* env)
    {
        InitGameAssets(gai, env);
        CatchJNIException(env, "j0");
        ...
        static int z = 0;
        if (z < numSprites)
        {
            CatchJNIException(env, "j1");
            OpenGameImage(gai, SpriteIDFromNumber(z));
            CatchJNIException(env, "j2");
            unsigned int actualWidth = GetGameImageWidth(gai);
            CatchJNIException(env, "j3");
            unsigned int actualHeight = GetGameImageHeight(gai);
            CatchJNIException(env, "j4");
            ...
            jint i;
            int r = 0;
            CatchJNIException(env, "j5");
            do {
                CatchJNIException(env, "j6");
                i = ReadGameImage(gai);
                CatchJNIException(env, "j7");
                if (i > 0)
                {
                    // Deal with the pure data chunk -- One line at a time.
                    CatchJNIException(env, "j8");
                    StoreGameImageChunk(gai, (int*)sprites[z].data + r, 0, i);
                    ...
                    r += sprites[z].width;
                    CatchJNIException(env, "j9");
                    UnreadGameImage(gai);
                    CatchJNIException(env, "j10");
                } else {
                    break;
                }
            } while (true);
    
            CatchJNIException(env, "j11");
            CloseGameImage(gai);
            CatchJNIException(env, "j12");
    
            ... OpenGL ES calls ...
    
            glTexImage2D( ... );
    
            z++;
        }
    
        CatchJNIException(env, "j13");
    }
    

    CatchJNIException 在哪里(并且 从不 为我打印任何内容):
    void CatchJNIException(JNIEnv* env, const char* str)
    {
        jthrowable exc = env->ExceptionOccurred();
        if (exc) {
            jclass newExcCls;
            env->ExceptionDescribe();
            env->ExceptionClear();
            newExcCls = env->FindClass( 
                "java/lang/IllegalArgumentException");
            if (newExcCls == NULL) {
                // Couldn't find the exception class.. Uuh..
                LOGE("Failed to catch JNI exception entirely -- could not find exception class.");
                return;
                abort();
            }
            LOGE("Caught JNI exception. (%s)", str);
            env->ThrowNew( newExcCls, "thrown from C code");
    //      abort();
        }
    }
    

    而 GameAssetInfo 的相关部分和相关代码仅从 native 代码中调用,其工作方式如下:
    void InitGameAssets(GameAssetsInfo& gameasset, JNIEnv* env)
    {
        CatchJNIException(env, "jS0");
        FST;
        char str[64];
        sprintf(str, "%s/GameAssets", ROOTSTR);
    
        gameasset.env = env;
        CatchJNIException(gameasset.env, "jS1");
        gameasset.cls = gameasset.env->FindClass(str);
        CatchJNIException(gameasset.env, "jS2");
        gameasset.openAsset = gameasset.env->GetStaticMethodID(gameasset.cls, "OpenAsset", "(I)V");
        CatchJNIException(gameasset.env, "jS3");
        gameasset.readAsset = gameasset.env->GetStaticMethodID(gameasset.cls, "ReadAsset", "()I");
        CatchJNIException(gameasset.env, "jS4");
        gameasset.closeAsset = gameasset.env->GetStaticMethodID(gameasset.cls, "CloseAsset", "()V");
        CatchJNIException(gameasset.env, "jS5");
        gameasset.buffID = gameasset.env->GetStaticFieldID(gameasset.cls, "buff", "[B");
    
        CatchJNIException(gameasset.env, "jS6");
        gameasset.openImage = gameasset.env->GetStaticMethodID(gameasset.cls, "OpenImage", "(I)V");
        CatchJNIException(gameasset.env, "jS7");
        gameasset.readImage = gameasset.env->GetStaticMethodID(gameasset.cls, "ReadImage", "()I");
        CatchJNIException(gameasset.env, "jS8");
        gameasset.closeImage = gameasset.env->GetStaticMethodID(gameasset.cls, "CloseImage", "()V");
        CatchJNIException(gameasset.env, "jS9");
        gameasset.buffIntID = gameasset.env->GetStaticFieldID(gameasset.cls, "buffInt", "[I");
        CatchJNIException(gameasset.env, "jS10");
        gameasset.imageWidth = gameasset.env->GetStaticFieldID(gameasset.cls, "imageWidth", "I");
        CatchJNIException(gameasset.env, "jS11");
        gameasset.imageHeight = gameasset.env->GetStaticFieldID(gameasset.cls, "imageHeight", "I");
        CatchJNIException(gameasset.env, "jS12");
        gameasset.imageHasAlpha = gameasset.env->GetStaticFieldID(gameasset.cls, "imageHasAlpha", "I");
        CatchJNIException(gameasset.env, "jS13");
    }
    
    void OpenGameAsset(GameAssetsInfo& gameasset, int rsc)
    {
        FST;
        CatchJNIException(gameasset.env, "jS14");
        gameasset.env->CallStaticVoidMethod(gameasset.cls, gameasset.openAsset, rsc);
        CatchJNIException(gameasset.env, "jS15");
    }
    
    void CloseGameAsset(GameAssetsInfo& gameasset)
    {
        FST;
        CatchJNIException(gameasset.env, "jS16");
        gameasset.env->CallStaticVoidMethod(gameasset.cls, gameasset.closeAsset);
        CatchJNIException(gameasset.env, "jS17");
    }
    
    int ReadGameAsset(GameAssetsInfo& gameasset)
    {
        FST;
        CatchJNIException(gameasset.env, "jS18");
        int ret = gameasset.env->CallStaticIntMethod(gameasset.cls, gameasset.readAsset);
        CatchJNIException(gameasset.env, "jS19");
        if (ret > 0)
        {
        CatchJNIException(gameasset.env, "jS20");
            gameasset.obj = gameasset.env->GetStaticObjectField(gameasset.cls, gameasset.buffID);
        CatchJNIException(gameasset.env, "jS21");
            gameasset.arr = reinterpret_cast<jbyteArray*>(&gameasset.obj);
        }
        return ret;
    }
    
    void UnreadGameAsset(GameAssetsInfo& gameasset)
    {
        FST;
        CatchJNIException(gameasset.env, "jS22");
        gameasset.env->DeleteLocalRef(gameasset.obj);
        CatchJNIException(gameasset.env, "jS23");
    }
    
    void StoreGameAssetChunk(GameAssetsInfo& gameasset, void* store, int offset, int length)
    {
        FST;
        CatchJNIException(gameasset.env, "jS24");
        gameasset.env->GetByteArrayRegion(*gameasset.arr, offset, length, (jbyte*)store);
        CatchJNIException(gameasset.env, "jS25");
    }
    
    void OpenGameImage(GameAssetsInfo& gameasset, int rsc)
    {
        FST;
        CatchJNIException(gameasset.env, "jS26");
        gameasset.env->CallStaticVoidMethod(gameasset.cls, gameasset.openImage, rsc);
        CatchJNIException(gameasset.env, "jS27");
        gameasset.l_imageWidth = (int)gameasset.env->GetStaticIntField(gameasset.cls, gameasset.imageWidth);
        CatchJNIException(gameasset.env, "jS28");
        gameasset.l_imageHeight = (int)gameasset.env->GetStaticIntField(gameasset.cls, gameasset.imageHeight);
        CatchJNIException(gameasset.env, "jS29");
        gameasset.l_imageHasAlpha = (int)gameasset.env->GetStaticIntField(gameasset.cls, gameasset.imageHasAlpha);
        CatchJNIException(gameasset.env, "jS30");
    }
    
    void CloseGameImage(GameAssetsInfo& gameasset)
    {
        FST;
        CatchJNIException(gameasset.env, "jS31");
        gameasset.env->CallStaticVoidMethod(gameasset.cls, gameasset.closeImage);
        CatchJNIException(gameasset.env, "jS32");
    }
    
    int ReadGameImage(GameAssetsInfo& gameasset)
    {
        FST;
        CatchJNIException(gameasset.env, "jS33");
        int ret = gameasset.env->CallStaticIntMethod(gameasset.cls, gameasset.readImage);
        CatchJNIException(gameasset.env, "jS34");
        if ( ret > 0 )
        {
            CatchJNIException(gameasset.env, "jS35");
            gameasset.obj = gameasset.env->GetStaticObjectField(gameasset.cls, gameasset.buffIntID);
            CatchJNIException(gameasset.env, "jS36");
            gameasset.arrInt = reinterpret_cast<jintArray*>(&gameasset.obj);
        }
        return ret;
    }
    
    void UnreadGameImage(GameAssetsInfo& gameasset)
    {
        FST;
        CatchJNIException(gameasset.env, "jS37");
        gameasset.env->DeleteLocalRef(gameasset.obj);
        CatchJNIException(gameasset.env, "jS38");
    }
    
    void StoreGameImageChunk(GameAssetsInfo& gameasset, void* store, int offset, int length)
    {
        FST;
        CatchJNIException(gameasset.env, "jS39");
        gameasset.env->GetIntArrayRegion(*gameasset.arrInt, offset, length, (jint*)store);
        CatchJNIException(gameasset.env, "jS40");
    }
    
    int GetGameImageWidth(GameAssetsInfo& gameasset) { return gameasset.l_imageWidth; }
    int GetGameImageHeight(GameAssetsInfo& gameasset) { return gameasset.l_imageHeight; }
    int GetGameImageHasAlpha(GameAssetsInfo& gameasset) { return gameasset.l_imageHasAlpha; }
    

    它在 Java 方面得到了支持:
    public class GameAssets {
        static public Resources res = null;
        static public InputStream is = null;
        static public byte buff[];
        static public int buffInt[];
        static public final int buffSize = 1024;
        static public final int buffIntSize = 2048;
    
        static public int imageWidth;
        static public int imageHeight;
        static public int imageHasAlpha;
        static public int imageLocX;
        static public int imageLocY;
        static public Bitmap mBitmap;
        static public BitmapFactory.Options decodeResourceOptions = new BitmapFactory.Options();
    
        public GameAssets(Resources r) {
            res = r;
            buff = new byte[buffSize];
            buffInt = new int[buffIntSize];
            decodeResourceOptions.inScaled = false;
        }
        public static final void OpenAsset(int id) {
            is = res.openRawResource(id);
        }
        public static final int ReadAsset() {
            int num = 0;
            try {
                num = is.read(buff);
            } catch (Exception e) {
                ;
            }
            return num;
        }
        public static final void CloseAsset() {
            try {
                is.close();
            } catch (Exception e) {
                ;
            }
            is = null;
        }
    
        // We want all the advantages that BitmapFactory can provide -- reading
        // images of compressed image formats -- so we provide our own interface
        // for it.
        public static final void OpenImage(int id) {
            mBitmap = BitmapFactory.decodeResource(res, id, decodeResourceOptions);
            imageWidth = mBitmap.getWidth();
            imageHeight = mBitmap.getHeight();
            imageHasAlpha = mBitmap.hasAlpha() ? 1 : 0;
            imageLocX = 0;
            imageLocY = 0;
        }
        public static final int ReadImage() {
            if (imageLocY >= imageHeight) return 0;
            int numReadPixels = buffIntSize;
            if (imageLocX + buffIntSize >= imageWidth)
            {
                numReadPixels = imageWidth - imageLocX;
                mBitmap.getPixels(buffInt, 0, imageWidth, imageLocX, imageLocY, numReadPixels, 1);
                imageLocY++;
            }
            else
            {
                mBitmap.getPixels(buffInt, 0, imageWidth, imageLocX, imageLocY, numReadPixels, 1);
                imageLocX += numReadPixels;
            }
            return numReadPixels;
        }
        public static final void CloseImage() {
        }
    }
    

    请注意游戏 Assets 代码中明显缺乏线程安全性。

    让我知道更多信息是否有用。

    最佳答案

    我无法回复:(,我刚刚遇到了类似的问题,并注意到某处的 java 文档说何时有线程(如果您正在使用 OpenGL,则有线程)。您需要小心第一个参数(env)和第二个参数(作业对象)。您不能在不同的线程上共享,因为它们是特定于线程的。

    就我而言,有事件线程和渲染线程。我有一个全局 env 和 jself 变量,它们是调用 jni 时传入的 2 个参数。我更改了代码以确保只有渲染线程接触 env/jself 变量。在事件线程中,我传入原始数据,只需注意需要完成的操作,因此不需要 env/jself 变量。当然,我使用互斥锁来锁定我的事件结构。

    看起来你在这里设置 env 可能是全局的
    游戏 Assets .env = env;

    如果 gameasset 是全局的或者被不同的线程使用,那么简单地通过互斥锁/锁定共享 env 或 jobject 类变量是行不通的(它们是线程特定的)。

    TL:博士;从java调用jni方法时,我只访问env变量,渲染线程上的jobject第二个变量,没有其他地方,到目前为止已经缓解了我的问题。

    关于android - 在 JNI/OpenGL ES 加载代码期间,非常规和狡猾的 Android 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6937001/

    有关android - 在 JNI/OpenGL ES 加载代码期间,非常规和狡猾的 Android 崩溃的更多相关文章

    1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

      如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

    2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

      在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

    3. ruby - 如何在续集中重新加载表模式? - 2

      鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

    4. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

      我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

    5. ruby-on-rails - 浏览 Ruby 源代码 - 2

      我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

    6. ruby - 模块嵌套代码风格偏好 - 2

      我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

    7. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

      几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

    8. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

      我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

    9. ruby - Net::HTTP 获取源代码和状态 - 2

      我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

    10. 程序员如何提高代码能力? - 2

      前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

    随机推荐