[and] Native crash fix: prevent AssetManager garbage collection + moved statics to Java.

This commit is contained in:
d-kunin 2013-08-13 17:11:44 +03:00
parent 5c2c344934
commit 627640377d
8 changed files with 163 additions and 110 deletions

View file

@ -8,7 +8,6 @@
#include <android/asset_manager_jni.h>
class AndStorage
{
class AssetReader : public rd::Reader
@ -33,16 +32,15 @@ class AndStorage
};
public:
AndStorage()
: m_isInited(false) {}
void Init(JNIEnv * env, jobject manager)
{
AssetReader reader("index.dat", AAssetManager_fromJava(env, manager));
m_storage.Load(reader);
}
static AndStorage & Instance()
{
static AndStorage storage;
return storage;
m_isInited = true;
}
void Query(string const & query, bool useLocation, double lat, double lon)
@ -73,8 +71,14 @@ public:
return m_storage.GetArticleInfoFromUrl(url);
}
bool IsInited()
{
return m_isInited;
}
private:
Storage m_storage;
bool m_isInited;
};
#ifdef __cplusplus
@ -82,31 +86,13 @@ extern "C"
{
#endif
// TODO: *problem
//#define DECLARE_FN(retType, suffix)
AndStorage m_andStorage;
#define STORAGE m_andStorage
#define STORAGE AndStorage::Instance()
/*
* Class: com_example_travelguide_cpp_Storage
* Method: query
* Signature: (Ljava/lang/String;DD)V
*/
JNIEXPORT void JNICALL Java_com_example_travelguide_cpp_Storage_query(JNIEnv * env, jclass clazz, jstring query,
jboolean useLocation, jdouble lat, jdouble lon)
void InitStorage(JNIEnv * env, jobject jAssetManager)
{
STORAGE.Query(JString2StdString(env, query), useLocation, lat, lon);
}
/*
* Class: com_example_travelguide_cpp_Storage
* Method: getResultSize
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_travelguide_cpp_Storage_getResultSize
(JNIEnv * env, jclass clazz)
{
return STORAGE.GetResultSize();
if (!STORAGE.IsInited())
m_andStorage.Init(env, jAssetManager);
}
jobject NativeArticle2JavaArticle(JNIEnv * env, ArticleInfo const * p)
@ -126,25 +112,41 @@ jobject NativeArticle2JavaArticle(JNIEnv * env, ArticleInfo const * p)
p->m_lon);
}
/*
* Class: com_example_travelguide_cpp_Storage
* Method: query
* Signature: (Ljava/lang/String;DD)V
*/
JNIEXPORT void JNICALL Java_com_example_travelguide_cpp_Storage_query(JNIEnv * env, jobject thiz, jstring query,
jboolean useLocation, jdouble lat, jdouble lon)
{
STORAGE.Query(JString2StdString(env, query), useLocation, lat, lon);
}
/*
* Class: com_example_travelguide_cpp_Storage
* Method: getResultSize
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_travelguide_cpp_Storage_getResultSize
(JNIEnv * env, jobject thiz)
{
return STORAGE.GetResultSize();
}
/*
* Class: com_example_travelguide_cpp_Storage
* Method: getArticleInfoByIndex
* Signature: (I)Lcom/example/travelguide/article/ArticleInfo;
*/
JNIEXPORT jobject JNICALL Java_com_example_travelguide_cpp_Storage_getArticleInfoByIndex
(JNIEnv * env, jclass clazz, jint index)
(JNIEnv * env, jobject thiz, jint index)
{
return NativeArticle2JavaArticle(env, &STORAGE.GetArticleInfoByIndex(index));
}
JNIEXPORT void JNICALL Java_com_example_travelguide_cpp_Storage_nativeInitIndex
(JNIEnv * env, jclass clazz, jobject assetManager)
{
STORAGE.Init(env, assetManager);
}
JNIEXPORT jobject JNICALL Java_com_example_travelguide_cpp_Storage_getArticleInfoByUrl
(JNIEnv * env, jclass clazz, jstring url)
(JNIEnv * env, jobject thiz, jstring url)
{
return NativeArticle2JavaArticle(env, STORAGE.GetArticleInfoByUrl(JString2StdString(env, url)));
}
@ -155,6 +157,16 @@ JNIEXPORT jboolean JNICALL Java_com_example_travelguide_cpp_Storage_isValidLatLo
return ll::ValidLat(lat) && ll::ValidLon(lon) ? JNI_TRUE : JNI_FALSE;
}
/*
* Class: com_example_travelguide_cpp_Storage
* Method: nativeCreate
* Signature: (Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_com_example_travelguide_cpp_Storage_nativeInit
(JNIEnv * env, jobject thiz, jobject jAssetManager)
{
InitStorage(env, jAssetManager);
}
#ifdef __cplusplus
}

View file

@ -9,11 +9,19 @@ extern "C" {
#endif
/*
* Class: com_example_travelguide_cpp_Storage
* Method: getTitleByUrl
* Signature: (Ljava/lang/String;)Ljava/lang/String;
* Method: nativeCreate
* Signature: (Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_com_example_travelguide_cpp_Storage_nativeInit
(JNIEnv *, jobject, jobject);
/*
* Class: com_example_travelguide_cpp_Storage
* Method: getArticleInfoByUrl
* Signature: (Ljava/lang/String;)Lcom/example/travelguide/article/ArticleInfo;
*/
JNIEXPORT jobject JNICALL Java_com_example_travelguide_cpp_Storage_getArticleInfoByUrl
(JNIEnv *, jclass, jstring);
(JNIEnv *, jobject, jstring);
/*
* Class: com_example_travelguide_cpp_Storage
@ -21,7 +29,7 @@ JNIEXPORT jobject JNICALL Java_com_example_travelguide_cpp_Storage_getArticleInf
* Signature: (Ljava/lang/String;ZDD)V
*/
JNIEXPORT void JNICALL Java_com_example_travelguide_cpp_Storage_query
(JNIEnv *, jclass, jstring, jboolean, jdouble, jdouble);
(JNIEnv *, jobject, jstring, jboolean, jdouble, jdouble);
/*
* Class: com_example_travelguide_cpp_Storage
@ -29,7 +37,7 @@ JNIEXPORT void JNICALL Java_com_example_travelguide_cpp_Storage_query
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_example_travelguide_cpp_Storage_getResultSize
(JNIEnv *, jclass);
(JNIEnv *, jobject);
/*
* Class: com_example_travelguide_cpp_Storage
@ -37,15 +45,15 @@ JNIEXPORT jint JNICALL Java_com_example_travelguide_cpp_Storage_getResultSize
* Signature: (I)Lcom/example/travelguide/article/ArticleInfo;
*/
JNIEXPORT jobject JNICALL Java_com_example_travelguide_cpp_Storage_getArticleInfoByIndex
(JNIEnv *, jclass, jint);
(JNIEnv *, jobject, jint);
/*
* Class: com_example_travelguide_cpp_Storage
* Method: nativeInitIndex
* Signature: (Ljava/lang/Object;)V
* Method: isValidLatLon
* Signature: (DD)Z
*/
JNIEXPORT void JNICALL Java_com_example_travelguide_cpp_Storage_nativeInitIndex
(JNIEnv *, jclass, jobject);
JNIEXPORT jboolean JNICALL Java_com_example_travelguide_cpp_Storage_isValidLatLon
(JNIEnv *, jclass, jdouble, jdouble);
#ifdef __cplusplus
}

