2017-08-24 14:30:14 omnispace 阅读数 308
  • 快速入门Android开发 视频 教程 android studio

    这是一门快速入门Android开发课程,顾名思义是让大家能快速入门Android开发。 学完能让你学会如下知识点: Android的发展历程 搭建Java开发环境 搭建Android开发环境 Android Studio基础使用方法 Android Studio创建项目 项目运行到模拟器 项目运行到真实手机 Android中常用控件 排查开发中的错误 Android中请求网络 常用Android开发命令 快速入门Gradle构建系统 项目实战:看美图 常用Android Studio使用技巧 项目签名打包 如何上架市场

    21670 人正在学习 去看看 任苹蜻

JNI实现源码分析【二 数据结构】的参数传递一节中,我们提到,JNI为了安全性的考虑使用了形如jobject的结构来传递参数。而jobject被表述为指针,但又不是直接指向Object的指针那么jobject是如何和真正的Object建立联系呢?
在JNI的API中,有一组API Global and Local References,这里的References又是什么?为啥会有这一组API?
答案都和间接引用表(IndirectRefTable)有关

0x01: IndirectRefTable

源码见IndirectRefTable.h
代码很复杂,等效理解就可以了,其作用就是一张保存了间接引用的表。让jobject和Object建立起联系。

0x02: 作用域

在JNI中,有两个不同的作用域:全局作用域(进程级别)和线程作用域(线程级别)。这两个作用域分别有自己的间接引用表。
全局作用域的间接引用表保存在gDvm.jniGlobalRefTable中。gDvm是一个全局变量,在虚拟机启动的时候就创建。
线程作用域的间接引用表保存在thread.jniLocalRefTable中。和线程绑定,线程创建时创建,线程销毁时销毁。

JNI API中的全局引用和局部引用,指的就是全局作用域的间接引用表和线程作用域的间接引用表。

于是:

jobject NewGlobalRef(JNIEnv *env, jobject obj);
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);

我们就是操作了全局引用表

而:

jobject NewLocalRef(JNIEnv *env, jobject ref);
void DeleteLocalRef(JNIEnv *env, jobject localRef);

我们操作了线程引用表

让我们再来看看两个表的大小,在创建的时候,就指定了其大小:

#define kGlobalRefsTableInitialSize 512
#define kGlobalRefsTableMaxSize     51200  
if (!gDvm.jniGlobalRefTable.init(kGlobalRefsTableInitialSize,
                                 kGlobalRefsTableMaxSize,
                                 kIndirectKindGlobal)) {
        return false;
    }

可以看到,全局引用表的初始大小为512,最大为51200。

#define kJniLocalRefMin         64
#define kJniLocalRefMax         512 
if (!thread->jniLocalRefTable.init(kJniLocalRefMin,
            kJniLocalRefMax, kIndirectKindLocal)) {
        return false;
    }

而局部引用表的初始大小为64,最大为512。 这里顺便提一下,当超过这个最大时,就会报local reference table overflow (max=512)的错误。

0x03: jobject到Object的映射

到现在,我们应该可以顺理成章的理解到,jobject到Object的映射借用了间接引用表,没错!
我们来分析局部引用,全局引用是类似的。

static inline jobject addLocalReference(Thread* self, Object* obj) {
    if (obj == NULL) {
        return NULL;
    }

    IndirectRefTable* pRefTable = &self->jniLocalRefTable;
    void* curFrame = self->interpSave.curFrame;
    u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
    jobject jobj = (jobject) pRefTable->add(cookie, obj);
    if (UNLIKELY(jobj == NULL)) {
        AddLocalReferenceFailure(pRefTable);
    }

    if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
        // Hand out direct pointers to support broken old apps.
        return reinterpret_cast<jobject>(obj);
    }
    return jobj;
}

非常明了的代码,我们使用了pRefTable->add将实际对象添加到了间接引用表,从而获取了jobject的间接引用。

我们看一下add做了啥:

IndirectRef IndirectRefTable::add(u4 cookie, Object* obj)
{
    IRTSegmentState prevState;
    prevState.all = cookie;
    size_t topIndex = segmentState.parts.topIndex;

    assert(obj != NULL);
    assert(dvmIsHeapAddress(obj));
    assert(table_ != NULL);
    assert(alloc_entries_ <= max_entries_);
    assert(segmentState.parts.numHoles >= prevState.parts.numHoles);

    /*
     * We know there's enough room in the table.  Now we just need to find
     * the right spot.  If there's a hole, find it and fill it; otherwise,
     * add to the end of the list.
     */
    IndirectRef result;
    IndirectRefSlot* slot;
    int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles;
    if (numHoles > 0) {
        assert(topIndex > 1);
        /* find the first hole; likely to be near the end of the list,
         * we know the item at the topIndex is not a hole */
        slot = &table_[topIndex - 1];
        assert(slot->obj != NULL);
        while ((--slot)->obj != NULL) {
            assert(slot >= table_ + prevState.parts.topIndex);
        }
        segmentState.parts.numHoles--;
    } else {
        /* add to the end, grow if needed */
        if (topIndex == alloc_entries_) {
            /* reached end of allocated space; did we hit buffer max? */
            if (topIndex == max_entries_) {
                ALOGE("JNI ERROR (app bug): %s reference table overflow (max=%d)",
                        indirectRefKindToString(kind_), max_entries_);
                return NULL;
            }

            size_t newSize = alloc_entries_ * 2;
            if (newSize > max_entries_) {
                newSize = max_entries_;
            }
            assert(newSize > alloc_entries_);

            IndirectRefSlot* newTable =
                    (IndirectRefSlot*) realloc(table_, newSize * sizeof(IndirectRefSlot));
            if (table_ == NULL) {
                ALOGE("JNI ERROR (app bug): unable to expand %s reference table "
                        "(from %d to %d, max=%d)",
                        indirectRefKindToString(kind_),
                        alloc_entries_, newSize, max_entries_);
                return NULL;
            }

            memset(newTable + alloc_entries_, 0xd1,
                   (newSize - alloc_entries_) * sizeof(IndirectRefSlot));

            alloc_entries_ = newSize;
            table_ = newTable;
        }
        slot = &table_[topIndex++];
        segmentState.parts.topIndex = topIndex;
    }

    slot->obj = obj;
    slot->serial = nextSerial(slot->serial);
    result = toIndirectRef(slot - table_, slot->serial, kind_);

    assert(result != NULL);
    return result;
}

我擦,真的是太复杂了,里面肯定包含了某个算法,反正就是通过参数cookie,通过slot等,在表的合适位置引用了真正的Object,然后返回了一个值(间接引用),后续通过这个值,能够去表里面的这个位置找到Object。
所以之前说过,jobject并不是直接指向Object的指针。甚至它并不是真正的地址,它仅仅是表的间接引用。

让我们继续看看,如何通过这个间接引用找到真实的Object吧:

Object* dvmDecodeIndirectRef(Thread* self, jobject jobj) {
    if (jobj == NULL) {
        return NULL;
    }

    switch (indirectRefKind(jobj)) {
    case kIndirectKindLocal:
        {
            Object* result = self->jniLocalRefTable.get(jobj);
            if (UNLIKELY(result == NULL)) {
                ALOGE("JNI ERROR (app bug): use of deleted local reference (%p)", jobj);
                ReportJniError();
            }
            return result;
        }
    case kIndirectKindGlobal:
        {
            // TODO: find a way to avoid the mutex activity here
            IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
            ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock);
            Object* result = pRefTable->get(jobj);
            if (UNLIKELY(result == NULL)) {
                ALOGE("JNI ERROR (app bug): use of deleted global reference (%p)", jobj);
                ReportJniError();
            }
            return result;
        }
    case kIndirectKindWeakGlobal:
        {
            // TODO: find a way to avoid the mutex activity here
            IndirectRefTable* pRefTable = &gDvm.jniWeakGlobalRefTable;
            ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock);
            Object* result = pRefTable->get(jobj);
            if (result == kClearedJniWeakGlobal) {
                result = NULL;
            } else if (UNLIKELY(result == NULL)) {
                ALOGE("JNI ERROR (app bug): use of deleted weak global reference (%p)", jobj);
                ReportJniError();
            }
            return result;
        }
    case kIndirectKindInvalid:
    default:
        if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) {
            // Assume an invalid local reference is actually a direct pointer.
            return reinterpret_cast<Object*>(jobj);
        }
        ALOGW("Invalid indirect reference %p in decodeIndirectRef", jobj);
        ReportJniError();
        return kInvalidIndirectRefObject;
    }
}

可以看到,通过jobject可以计算出RefKind,即jobject还包含了类型信息。
真正找Object是在pRefTable->get:

Object* IndirectRefTable::get(IndirectRef iref) const {
    IndirectRefKind kind = indirectRefKind(iref);
    if (kind != kind_) {
        if (iref == NULL) {
            ALOGW("Attempt to look up NULL %s reference", indirectRefKindToString(kind_));
            return kInvalidIndirectRefObject;
        }
        if (kind == kIndirectKindInvalid) {
            ALOGE("JNI ERROR (app bug): invalid %s reference %p",
                    indirectRefKindToString(kind_), iref);
            abortMaybe();
            return kInvalidIndirectRefObject;
        }
        // References of the requested kind cannot appear within this table.
        return kInvalidIndirectRefObject;
    }

    u4 topIndex = segmentState.parts.topIndex;
    u4 index = extractIndex(iref);
    if (index >= topIndex) {
        /* bad -- stale reference? */
        ALOGE("JNI ERROR (app bug): accessed stale %s reference %p (index %d in a table of size %d)",
                indirectRefKindToString(kind_), iref, index, topIndex);
        abortMaybe();
        return kInvalidIndirectRefObject;
    }

    Object* obj = table_[index].obj;
    if (obj == NULL) {
        ALOGI("JNI ERROR (app bug): accessed deleted %s reference %p",
                indirectRefKindToString(kind_), iref);
        abortMaybe();
        return kInvalidIndirectRefObject;
    }

    u4 serial = extractSerial(iref);
    if (serial != table_[index].serial) {
        ALOGE("JNI ERROR (app bug): attempt to use stale %s reference %p",
                indirectRefKindToString(kind_), iref);
        abortMaybe();
        return kInvalidIndirectRefObject;
    }

    return obj;
}

和我们前面想的一样,这里通过一堆的计算后,在Object* obj = table_[index].obj;处找到了真实的Object。

0x04: JNI在背后默默做的事

在JNI环境中,我们永远接触不了真实的Object对象,上面映射方法是虚拟机内部的,我们在JNI环境也是没法调用的。所以,我们在JNI环境中,使用的都是间接引用,比如jobject,jmethodID等。确实,JNI的所有API都在使用这些间接引用。
那么,这里就有一个问题了,既然间接引用和间接引用表有关,那在使用JNI的API时,获取到这些间接引用时,JNI将真实的对象保存在哪个表里面?
答案是线程引用表,几乎每一个API都有JNIEnv,JNIEnv和线程绑定,可以很容易定位到线程引用表。放到线程应用表,随着线程的销毁,引用表也不会被销毁,不会一直占用空间。

我当初在JNI中想要获取Throwable.printStackTrace时,就因为调用相关的API,然后产生了很多的间接引用,将间接引用表撑爆,报了:local reference table overflow (max=512)

除了JNI的默认行为,假如我们想要自己控制引用的生命周期,比如提前删除,将引用放置到全局引用表等,我们可以使用Ref相关的API即可,记住,不用了一定要删除,不要存在引用泄漏。



作者:difcareer
链接:http://www.jianshu.com/p/127adc130508
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2018-04-19 11:37:57 crrose 阅读数 2325
  • 快速入门Android开发 视频 教程 android studio

    这是一门快速入门Android开发课程,顾名思义是让大家能快速入门Android开发。 学完能让你学会如下知识点: Android的发展历程 搭建Java开发环境 搭建Android开发环境 Android Studio基础使用方法 Android Studio创建项目 项目运行到模拟器 项目运行到真实手机 Android中常用控件 排查开发中的错误 Android中请求网络 常用Android开发命令 快速入门Gradle构建系统 项目实战:看美图 常用Android Studio使用技巧 项目签名打包 如何上架市场

    21670 人正在学习 去看看 任苹蜻
  • 在项目引用过多的library时我们可以将library打包成aar包来引用。通常的我们会把一些公共jar包只配置到base包下,其他library只要引用base就能引用到该jar包。在配置aar包时,我们也会想只配置aar在base,这时引用base的其他module都会报错不能解析此aar,有这个问题的可以跳到末尾。

 

  • 首先是生成aar包与引用aar包

 

  • library只要编译过了在build/outputs目录下就会有对用的包,这里我们使用release的包
  • 将library放在对应module的libs包下,名字可以修改
  • build.gradle文件添加repositories
  •  buildTypes {
            debug {
                minifyEnabled false
                debuggable true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        repositories {
            flatDir {
                dirs 'libs'
            }
        }
  • 添加dependencies
  • dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
    
        compile(name: 'image-selector-1.0', ext: 'aar')//aar引用
    
        compile project(':vlayout')
        compile 'com.crittercism.dexmaker:dexmaker:1.4'
        compile 'com.squareup.picasso:picasso:2.5.2'
        compile 'com.android.support:appcompat-v7:21.0.0'
        compile 'com.android.support:support-annotations:21.0.0'
        compile 'com.android.support:cardview-v7:23.1.1'
    }
    
  • 这样就可以在该module中引用aar包了
  • 这种配置方式会存在一个小问题,当把这个aar像jar包一样只配置在base包下,供多个模块使用时,其他library会报错不能解析image-selector-1.0.aar
  • 解决方法有三种
  1. 第一种就是使用as来导入aar,当成一个library来引用。方式为new module

    然后选择自己的arr包,就能生成一个类似的library。在base里面应用就行了。问题是解决了,但是还是有一个目录在哪,总感觉不自在。不行还得想办法
  2. 第二种方式就简单了,arr还是在放在base包下的libs中,dependencies的写法也是一样只需要把repositories的代码修改一下移到真个project的build.gradle中.
  3. 第三种方式在比较高的gradle plugin版本3.0.0下可以直接使用fileTree来依赖aar
implementation fileTree(include: ['*.jar','*.aar'], dir: 'libs')

 

dirs'../你放aar包的library名称/libs' 就是路径名,build 的脚本使用 groovy 语言编写“..”代表回退,因为所有的library在同一级目录。gradle编译每个library时读到 '../你放aar包的library名称/libs' 回回退上一级目录在进入放入aar包的目录/libs中找到aar包。把这个repositories的路径配置到整个项目的build.gradle中就不需要每个library中都去配置了。这样就可以偷懒了 嘻嘻~

 

 

 

 

 

2016-12-12 10:05:10 luweicheng24 阅读数 325
  • 快速入门Android开发 视频 教程 android studio

    这是一门快速入门Android开发课程,顾名思义是让大家能快速入门Android开发。 学完能让你学会如下知识点: Android的发展历程 搭建Java开发环境 搭建Android开发环境 Android Studio基础使用方法 Android Studio创建项目 项目运行到模拟器 项目运行到真实手机 Android中常用控件 排查开发中的错误 Android中请求网络 常用Android开发命令 快速入门Gradle构建系统 项目实战:看美图 常用Android Studio使用技巧 项目签名打包 如何上架市场

    21670 人正在学习 去看看 任苹蜻

这里写图片描述

我们直接开门见山,Android中对象存在着4中引用的类型,因为android开发中绝大部分使用面向对象的java编写的,对于java而言世界万物皆是对象,而引用就好比是对象的代表,可以利用引用做对象包含的所有事情,而在android中Garbage Collection(简称GC)也就是垃圾回收器,是对象的克星,一个对象可以包含多个直接引用和间接引用,GC就像一名私人侦探一样随时在内存中查看对象的引用,一旦发现该对象不存在引用了,马上将该对象回收,释放占用的内存。说了这么多到底什么是引用啊,for example:

/**
 *创建一个Couputer对象,执行运算的行为
 *
 */
Computer mComputer = new Computer();
mComputer.computer();

在这里new Computer()即创建了一个对象并将这个对象存储在mComputer这个引用中,在任何地方只要拿到这个引用就能随时操作Computer,执行计算的功能,虽然引用可以随时随地去操作对象,但是如果创建没有指向任何对象的引用,for example:

/**
 *创建一个Computer的引用,执行computer方法
 */
Computer mComputer;
mComputer.computer();

这样的引用一旦执行,就会出现司空见惯的空指针异常(NullPositionException)。

内存泄漏

谈到内存泄漏,大家肯定不会陌生,一个好的app内存必定得到了良好的管理,但是如何很好的管理内存呢?那么我们先看一下Android中是如何对内存进行回收的,在Android中GC采用了标注和清理(Mark and
sweep)回收算法:从” GC Roots“集合开始,将内存中存活的对象整个遍历一遍,凡是能够被GC Roots直接或者间接引用到的对象保存起来,而剩下的孤魂野鬼的对象便被当成垃圾回收掉了。下面我画几个图展示一下GC的回收过程:
这里写图片描述

这里写图片描述

这里写图片描述

上面三张图描述GC遍历内存中对象的过程。
其中的每个圆形节点代表一个对象(内存的资源),箭头表示引用的路径(可达路径),黄色圆形表示遍历后GC Roots与该对象存在可以到达的路径,深蓝色代表无法到达的路径,意味着该对象不存在引用即将被回收,释放占用的资源。因为android是基于Linux内核的,每个应用程序都是一个单独的Linux进程,也对应着一个单独的Dalvik虚拟机的实例,而每个Dalvik虚拟机的大小是固定的(比如:16M,32M,64等),这就意味着我们使用的内存是有限的,所以需要我们在开发中注意内存的使用,GC就能帮我们回收没有引用的对象释放内存,所以在开发中应该随时注意对对象的引用,不要到处new对象,否则,将会内存泄漏。说了这么多其实内存泄漏最直白的解释就是本该死的不死,还占用着位置

文章开始我就说了Android存在着4种引用类型,之所以要将引用类型分为4种,那是因为咱们的GC回收引用的时候对这4种引用疼爱不一,根据不同的类型执行不同的回收策略,下面我们扯一扯这四种引用类型,了解一下她们和GC之间的爱恨情仇:
Strong peference(强引用)
顾名思义,她和GC的关系肯定是真爱。其实我们平时创建的引用都是强引用,for example:

Person person = new Person();

这里的person就是对象Person的强引用,当一个对象含有强引用的时候,GC是绝对不会回收和销毁该对象的,对象的强引用可以在程序中随意传递,因为她有GC的保护不会回收,很多情况下,会有多个强引用指向一个对象。强引用的存在增加了对象的寿命,比如对象A中包含了对象B的强引用,那么对象B的存活期就不是它自己能够决定,除非当A把它对B 的引用显示的置为null,要不就必须等到A被回收之后才能回收B。下面我们举个例子:


public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    private ImageView img;
    private static String url = "http://pic3.zhongsou.com/image/38063b6d7defc892894.jpg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image);
        img = (ImageView) findViewById(R.id.image);
        new MyAsyncTask().execute(url);
    }

    @TargetApi(Build.VERSION_CODES.CUPCAKE)
    private class MyAsyncTask extends AsyncTask<String, Void, Bitmap> {

        @Override
        protected Bitmap doInBackground(String... strings) {
            //执行耗时操作(图片下载)
            String url = strings[0];
            HttpURLConnection connection;
            Bitmap bmp = null;
            try {
                connection = (HttpURLConnection) new URL(url).openConnection();
                InputStream is = connection.getInputStream();
                bmp = BitmapFactory.decodeStream(is);

            } catch (IOException e) {
                e.printStackTrace();
            }
            return bmp;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            //更新UI
            img.setImageBitmap(bitmap);
        }
    }


}

我创建了一个AsyncTask执行下载图片的任务下载完成后更新UI,在java中,非静态内部类在整个生命周期中持有对其外部类的强引用因此即使当该Activity已经destroy后仍然不会让GC回收内存,因为AsyncTask没有结束,所以必须等到AsyncTask耗时操作完成后回收,只有AsyncTask被GC回收后,MainActivity才能被回收,如果MainActivity中持有Bitmap等比较大的对象,反复进出这个界面可能就会出现OOM而Crash了。

  • Weak peference(弱引用)
    弱引用通过类WeakPeference来表示,弱引用并不能阻止
    GC回收该对象,如果使用一个强引用的话,只要该引用存在,那么被引用的对象将一直存在不能被垃圾回收器回收,在垃圾回收器运行的时候,如果该对象的所用引用都是弱引用则垃圾回收器便会马上回收该对象,下面我们来把上面的例子稍微调整一下来避免内存泄漏:
public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";
    private static ImageView img;
    //private Button but_finish;
    private static String url = "http://pic3.zhongsou.com/image/38063b6d7defc892894.jpg";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image);
        img = (ImageView) findViewById(R.id.image);
        but_finish = (Button) findViewById(R.id.but_finish);
     /*   but_finish.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

            }
        });*/
        new MyAsyncTask(this).execute(url);
    }

    @TargetApi(Build.VERSION_CODES.CUPCAKE)
    private static class MyAsyncTask extends AsyncTask<String, Void, Bitmap>
    {
        private WeakReference<MainActivity> mainActivity;//创建一个泛型为MianActivityd的弱引用
        public MyAsyncTask(MainActivity mainActivity){//创建该弱引用对象
            this.mainActivity = new WeakReference<MainActivity>(mainActivity);
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            //执行耗时操作(图片下载)
            String url = strings[0];
            HttpURLConnection connection;
            Bitmap bmp = null;
            try {
                connection = (HttpURLConnection) new URL(url).openConnection();
                InputStream is = connection.getInputStream();
                bmp = BitmapFactory.decodeStream(is);

            } catch (IOException e) {
                e.printStackTrace();
            }
            return bmp;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            if(mainActivity.get()!=null) {//判断该对象是否已经被GC回收
                Logger.d("MainActivity没有被回收");
                img.setImageBitmap(bitmap);
                return;
            }
            Logger.d("MainActivity被回收");

        }
    }
}

对比以上两段代码,主要是我们把MySyncTask变成了静态内部类,并且把其对外部类的强引用转换成个弱引用,

private WeakPeference<MainActivity> mainActivity;
this.mainActivity = new WeakPeference<MainActivty>(mainActivity);

修改之后当MainActivity destroy时,由于MyAsyncTask是通过弱引用的方式持有对外部类MainActivity的引用,所以并不会阻止MainActivity被垃圾回收器回收,这样就不会产生内存泄漏了。

  • Soft Reference(软引用)
    软引用利用类SoftReference来表示,它是比弱引用稍强的引用类型,对于很多人来说,软引用和弱引用很容易混淆,其实对于软引用来说,被它修饰的对象生命周期在内存满足的时候和强引用一样不会去回收任何对象,但是当内存紧张时,GC首先将回收所用被软引用修饰的对象,此时的地位和弱引用相同,那么既然软引用只有在内存不足的的时候才会销毁回收,那用它做缓存肯定不错吧,因为只有当内存不足时才会回收,但是事实上,用作软应用作为缓存存在很多不足,比如有些Bitmap的对象引用不应该全部回收如果回收就得重新从SD卡或者网络加载,因此在实际开发中不会使用软引用来进行数据缓存,目前基本如果使用缓存都是用LruCache进行数据缓存,LruCache中LRU是Least Recently Used的缩写,即最近最少使用,它的内部维护了一个固定大小的内存,当内存不足时时候,会根据策略把最近最少使用的数据从内存中移除,让出内存给出最新的数据,下面是我一个简单的LruCache的使用:
/**
 * Created by luweicheng on 2016/12/12.
 */

public class CacheUtils {
    private static LruCache<String, Bitmap> mCache;
    private static CacheUtils mCacheUtils;

    int maxMemory = (int) (Runtime.getRuntime().maxMemory() * (1 / 8));//设置内存大小

    private CacheUtils() {

    }

    /**
     * 获取实例
     * @return
     */
    public static CacheUtils getInstance() {
        if (mCacheUtils == null) {

            synchronized (MyApplication.class) {
                mCacheUtils = new CacheUtils();

            }
            return mCacheUtils;
        } else {
            return mCacheUtils;
        }

    }


