简体   繁体   English

是否可以在封闭范围内使用非最终形式,如可运行(no。)

[英]Is it possible to use a non-final in an enclosing scope like a runnable (no.)

The plan: 计划:

I created a little PreferenceActivity (don't hate me, I'm supporting API 10 and up) and need to display the current usage of local storage data by my app. 我创建了一个PreferenceActivity (不要恨我,我支持API 10及更高版本),并且需要显示我的应用程序对本地存储数据的当前使用情况。 I did this using a specialized class (a pretty big one, as of the moment) that handles all file operations (it's called FileOperations.java for a reason). 我使用处理所有文件操作的专用类( FileOperations.java ,这是一个很大的类)来完成此操作(由于某种原因,它称为FileOperations.java )。 Inside this class file there is a method getSize(File file) {...} which does just that. 在此类文件中,有一个方法getSize(File file) {...}就是这样做的。 It gets the size of a file (or folder) with this little piece of code: 它使用以下这段代码来获取文件(或文件夹)的大小:

public long getSize(File file) {
    long size = 0;

    if(file.isDirectory()) {
        for(File child : file.listFiles()) {
            size += getSize(child);
        }
    }
    size = file.length();

    return size;
}

The general idea was to use this in a background Thread so it doesn't clog the UI even the slightest bit. 通常的想法是在后台Thread使用它,以至于即使丝毫也不会阻塞UI。 (I am really annoyed by lagging apps and suffer from them daily) (我对应用程序的滞后感到非常恼火,每天都遭受它们的困扰)

The problem: 问题:

This works just fine. 这样很好。 However, as soon as I purge the folder the app stores it's data in using this beauty: 但是,一旦我清除了文件夹,应用程序便会使用此功能来存储其数据:

private void purgeLocalStorage() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            Log.i("ActivityPrefsLocalStorage.purgeLocalStorage.Thread.Runnable.run", "Started to run");
            final String         directory = context.getResources().getString(R.string.app_name);
            final String         usedData  = context.getResources().getString(R.string.ActivityPrefsLocalStorage_usedData);
            final File           file      = new File(Environment.getExternalStorageDirectory()+"/"+directory);
            final FileOperations FO        = new FileOperations(context);
            Log.i("ActivityPrefsLocalStorage.purgeLocalStorage.Thread.Runnable.run", "deleting folder: "+file);
            if(FO.delete(file)) {
                Log.i("ActivityPrefsLocalStorage.purgeLocalStorage.Thread.Runnable.run", file+" deleted");
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, R.string.ActivityPrefsLocalStorage_deleteSucces, Toast.LENGTH_SHORT).show();
                        setTotalLocalDataTexts(usedData+" "+context.getResources().getString(R.string.pref_totalData_default), "");
                        getUsedStorage();
                    }
                });
            } else {
                Log.e("ActivityPrefsLocalStorage.purgeLocalStorage.Thread.Runnable.run", "could not delete "+file);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(context, R.string.ActivityPrefsLocalStorage_deleteError, Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }
    }).start();
}

Things hit the fan... 事情风靡一时...

See, the problem is that my method for reading the size of the folder does not want to function properly when called by the previous method. 看到的问题是,我的读取文件夹大小的方法在被前一个方法调用时不希望其正常运行。

Here's a snippet: 这是一个片段:

private void getUsedStorage() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Log.i("ActivityPrefsLocalStorage.getUsedStorage.Thread.Runnable.run", "Started to run");
            final String         directory = context.getResources().getString(R.string.app_name);
            final String         usedData  = context.getResources().getString(R.string.ActivityPrefsLocalStorage_usedData);
            final File           file      = new File(Environment.getExternalStorageDirectory()+"/"+directory);
            final FileOperations FO        = new FileOperations(context);
            final DataUsage      DU        = new DataUsage(context);
            Log.i("ActivityPrefsLocalStorage.getUsedStorage.Thread.Runnable.run", "Checking filesize of folder: "+file);
                  long           fileSize  = FO.getSize(file);
                  String         usedUnits = DU.getUnit(fileSize, true, false, false);
                  String         usedBytes = DU.getUnit(fileSize, true, true, true);
            Log.i("ActivityPrefsLocalStorage.getUsedStorage.Thread.Runnable.run", "filesize of "+file+": "+usedUnits);
            setTotalLocalDataTexts(usedData+" "+usedUnits, usedBytes);
        }
    }).start();
}

