Ekibim, harici dosya dizinlerindeki yazılabilir moddaki veritabanlarına erişemediği için, uygulamanızın kullanılamaz hale geldiği Nexus 9'da bir hata buldu. Uygulamanın JNI'yi kullanması ve sadece kodda bir arm64-v8a sürümü içermemesi durumunda gerçekleşir.Nexus 9 SQLite dosyası için geçici çözüm Harici yönlerde yazma işlemleri?
Geçerli kuramımız, yalnızca armeabi veya armeabi-v7a kitaplıklarına sahip uygulamalarla geriye dönük olarak uyumlu olması için, arm64-v8a'nın dahil edilmemesi durumunda Nexus 9'un yerel kitaplıkların bazı alternatif sürümlerini içermesidir. Yukarıdaki operasyonları engelleyen alternatif SQLite kütüphanelerinin bazılarında bir hata var gibi görünüyor.
kimse bu sorunla ilgili herhangi bir geçici çözüm buldu? Tüm yerel kitaplıklarımızı arm64'te yeniden oluşturmak, şimdiki izimiz ve en eksiksiz çözümdür, ancak bu bize zaman kazandıracaktır (bazı kütüphanelerimiz haricidır) ve Nexus 9'umuz için uygulamayı düzeltmek için mümkünse daha hızlı bir dönüş yapmayı tercih ederiz. kullanıcılar.
Kolayca bu basit örnek proje (son Android NDK gerekir) bu konuyu görebilirsiniz.
- projeye aşağıda dosyaları ekleyin.
- Eğer yoksa, en son Android NDK'u yükleyin.
- Proje dizinine
ndk-build
'u çalıştırın. - Yenileyin, kurun, yükleyin ve çalıştırın.
- Eğer Android.mk veya Application.mk değiştirmek yeniden
ndk-build
çalıştırmadan önce kütüphanelerini ve obj klasörleri silerek projeyi temizledikten . Ayrıca her bir
ndk-build
'dan sonra projenizi manuel olarak yenilemeniz gerekir. 01221606a
Nexus 9'daki "bozuk" yapı, dahili dosyalar ile birlikte çalıştığını, ancak harici dosyalarla çalışmadığına dikkat edin.
src/com/example/dbtester/DBTesterActivity.java
package com.example.dbtester;
import java.io.File;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class DBTesterActivity extends Activity {
protected static final String TABLE_NAME = "table_timestamp";
static {
System.loadLibrary("DB_TESTER");
}
private File mDbFileExternal;
private File mDbFileInternal;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dbtester);
mDbFileExternal = new File(getExternalFilesDir(null), "tester_ext.db");
mDbFileInternal = new File(getFilesDir(), "tester_int.db");
((Button)findViewById(R.id.button_e_add)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
addNewTimestamp(true);
}
});
((Button)findViewById(R.id.button_e_del)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
deleteDbFile(true);
}
});
((Button)findViewById(R.id.button_i_add)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
addNewTimestamp(false);
}
});
((Button)findViewById(R.id.button_i_del)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
deleteDbFile(false);
}
});
((Button)findViewById(R.id.button_display)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
setMessageView(getNativeMessage());
}
});
}
private void addNewTimestamp(boolean external) {
long time = System.currentTimeMillis();
File file;
if (external) {
file = mDbFileExternal;
} else {
file = mDbFileInternal;
}
boolean createNewDb = !file.exists();
SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getAbsolutePath(), null,
SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.NO_LOCALIZED_COLLATORS
| SQLiteDatabase.OPEN_READWRITE);
if (createNewDb) {
db.execSQL("CREATE TABLE " + TABLE_NAME + "(TIMESTAMP INT PRIMARY KEY)");
}
ContentValues values = new ContentValues();
values.put("TIMESTAMP", time);
db.insert(TABLE_NAME, null, values);
Cursor cursor = db.query(TABLE_NAME, null, null, null, null, null, null);
setMessageView("Table now has " + cursor.getCount() + " entries." + "\n\n" + "Path: "
+ file.getAbsolutePath());
}
private void deleteDbFile(boolean external) {
// workaround for Android bug that sometimes doesn't delete a file
// immediately, preventing recreation
File file;
if (external) {
file = mDbFileExternal;
} else {
file = mDbFileInternal;
}
// practically guarantee unique filename by using timestamp
File to = new File(file.getAbsolutePath() + "." + System.currentTimeMillis());
file.renameTo(to);
to.delete();
setMessageView("Table deleted." + "\n\n" + "Path: " + file.getAbsolutePath());
}
private void setMessageView(String msg) {
((TextView)findViewById(R.id.text_messages)).setText(msg);
}
private native String getNativeMessage();
}
res/düzen/dbtester.xml
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnCount="1" >
<Button
android:id="@+id/button_e_add"
android:text="Add Timestamp EXT" />
<Button
android:id="@+id/button_e_del"
android:text="Delete DB File EXT" />
<Button
android:id="@+id/button_i_add"
android:text="Add Timestamp INT" />
<Button
android:id="@+id/button_i_del"
android:text="Delete DB File INT" />
<Button
android:id="@+id/button_display"
android:text="Display Native Message" />
<TextView
android:id="@+id/text_messages"
android:text="Messages appear here." />
</GridLayout>
jni/Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS += -std=c99
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
LOCAL_MODULE := DB_TESTER
LOCAL_SRC_FILES := test.c
include $(BUILD_SHARED_LIBRARY)
jni/Application.mk (BROKEN)
APP_ABI := armeabi-v7a
JNI'yı/Application.mk ( ÇALIŞMA )
APP_ABI := armeabi-v7a arm64-v8a
JNI'yı/test.c
#include <jni.h>
JNIEXPORT jstring JNICALL Java_com_example_dbtester_DBTesterActivity_getNativeMessage
(JNIEnv *env, jobject thisObj) {
return (*env)->NewStringUTF(env, "Hello from native code!");
}
AndroidManifest.Nexus 9 kırık yapı çalıştırırsanız xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.dbtester"
android:versionCode="10"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="21" />
<application>
<activity
android:name="com.example.dbtester.DBTesterActivity"
android:label="DB Tester" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
, aşağıdaki gibi LogCat içinde SQLiteLog hata mesajlarını göreceksiniz:
SQLiteLog: (28) file renamed while open: /storage/emulated/0/Android/data/com.example.dbtester/files/tester.db
SQLiteDatabase: android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032)
* İlginçtir ki, sen veritabanı dosyaları depolamak ise iç dosya dizini, veritabanları yazılabilir modunda erişilebilir ARE. Ancak, bazı büyük veritabanlarımız var ve hepsini iç klasörlere taşımak istemiyor. erişilen
* Dış dosya dizini {sdcard} /Android/data/com.example.dbtester ve Context.getExternalFilesDir (null) ve Context.getExternalCacheDir dahil tüm alt klasörler() klasörler vardır. Bu klasörlere erişmek için Lollipop'ta okuma/yazma izinleri artık gerekli değildir, ancak bu izinleri tamamen açık ve kapalı olarak test ettim.
Harika bir soru. Eğer gün içinde bir cevap alamazsan, bunun üzerine kendi lütfumu koyacağım. – Simon
xbmc/Kodi ile tam olarak aynı sorunu yaşıyoruz. Bilmemiz iyi değil, yalnız değiliz. – Koying