jjzjj

android - Android的runOnUiThread是否在Adapters中调用了getView()?

coder 2023-12-27 原文

我正在使用延迟加载图像的 Gallery(一个类用于 GalleryAdapter,另一个用于延迟加载部分)。第二个类使用 runOnUiThread 使用其上下文更新第一个类,但它似乎再次调用了第一个类适配器的 getView() 方法。这意味着 getView() 会为 Gallery 中的每个图像调用两次。查看下面的代码。

奇怪的是,第二个 getView() 调用仅在 Gallery 小部件中的选定图像上调用。如果同时显示四张图片,将对这四张图片调用一次 getView(),并对所选图片调用另外三次。

知道为什么会这样吗?

这是适配器

public class ImageAdapter extends BaseAdapter {

        public HorizontalImageLoader horImageLoader;

        public ImageAdapter() {
            horImageLoader = new HorizontalImageLoader(Main.this);
        }

        public int getCount() {
            return coverFileNames.size();
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {

            ImageView imageView;

            if (convertView == null) {
                imageView = new ImageView(Main.this);
            } else {
                imageView = (ImageView) convertView;
            }

            // If I just use this, getView() is only called once per image (the correct way):
            // imageView.setImageResource(R.drawable.noposterxl);

            // If I just use this, getView() is only called once per
            // image, and additional times on the selected image (not correct):
            horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);

            return imageView;
        }

    }

这是 Horizo​​ntalImageLoader 类 ( based on this example )

public class HorizontalImageLoader {

    private Activity activity;
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());

    public HorizontalImageLoader(Activity activity) {
        this.activity = activity;
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
    }

    public void DisplayImage(String fileUrl, ImageView imageView, final int pos) {
        imageViews.put(imageView, fileUrl);
        queuePhoto(fileUrl, activity, imageView, pos);
        imageView.setImageResource(R.drawable.noposterxl);
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView, int position) {
        //This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them.
        photosQueue.Clean(imageView);
        PhotoToLoad p=new PhotoToLoad(url, imageView, position);
        synchronized(photosQueue.photosToLoad){
            photosQueue.photosToLoad.push(p);
            photosQueue.photosToLoad.notifyAll();
        }

        //start thread if it's not started yet
        if(photoLoaderThread.getState()==Thread.State.NEW)
            photoLoaderThread.start();
    }

    private Bitmap getBitmap(String fileUrl, int position) {

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPurgeable = true;

        Bitmap bm = BitmapFactory.decodeFile(fileUrl, options);

        return bm;
    }

    //Task for the queue
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public int pos;
        public PhotoToLoad(String u, ImageView i, int p){
            url=u;
            imageView=i;
            pos = p;
        }
    }

    PhotosQueue photosQueue=new PhotosQueue();

    public void stopThread()
    {
        photoLoaderThread.interrupt();
    }

    //stores list of photos to download
    class PhotosQueue
    {
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();

        //removes all instances of this ImageView
        public void Clean(ImageView image)
        {
            try {
                for(int j=0 ;j<photosToLoad.size();){
                    if(photosToLoad.get(j).imageView==image)
                        photosToLoad.remove(j);
                    else
                        ++j;
                }
            } catch (Exception e) {
                // Do nothing
            }
        }
    }

    class PhotosLoader extends Thread {
        public void run() {
            try {
                while(true)
                {
                    //thread waits until there are any images to load in the queue
                    if(photosQueue.photosToLoad.size()==0)
                        synchronized(photosQueue.photosToLoad){
                            photosQueue.photosToLoad.wait();
                        }
                    if(photosQueue.photosToLoad.size()!=0)
                    {
                        PhotoToLoad photoToLoad;
                        synchronized(photosQueue.photosToLoad){
                            photoToLoad=photosQueue.photosToLoad.pop();
                        }
                        Bitmap bmp = getBitmap(photoToLoad.url, photoToLoad.pos);
                        String tag=imageViews.get(photoToLoad.imageView);

                        if(tag!=null && tag.equals(photoToLoad.url)){
                            BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
                            Activity a=(Activity)photoToLoad.imageView.getContext();
                            a.runOnUiThread(bd); // This seems to be causing the additional calls to getView()
                        }
                    }
                    if(Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                //allow thread to exit
            }
        }
    }

    PhotosLoader photoLoaderThread=new PhotosLoader();

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageView imageView;
        public BitmapDisplayer(Bitmap b, ImageView i){
            bitmap=b;
            imageView=i;
        }
        public void run()
        {
            if(bitmap!=null)
                imageView.setImageBitmap(bitmap);
            else
                imageView.setImageResource(R.drawable.noposterxl);
        }
    }

}

再次更新

如果我在我的 getView() 方法中执行以下操作,这就是 LogCat 所说的:

代码:

Log.d("POSITION", "Current position: " + position);
horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);

日志:

09-03 13:59:11.920: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 2
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 3
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 0
09-03 13:59:12.110: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.240: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.300: DEBUG/POSITION(15278): Current position: 1

(注意底部带有“当前位置:1”的额外三个日志条目)

如果我这样做,那么 LogCat 会这样说:

代码:

Log.d("POSITION", "Current position: " + position);
imageView.setImageResource(R.drawable.noposterxl);

日志:

09-03 14:02:47.340: DEBUG/POSITION(15412): Current position: 1
09-03 14:02:47.360: DEBUG/POSITION(15412): Current position: 2
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 3
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 0

(请注意,这是返回正确的结果——每张图片只调用一次)

附言。我的 Gallery 设置为首先选择索引 1,这就是首先调用位置 1 的原因。

最佳答案

执行:

  1. public View getView(int position, View convertView, ViewGroup parent)
  2. DisplayImage(coverFileNames.get(position), imageView, position)

请记住,如果 getView 包含在 DisplayImage 的执行中,您将陷入无限循环。

既然没有无限循环,那么以下两种情况之一可以成立:<​​>

  1. DisplayImageONCE 执行something 并且这个something 正在触发 getView < strong="">一次
  2. 其他事情正在触发 getView

2 不正确,因为在 getView 中执行的唯一语句是 DisplayImage

1 为真,DisplayImage 执行一次 setImageDrawable 之一。

根据 the source code of setImageDrawable View.requestLayout()View.invalidate 被调用。

  • View.requestLayout() 将调用 ViewParent 接口(interface)的 requestLayout(),根据文档:

    Called when something has changed which has invalidated the layout of a child of this view parent. This will schedule a layout pass of the view tree.

  • View.invalidate 将调用 ViewParent 接口(interface)的 invalidateChild,这可能会导致 ViewParent 本身失效。


Therefore, it is safe to say that setting the image of an ImageView will cause an invalidation to itself. (Which is really the case and does not need to be proven anyway)

现在,由于此 ImageView 是 Adapter 接口(interface)的子级,因此最先进的方法是使用包含此接口(interface)的确切 View 的位置调用 Adapter 的 getView ImageView


最后,不用担心 因为 getView 在这种情况下等同于 invalidate() 并且您肯定希望调用 invalidate() 以便显示您的图像

这是正常的

1 更多内容

您的应用程序性能不佳并不是因为调用了 getView!

您应该正确地实现您的 DisplayImage。您的类(class)没有考虑任何优化。这不是 BaseAdapter 的责任。

建议:

  1. 我认为一个很大的瓶颈是您的 queuePhoto 函数,它总是调用 Clean 函数!你一直在打扫卫生!
  2. 您的 key 是一个 ImageView!并且您一直在循​​环比较 ImageView

只需尝试增强您的代码并对其进行优化。您应该考虑多次调用 getView。

关于android - Android的runOnUiThread是否在Adapters中调用了getView()?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7292293/

有关android - Android的runOnUiThread是否在Adapters中调用了getView()?的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  3. ruby - 检查字符串是否包含散列中的任何键并返回它包含的键的值 - 2

    我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案

  4. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  5. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  6. ruby - 检查日期是否在过去 7 天内 - 2

    我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/

  7. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  8. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  9. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  10. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

随机推荐