    /**
     * 加入数据到缓存
     */
    private void addCaheData(String url, Bitmap bitmap) {
        if (mCache.get(url) == null) {//判断是否已经存在于缓存
            return;
        }
        mCache.put(url, bitmap);

    }

    /**
     * 从缓存中取数据
     */
    private Bitmap getCacheData(String url) {
        if (mCache.get(url) != null) {
            return mCache.get(url);
        }
        return null;
    }

    /**
     * 移除数据从缓存
     */
    private void clearCacheData(String url) {
        if (mCache.get(url) != null) {
            mCache.remove(url);
        }
    }

}
  • PhantomRerence(虚引用)
    一个只被虚引用持有的对象可能会在任何时候被GC回收,虚引用对对象的的生命周期完全没有影响,也无法通过虚引用用来获取对象实例,仅仅实在对象回收时获取一个通知(通过判断该对象是否加入到RefreenceQuenue来判断是否被回收)其实虚引用基本没什么用处,主要是用来跟踪对象被垃圾回收的活动,比如一个Bitmap的对象的生命周期,通过虚引用跟踪该Bitmap是否回收,如果已经回收将重新加载,如果未被回收就可以不用重新加载,直接使用该引用。
    好了,以上就是我对引用的一点理解,晚安 : EveryBody
2012-08-15 22:36:38 huazai963184709 阅读数 8068
  • 快速入门Android开发 视频 教程 android studio

    这是一门快速入门Android开发课程,顾名思义是让大家能快速入门Android开发。 学完能让你学会如下知识点: Android的发展历程 搭建Java开发环境 搭建Android开发环境 Android Studio基础使用方法 Android Studio创建项目 项目运行到模拟器 项目运行到真实手机 Android中常用控件 排查开发中的错误 Android中请求网络 常用Android开发命令 快速入门Gradle构建系统 项目实战:看美图 常用Android Studio使用技巧 项目签名打包 如何上架市场

    21670 人正在学习 去看看 任苹蜻

今天导入别人的一个Android程序,程序是正确的,而且我的环境没错,为什么会报这个错呢?

试了好久,终于解决; 选择项目---->右键属性---->Android 这时你发现 Project Build Target 所有的模拟器都没选中,选择一个(我选2.1,这的根据程序选,有的模拟器不一定能用),应用-->确定 。把项目刷新一下,得以解决。

     不过前提一定是1:程序是正确的。 2:环境没错(安卓环境和java环境我都遇到过纠结的问题,前面文章有具体操作)

 

2018-12-20 15:44:27 helloFeiGe 阅读数 148
  • 快速入门Android开发 视频 教程 android studio

    这是一门快速入门Android开发课程,顾名思义是让大家能快速入门Android开发。 学完能让你学会如下知识点: Android的发展历程 搭建Java开发环境 搭建Android开发环境 Android Studio基础使用方法 Android Studio创建项目 项目运行到模拟器 项目运行到真实手机 Android中常用控件 排查开发中的错误 Android中请求网络 常用Android开发命令 快速入门Gradle构建系统 项目实战:看美图 常用Android Studio使用技巧 项目签名打包 如何上架市场

    21670 人正在学习 去看看 任苹蜻

问题描述

在搭建组件化结构项目的时候, 有主Moudle, 功能Module(库), 核心Moudle(库), 它们的依赖关系是前一个依赖后面一个Moudle,为了简化Build.gradle的依赖,我将appcompat包,constraint包等都放入了核心Moudle,但是在运行的时候,主Moudle提示无法找到AppCompatActivity,

解决方法

确定核心库已经添加依赖
在这里插入图片描述但是并没有起作用,思考应该是implementation的问题
之前在使用compile的时候给出过以下提示
在这里插入图片描述查寻资料后,知道implementation和api 的区别:

  1. gradle3.4后 , implementation 和api是complie的替代品;
  2. implementation声明的依赖包只限于模块内部使用,不允许其他模块使用。
  3. 其他模块依赖于此模块,此模块使用api声明的依赖包可以被其他模块使用.
    从而我将核心依赖库的implementation该为api, 以及功能Moudle中对核心Moudle的依赖也改为api,至此解决问题

Android内存泄漏研究

阅读数 1060

Android 多语言

阅读数 1256

没有更多推荐了,返回首页