小编ath*_*hos的帖子

是否真的可以避免Java终结器用于本机对等体对象生命周期管理?

根据我作为C++/Java/Android开发人员的经验,我已经了解到终结器几乎总是一个坏主意,唯一的例外是管理Java所需的"本地对等"对象来调用C/C++代码通过JNI.

我知道JNI:正确管理java对象问题的生命周期,但是这个问题解决了不使用终结器的原因,对于本地对等体也是如此.因此,在上述问题中对答案的解释是一个问题/讨论.

Joshua Bloch在他的Effective Java中明确将此案例列为他不使用终结器的着名建议的例外:

终结器的第二个合法使用涉及与本地对等体的对象.本机对等体是普通对象通过本机方法委托的本机对象.由于本机对等体不是普通对象,因此垃圾回收器不知道它,并且在回收Java对等体时无法回收它.假设本地对等方没有关键资源,终结器是执行此任务的适当工具.如果本机对等体拥有必须立即终止的资源,则该类应具有显式终止方法,如上所述.终止方法应该执行释放关键资源所需的任何操作.终止方法可以是本机方法,也可以调用一个.

(另请参阅"堆栈交换中的为什么最终方法包含在Java中?"问题)

然后我看到真正有趣的如何在Google I/O '17中管理Android演讲中的本机内存,其中Hans Boehm实际上主张使用终结器来管理java对象的本地对等,同时引用Effective Java作为参考.在快速提及为什么显式删除本地对等或基于范围的自动关闭可能不是一个可行的替代方案后,他建议使用java.lang.ref.PhantomReference.

他提出了一些有趣的观点,但我并不完全相信.我将尝试通过其中一些并陈述我的疑虑,希望有人能够进一步了解它们.

从这个例子开始:

class BinaryPoly {

    long mNativeHandle; // holds a c++ raw pointer

    private BinaryPoly(long nativeHandle) {
        mNativeHandle = nativeHandle;
    }

    private static native long nativeMultiply(long xCppPtr, long yCppPtr);

    BinaryPoly multiply(BinaryPoly other) {
        return new BinaryPoly ( nativeMultiply(mNativeHandle, other.mNativeHandler) );
    }

    // …

    static native void nativeDelete (long …
Run Code Online (Sandbox Code Playgroud)

java java-native-interface android googleio

36
推荐指数
2
解决办法
1282
查看次数

如果选择了非零位置,则旋转后Spinner的onItemSelected回调调用两次

当我创建我的活动时,我设置了一个Spinner,为它分配一个监听器和一个初始值.我知道onItemSelected在应用程序初始化期间会自动调用回调.我觉得很奇怪的是,当设备旋转时会发生两次这种情况,这会导致我遇到一些问题,我不得不绕过它.如果微调器初始选择为零,则不会发生这种情况.我能够隔离问题,这是触发它的最简单的活动:

public class MainActivity extends Activity implements OnItemSelectedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i("Test","Activity onCreate");
    setContentView(R.layout.activity_main);
    ((Spinner)findViewById(R.id.spinner1)).setSelection(2);
    ((Spinner)findViewById(R.id.spinner1)).setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> spin, View selview, int pos, long selId)
{
    Log.i("Test","spin:"+spin+" sel:"+selview+" pos:"+pos+" selId:"+selId);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {}
}
Run Code Online (Sandbox Code Playgroud)

这是启动应用程序然后旋转设备时显示的logcat:

    I/Test( 9881): spin:android.widget.Spinner@4052f508 sel:android.widget.TextView@40530b08 pos:2 selId:2
    I/Test( 9881): Activity onCreate
    I/Test( 9881): spin:android.widget.Spinner@40535d80 sel:android.widget.TextView@40538758 pos:2 selId:2
    I/Test( 9881): spin:android.widget.Spinner@40535d80 sel:android.widget.TextView@40538758 pos:2 selId:2
Run Code Online (Sandbox Code Playgroud)

这是预期的行为吗?我错过了什么吗?

android android-widget android-spinner

28
推荐指数
4
解决办法
1万
查看次数

如何知道我正在使用ndk-build构建哪个API级别?

我正在尝试更好地理解在使用ndk-build时如何选择api级别.

我知道我可以明确地设置APP_PLATFORMApplication.mk,并且否则NDK建造将针对在清单中与指定的API android:minSdkVersion,但如果我的应用程序清单既具有什么android:minSdkVersionandroid:targetSdkVersion,这比的minSdkVersion高?

ndk-build会定位targetSdkVersion吗?我怎么检查呢?

如果它针对更高的api级别,我想我将能够使用仅适用于该级别的本机apis进行构建,但是如果我在具有较低api级别的设备上运行该应用程序,它应该会失败,所以在这种情况下我应该实现某种api级别的检查,这是正确的吗?

c c++ android android-ndk android-manifest

3
推荐指数
2
解决办法
7952
查看次数

使用android ndk调试内存损坏

我在我的android应用程序的本机部分中获得了一个段错误,此时void函数返回其调用者.为了更好地可视化,我在被调用者函数的末尾添加了一个日志语句,在调用者函数的一个函数中,在调用被调用者之后(对于双关语抱歉).在logcat中,打印第一条消息,第二条消息打印(应用程序崩溃).

考虑到可能的内存损坏我决定激活malloc debug(在adb shell中给出"setprop libc.debug.malloc 10").然后,我在来自被调用函数结束的日志消息之后的logcat中得到这个:

D/MyApp - NativeSide(12778):  I am the callee function and I am about to return!
E/libc    (12778): *** FREE CHECK: buffer 0x82869900 corrupted 16 bytes before allocation
E/libc    (12778): call stack:
E/libc    (12778):  0: 8000e3ea
E/libc    (12778):  1: 8000e49c
E/libc    (12778):  2: 8000e4e2
E/libc    (12778):  3: 8000e540
E/libc    (12778):  4: afd14ccc
E/libc    (12778):  5: 81258188
E/libc    (12778):  6: 81258188
E/libc    (12778):  7: 81258188
E/libc    (12778):  8: 81258188
E/libc    (12778):  9: 81258188
E/libc    (12778): 10: 81258188 …
Run Code Online (Sandbox Code Playgroud)

memory memory-management android-ndk

2
推荐指数
1
解决办法
1万
查看次数