However, a quick and easy workaround would be to place it on the UI thread like so: 但是,一种快速简便的解决方法是将其放置在UI线程上,如下所示:

...blabla code you already read above.
                  long           fileSize  = FO.getSize(file);
                  String         usedUnits = DU.getUnit(fileSize, true, false, false);
                  String         usedBytes = DU.getUnit(fileSize, true, true, true);
            Log.i("ActivityPrefsLocalStorage.getUsedStorage.Thread.Runnable.run", "filesize of "+file+": "+usedUnits);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    setTotalLocalDataTexts(usedData+" "+usedUnits, usedBytes);
                }
            });
        }
    }).start();
}

And that's where it starts getting interesting. 这就是开始变得有趣的地方。 I cannot use non-finals inside the new Runnable() , and I cannot make them final since I want the value to update and not remain stuck at eg. 我不能在new Runnable()使用非final类型, 也不能使其成为final类型,因为我想更新值并且不要停留在例如。 32MiB (while it has just been purged). 32MiB(虽然它刚刚被清除)。

Possible fixes: 可能的修复:

I should man up and just use a final. 我应该动手,只用决赛。 The user will understand they need to refresh the page manually. 用户将了解他们需要手动刷新页面。 (oh no...) (不好了...)

Hire... erm. 雇用...嗯。 Extend an AsyncTask<Void, Void, Void> to do the work. 扩展AsyncTask<Void, Void, Void>来完成工作。


My ideal fix: 我的理想解决方案:

Someone giving me an awesome snippet of code for free that does all the magic. 有人免费给我提供了很棒的代码片段,它可以发挥所有魔力。 no, seriously though, I would really appreciate anything apart from my list of possible fixes. 不,虽然说真的,除了可能的修复程序之外,我真的很感激。 There has to be some way to pass the new Runnable() a variable without creating classes and implementing the entire universe? 必须有某种方法可以将new Runnable()传递给变量,而无需创建类和实现整个Universe? Or is that what I am trying to achieve really a new thing? 还是我要实现的目标真的是新事物?

TL;DR: TL; DR:
Things go wrong as soon as I call getUsedStorage() from within a new Runnable() . 当我new Runnable()调用getUsedStorage() ,事情就出错了。 This function is also a background task inside a Runnable, but updates the UI using a private void function that sets it. 此函数也是Runnable内部的后台任务,但是使用设置它的私有void函数更新UI。 It only passes variables to this function. 它仅将变量传递给此函数。 and then things fly off the handle(r). 然后东西从手柄上飞走。