View file

@ -27,23 +27,11 @@ public class ArticleInfoDetailActivity extends FragmentActivity
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_articleinfo_detail);
// Show the Up button in the action bar.
// getActionBar().setDisplayHomeAsUpEnabled(true);
// savedInstanceState is non-null when there is fragment state
// saved from previous configurations of this activity
// (e.g. when rotating the screen from portrait to landscape).
// In this case, the fragment will automatically be re-added
// to its container so we don't need to manually add it.
// For more information, see the Fragments API guide at:
//
// http://developer.android.com/guide/components/fragments.html
//
if (savedInstanceState == null)
{
// Create the detail fragment and add it to the activity
// using a fragment transaction.
Bundle arguments = new Bundle();
final Bundle arguments = new Bundle();
arguments.putSerializable(ArticleInfoDetailFragment.ARTICLE_INFO,
getIntent().getSerializableExtra(ArticleInfoDetailFragment.ARTICLE_INFO));

View file

@ -14,6 +14,7 @@ import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import com.example.travelguide.ArticleInfoListFragment.OnListIconClickedListener;
import com.example.travelguide.article.ArticleInfo;
import com.example.travelguide.article.ArticlePathFinder;
@ -27,7 +28,8 @@ import com.susanin.travelguide.R;
* either contained in a {@link ArticleInfoListActivity} in two-pane mode (on
* tablets) or a {@link ArticleInfoDetailActivity} on handsets.
*/
public class ArticleInfoDetailFragment extends Fragment implements OnClickListener
public class ArticleInfoDetailFragment extends Fragment
implements OnClickListener
{
public static final String ARTICLE_INFO = "article_info";
@ -41,6 +43,7 @@ public class ArticleInfoDetailFragment extends Fragment implements OnClickListen
private View mProgressContainer;
private ArticlePathFinder mFinder;
// private Storage mStorage;
private OnListIconClickedListener mIconClickedListener;
@ -129,7 +132,7 @@ public class ArticleInfoDetailFragment extends Fragment implements OnClickListen
if (!Utils.isExternalUrl(url) && url.endsWith(".html"))
{
final String strippedUrl = url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.'));
mTitle.setText(Storage.getArticleInfoByUrl(strippedUrl).getName());
mTitle.setText(getStorage().getArticleInfoByUrl(strippedUrl).getName());
}
}
@ -179,4 +182,9 @@ public class ArticleInfoDetailFragment extends Fragment implements OnClickListen
mWebView.goBack();
}
public Storage getStorage()
{
return Storage.get(getActivity());
}
}

View file

@ -6,6 +6,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import com.example.travelguide.article.ArticleInfo;
import com.example.travelguide.cpp.Storage;
import com.mapswithme.maps.api.MWMPoint;
@ -28,8 +29,10 @@ import com.susanin.travelguide.R;
* {@link ArticleInfoListFragment.Callbacks} interface to listen for item
* selections.
*/
public class ArticleInfoListActivity extends FragmentActivity implements ArticleInfoListFragment.Callbacks,
ArticleInfoListFragment.OnListIconClickedListener, ArticleInfoListFragment.OnFirstLoadListener
public class ArticleInfoListActivity extends FragmentActivity
implements ArticleInfoListFragment.Callbacks,
ArticleInfoListFragment.OnListIconClickedListener,
ArticleInfoListFragment.OnFirstLoadListener
{
private static final String TAG = ArticleInfoListActivity.class.getSimpleName();
@ -48,11 +51,14 @@ public class ArticleInfoListActivity extends FragmentActivity implements Article
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_articleinfo_list);
mArtInfoListFragment = (ArticleInfoListFragment) getSupportFragmentManager()
.findFragmentById(R.id.articleinfo_list);
if (findViewById(R.id.articleinfo_detail_container) != null)
{
mTwoPane = true;
mArtInfoListFragment = (ArticleInfoListFragment) getSupportFragmentManager().findFragmentById(R.id.articleinfo_list);
mArtInfoListFragment.setActivateOnItemClick(true);
mArtInfoListFragment.setOnFirstLoadListener(this);
mArtInfoListFragment.setHeaderVisible(false);
@ -88,7 +94,9 @@ public class ArticleInfoListActivity extends FragmentActivity implements Article
{
final String id = point.getId();
Log.d(TAG, id);
onItemSelected(Storage.getArticleInfoByUrl(id));
// final Storage storage = new Storage(getAssets());
// storage.create();
onItemSelected(Storage.get(this).getArticleInfoByUrl(id));
}
}

View file

@ -3,7 +3,9 @@ package com.example.travelguide;
import static com.example.travelguide.util.Utils.hideIf;
import static com.example.travelguide.util.Utils.hideView;
import static com.example.travelguide.util.Utils.showView;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.location.Location;
@ -22,6 +24,7 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TextView;
import com.example.travelguide.article.ArticleInfo;
import com.example.travelguide.async.QueryResultLoader;
import com.example.travelguide.cpp.Storage;
@ -40,10 +43,10 @@ import com.susanin.travelguide.R;
* Activities containing this fragment MUST implement the {@link Callbacks}
* interface.
*/
public class ArticleInfoListFragment extends ListFragment implements LoaderCallbacks<Storage>, TextWatcher,
OnClickListener, LocationListener
public class ArticleInfoListFragment extends ListFragment
implements LoaderCallbacks<Storage>, TextWatcher,
OnClickListener, LocationListener
{
private static final String TAG = ArticleInfoListFragment.class.getSimpleName();
public interface OnListIconClickedListener
{
@ -63,7 +66,6 @@ public class ArticleInfoListFragment extends ListFragment implements LoaderCallb
{
mOnFirstLoad = listener;
}
// !First load
private View mRootView;
@ -82,6 +84,8 @@ public class ArticleInfoListFragment extends ListFragment implements LoaderCallb
private final static long LOCATION_UPDATE_INTERVAL = 30 * 60 * 1000;
// !Location
// private Storage mStorage;
private static final String STATE_ACTIVATED_POSITION = "activated_position";
private Callbacks mCallbacks = sDummyCallbacks;
@ -126,9 +130,17 @@ public class ArticleInfoListFragment extends ListFragment implements LoaderCallb
Utils.hideKeyboard(getActivity());
checkLocation();
search(mSearchText.getText().toString());
}
@Override
public void onPause()
{
super.onPause();
mLocationManager.removeUpdates(this);
}
private void checkLocation()
{
if (System.currentTimeMillis() - sLastLocationRequestTime > LOCATION_UPDATE_INTERVAL)
@ -142,12 +154,7 @@ public class ArticleInfoListFragment extends ListFragment implements LoaderCallb
}
}
@Override
public void onPause()
{
super.onPause();
mLocationManager.removeUpdates(this);
}
@Override
public void onAttach(Activity activity)
@ -253,14 +260,13 @@ public class ArticleInfoListFragment extends ListFragment implements LoaderCallb
final Location loc = getLocation();
if (loc != null)
return new QueryResultLoader(getActivity(), query, loc.getLatitude(), loc.getLongitude());
return new QueryResultLoader(getActivity(), getStorage(), query, loc.getLatitude(), loc.getLongitude());
else
return new QueryResultLoader(getActivity(), query);
return new QueryResultLoader(getActivity(), getStorage(), query);
}
return null;
}
@SuppressWarnings("static-access")
@Override
public void onLoadFinished(Loader<Storage> loader, Storage result)
{
@ -320,14 +326,14 @@ public class ArticleInfoListFragment extends ListFragment implements LoaderCallb
private void openInMwm()
{
final int count = Storage.getResultSize();
final int count = getStorage().getResultSize();
if (count < 1)
return;
final ArrayList<MWMPoint> points = new ArrayList<MWMPoint>();
for (int i = 0; i < count; ++i)
{
final ArticleInfo info = Storage.getArticleInfoByIndex(i);
final ArticleInfo info = getStorage().getArticleInfoByIndex(i);
if (info.isValidLatLon())
points.add(Utils.articleInfo2MwmPoint(info));
}
@ -368,4 +374,9 @@ public class ArticleInfoListFragment extends ListFragment implements LoaderCallb
public void onStatusChanged(String provider, int status, Bundle extras)
{}
public Storage getStorage()
{
return Storage.get(getActivity());
}
}

View file

@ -7,34 +7,36 @@ import com.example.travelguide.cpp.Storage;
public class QueryResultLoader extends AsyncTaskLoader<Storage>
{
private final Storage mStorage;
private final String mQuery;
private final double mLat;
private final double mLon;
private final boolean mUseLocation;
public QueryResultLoader(Context context, String query)
public QueryResultLoader(Context context, Storage storage, String query)
{
super(context);
mQuery = query;
mUseLocation = false;
mLat = mLon = 0;
mStorage = storage;
}
public QueryResultLoader(Context context, String query, double lat, double lon)
public QueryResultLoader(Context context, Storage storage, String query, double lat, double lon)
{
super(context);
mQuery = query;
mUseLocation = true;
mLat = lat;
mLon = lon;
mStorage = storage;
}
@Override
public Storage loadInBackground()
{
Storage.initAssets(getContext());
Storage.query(mQuery, mUseLocation, mLat, mLon);
return new Storage();
mStorage.query(mQuery, mUseLocation, mLat, mLon);
return mStorage;
}
}

View file

@ -7,32 +7,48 @@ import com.example.travelguide.article.ArticleInfo;
public class Storage
{
private static volatile Storage sInstance;
public static Storage get(Context context)
{
if (sInstance == null)
synchronized (Storage.class)
{
if (sInstance == null)
{
sInstance = new Storage(context.getAssets());
sInstance.init();
}
}
return sInstance;
}
// We need to store this reference
// in order to prevent garbage collection
// see asset_manager_jni.h
private final AssetManager mAssetManager;
private Storage(AssetManager assetManager)
{
mAssetManager = assetManager;
}
private void init()
{
nativeInit(mAssetManager);
}
private native void nativeInit(Object jAssetManager);
public native ArticleInfo getArticleInfoByUrl(String url);
public native void query(String query, boolean useLocation, double lat, double lon);
public native int getResultSize();
public native ArticleInfo getArticleInfoByIndex(int index);
public native static boolean isValidLatLon(double lat, double lon);
static
{
System.loadLibrary("tguide");
}
private static boolean sIsAssetsInited = false;
public static void initAssets(Context context)
{
if (sIsAssetsInited)
return;
final AssetManager am = context.getAssets();
nativeInitIndex(am);
sIsAssetsInited = true;
}
public native static ArticleInfo getArticleInfoByUrl(String url);
public native static void query(String query, boolean useLocation, double lat, double lon);
public native static int getResultSize();
public native static ArticleInfo getArticleInfoByIndex(int index);
private native static void nativeInitIndex(AssetManager am);
public native static boolean isValidLatLon(double lat, double lon);
}