Edit: grammar. 编辑: 语法。
Edit2: Also a pretty interesting thing to note here, I used something similar in another PreferenceActivity, and that one works. Edit2: 在这里还要注意的一件非常有趣的事情,我在另一个PreferenceActivity中使用了类似的东西,并且这个作品有用。 (but that one does not update at the press of a button that calls another private something functionName() {new Thread(new Runnable() {public void run() {...});} ) (但是按下一个调用另一个private something functionName() {new Thread(new Runnable() {public void run() {...});}的按钮并不会更新private something functionName() {new Thread(new Runnable() {public void run() {...});}

There are a couple of ways to use non-finals inside of a Runnable or other enclosed classes. 有几种方法可以在Runnable或其他封闭的类中使用非final。

The first is to change your variables to be members of an enclosing class. 首先是将变量更改为封闭类的成员。 This will allow you to use the variables inside the Runnable. 这将允许您使用Runnable内部的变量。 An example follows: 下面是一个示例:

public class Foo {
    private long time;

    public void run() {
        time = System.currentTimeMillis();

        new Thread(new Runnable() {
            @Override
            public void run() {
                time += 1;
                System.out.println("Our current time is: " + time);
            }
        });
    }
}

The other option, and it is quite hacky, is to use a final array with a length of 1. An example of that follows: 另一个选择是很棘手的,它使用长度为1的最终数组。下面是一个示例:

public class Foo {
    public void run() {
        final long[] time = { System.currentTimeMillis() };

        new Thread(new Runnable() {
            @Override
            public void run() {
                time[0] += 1;
                System.out.println("Our current time is: " + time[0]);
            }
        });
    }
}

As gonemad16 on reddit.com/r/androiddev pointed out, my issue had nothing to do with final vs non final. 正如reddit.com/r/androiddev上的godmad16所指出的那样,我的问题与最终版和非最终版无关。 That was not the reason I was getting an old value. 那不是我获得旧价值的原因。 All my variables are given a value, sent to setTotalLocalDataTexts and then go out of scope... nothing is updating their values.. so there is no harm in them being final and no benefit to them being non final... 我所有的变量都有一个值,发送到setTotalLocalDataTexts,然后超出范围...什么都没有更新它们的值..因此,将它们定为无害,将其定为非定益也无济于事...

It was an an issue in getSize() 这是getSize()中的一个问题

I thought I had a correct loop there using if file.isDirectory() {...}. 我以为我在使用if file.isDirectory(){...}时有正确的循环。 It created a directory tree and executed itself using the children it has found. 它创建了一个目录树,并使用找到的子目录执行自己。 When all items have been scanned the value returns to the function calling it. 扫描完所有项目后,该值返回到调用它的函数。

This was working just fine for me while I was still running all of my code on the ui thread. 当我仍在ui线程上运行所有代码时,这对我来说工作得很好。 Everything was slow. 一切都很缓慢。 But it worked. 但这行得通。

However, I forgot that I removed a very crucial ...} else {... 但是,我忘了我删除了一个非常关键的...}其他{...

I believe I removed that one because it caused a stack overflow at some point, so I removed it and I guess forgot to put it back... 我相信我删除了那个,因为它在某个时候引起了堆栈溢出,所以我删除了它,我猜忘了放回去...

And here I was thinking my first SO question wouldn't be a noobish question... 在这里,我在想我的第一个SO问题不会是一个讨厌的问题...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法引用封闭 scope 中定义的非最终局部变量显示 - Cannot refer to the non-final local variable display defined in an enclosing scope 无法引用封闭范围中定义的非最终局部变量jSONString - Cannot refer to the non-final local variable jSONString defined in an enclosing scope 无法引用封闭范围中定义的非最终局部变量按钮,随机方法错误 - Cannot refer to the non-final local variable button defined in an enclosing scope, Random method error “不能引用封闭 scope 中定义的非最终局部变量。” 更改执行环境后 - “Cannot refer to the non-final local variable defined in an enclosing scope.” after changing Execution Environment 在Java中使用本地作用域中的非最终局部变量 - Use non-final local variable in local scope in Java 可运行的非最终字段是否不安全以提交给执行者? - Is a runnable with non-final fields unsafe to submit to an executor? 为什么非最终的“局部”变量不能在内部类中使用,而是封闭类的非最终字段可以? - Why a non-final “local” variable cannot be used inside an inner class, and instead a non-final field of the enclosing class can? Java使用非final变量来设置final变量 - Java use of non-final variable to set final variable 引用Java中匿名内部类中的封闭类的非final字段 - Referring to non-final fields of an enclosing class inside an anonymous inner class in Java 为什么匿名类可以访问封闭类的非final类成员 - Why can an anonymous class access non-final class member of the enclosing class
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM