I need to access a MySQL db from my android app. Now all the work is done through
DriverManager.getConnection(url);
and so on. But I have to access the db from multiple threads, so I have to use connection pooling.
Question 1. Is
com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();
the right way of creating the data source?
Question 2. If I write the previous line of code, my app compiles and installs on the device (not emulator) fine, but I get a weird
java.lang.NoClassDefFoundError: com.mysql.jdbc.jdbc2.optional.MysqlDataSource`
, that I can't catch with a try/catch handler:
try
{
com.mysql.jdbc.jdbc2.optional.MysqlDataSource a = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();
}
catch (Exception e)
{
I don't get here. The app just crashes, as if I had no try/catch block.
}
Question 3. I copied mysql-connector-java-5.1.20-bin.jar to the device and wrote the following code:
try
{
final String str = Environment.getExternalStorageState();
final File sd = getActivity().getExternalFilesDir(null);
final File file = new File(sd, "mysql-connector-java-5.1.20-bin.jar");
boolean b = file.exists();
final URLClassLoader cl = URLClassLoader.newInstance(new URL[] {file.toURI().toURL()} );
cl.loadClass("com.mysql.jdbc.jdbc2.optional.MysqlDataSource"); //$NON-NLS-1$
}
catch (Exception e)
{
new AlertDialog.Builder(getActivity())
.setMessage(ObjectConverter.throwable2String(e))
.show();
}
The file is found but
cl.loadClass()
fails with
java.lang.NullPointerException
at java.net.URLClassLoader.getPermissions(URLClassLoader.java:614)
at java.security.SecureClassLoader.getPD(SecureClassLoader.java:140)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:93)
at java.net.URLClassLoader.access$600(URLClassLoader.java:55)
at java.net.URLClassLoader$URLJarHandler.createClass(URLClassLoader.java:364)
at java.net.URLClassLoader$URLJarHandler.findClass(URLClassLoader.java:303)
at java.net.URLClassLoader.findClass(URLClassLoader.java:748)
at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
at ru.mypkg.myapp.func(myapp.java:367)
at android.view.View.performClick(View.java:3511)
at android.view.View$PerformClick.run(View.java:14110)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Any help greatly appreciated.
This question is 6 years old and Mark is correct that, in almost every scenario you can think of, JDBC on Android is about as sensible as trying to use a toaster in the bath. However, sometimes we do things because we can and not because we necessarily should, and today I had a justifiable reason to want to do this (for a specific very niche app running in an unusual environment), which is how I found this question.
Addressing the NoClassDefFoundError first, the reason it isn't caught by the catch block is because it's an Error not an Exception. Both Error and Exception inherit from Throwable, so you could catch that instead:
catch (Throwable t)
{
// This will catch NoClassDefFoundError
}
I believe that it's not MySqlDataSource that it can't find, but one of the classes or interfaces that it depends on - in my case it was javax.naming.Referenceable. Android doesn't provide the javax.naming package so trying to use the pooling features of the Connector/J JDBC driver for MySQL isn't going to get you very far (you could try to provide the missing dependencies but that road likely leads to madness).
Instead you'll probably have more luck with a third-party connection pool implementation. There are various Java libraries for doing this. Some of them will work on Android. One that I have verified does work is HikariCP. There are instructions for configuring it here and, specifically for MySQL, here.
Related
Is there a way to view the log on a tablet running 4.4? I've downloaded several apps like aLogCat and none of them show what my app writes out with S.o.p or Log.d. I have an intermittent bug that gives the Unfortunately appname has stopped message.Is there any way to view the log after this event without having to connect to a PC and use the adb program?
What other ways are there to get debug output? Would trapping the System.out and System.err classes get the stack trace?
Thanks,
Norm
You're focussing on tring to read out logcat, but there are better solutions for reading crash logs. My personal preference is Crashlytics, which automatically logs fatal exceptions and provides mechanisms for logging other messages.
The way all these crash reporters work, is by defining a UncaughtExceptionHandler:
Thread.setDefaultUncaughtExceptionHandler(
new MyUncaughtExceptionHandler(this));
If you prefer to use your own solution, you may want to look into using this. See this related question for more details.
Is there a way to view the log on a tablet running 4.4?
No, sorry. An app can only see its own log messages, not those from other apps. Hence, a third-party log viewer cannot see your app's messages.
Is there any way to view the log after this event without having to connect to a PC and use the adb program?
Use any standard crash management library, like ACRA, or services like Crashlytics, BugSense, etc.
The AIDE Application (Android Integrated Development Environment) allows one to develop android Apps directly on android device.
One particular feature is to read the logcat.
You can get it here https://play.google.com/store/apps/details?id=com.aide.ui
Here's the code I've put in the program. It seems to work:
// Define inner class to handle exceptions
class MyExceptionHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e){
java.util.Date dt = new java.util.Date();
String fn = LogFilePathPfx + "exception_" + sdf.format(dt) + ".txt";
try{
PrintStream ps = new PrintStream( fn );
e.printStackTrace(ps);
ps.close();
System.out.println("wrote trace to " + fn);
e.printStackTrace(); // capture here also???
SaveStdOutput.stop(); // close here vs calling flush() in class
}catch(Exception x){
x.printStackTrace();
}
lastUEH.uncaughtException(t, e); // call last one Gives: "Unfortunately ... stopped" message
return; //???? what to do here
}
}
lastUEH = Thread.getDefaultUncaughtExceptionHandler(); // save previous one
Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());
I know there is a million posts about this and i have read them all. I keep getting an internal error has occured when i try to sign in. I have tried multiple android-support-v4 and google-play-services jar files. I have tried including google play services as a library.
The only warning i am getting is this
03-11 11:58:52.465: W/dalvikvm(16989): VFY: unable to resolve static field 1363 (common_google_play_services_install_title) in Lcom/google/android/gms/R$string
I have cleaned and ran app this with every jar / project of the google librarys i could find.
this is my connection code
String scope = "oauth2:server:client_id:MY_ID:api_scope:" + Scopes.PLUS_LOGIN;
String token = "";
Bundle appActivities = new Bundle();
appActivities.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
"MY_ACTIVITY" );
try {
// We can retrieve the token to check via
// tokeninfo or to pass to a service-side
// application.
mAccountName = mPlusClient.getAccountName();
mAccessToken = GoogleAuthUtil.getToken(mContext, mAccountName, scope, appActivities);
} catch (GooglePlayServicesAvailabilityException playEx) {
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(
playEx.getConnectionStatusCode(), mContext,
0);
// Use the dialog to present to the user.
} catch (UserRecoverableAuthException recoverableException) {
Intent recoveryIntent = recoverableException.getIntent();
startActivityForResult(recoveryIntent, 8);
} catch (IOException e) {
e.printStackTrace();
} catch (GoogleAuthException e) {
e.printStackTrace();
}
** WARNING IF YOU HAVE SPENT A LARGE AMOUNT OF TIME ON THIS ISSUE ******
Follow these steps:
Find something soft, big and unbreakable
Sit down and buckle yourself in
So basically for me i resolved this issue by guess and check 40x and hopefully it works for you. So it turns out that my issues was the order in which i added the clientIds to the api console. First i added the SERVICE ACCOUNT then the WEB APPLICATIONS lastly ANDROID APPLICATION. The trick here is that i used the ClientID for the web application not the SERVICE OR ANDROID one. Idk why but that is the one that worked. I had cases before when the ClientID of the service account worked but didn't work when exchanging on the server side for the real access token. Anyway this process evolved a lot of guessing and hopefully it works for you. And remember that big soft object to let out your anger at (Hopefully not your local google dev)
As part of my app I get a list of apps installed on the device by using ApplicationPackageManager.getInstalledApplications but for some users I get crash reports saying that
TransactionTooLargeException at android.osBinderProxy.tranasact(Native Method)
Can anyone think why I'd get this?
I've found that this was solved on Android 5.1 (proof here, search for "Fix package manager TransactionTooLargeExceptions") as it was reported on multiple places:
https://code.google.com/p/android/issues/detail?id=95749
https://code.google.com/p/android/issues/detail?id=93717
https://code.google.com/p/android/issues/detail?id=69276
However, I wanted to solve this for pre-5.1, so I've come up with a solution (and suggested Google to put it on the support library, here) . Here's a short code version of what I've suggested:
public static List<PackageInfo> getInstalledPackages(Context context,int flags)
{
final PackageManager pm=context.getPackageManager();
try
{
return pm.getInstalledPackages(flags);
}
catch(Exception ignored)
{
//we don't care why it didn't succeed. We'll do it using an alternative way instead
}
// use fallback:
Process process;
List<PackageInfo> result=new ArrayList<>();
BufferedReader bufferedReader=null;
try
{
process=Runtime.getRuntime().exec("pm list packages");
bufferedReader=new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while((line=bufferedReader.readLine())!=null)
{
final String packageName=line.substring(line.indexOf(':')+1);
final PackageInfo packageInfo=pm.getPackageInfo(packageName,flags);
result.add(packageInfo);
}
process.waitFor();
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
if(bufferedReader!=null)
try
{
bufferedReader.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
return result;
}
What it does it to try using the official way first, and then, if failed, it fetches the package names using ADB command, and get the information of each of the apps, one after another.
It is much slower than the official one, but it didn't crash for me. I've tested it on Android emulators (2.3.x till 5.0.x, including), and on real devices too.
The time it took on my device (Galaxy S3 with custom rom of Android 5.1) is 1375-2012 ms (on 197 apps total) compared to 37-65 ms using the official way .
EDIT: people claim here that it's not fixed on Android 5.1 . I hope that it got fixed on Android 6 .
This exception is kind of difficult to reproduce under normal circumstances. You will get this exception when there IPC memory is exhausted when transferring data. This can occur in both cases, where a service is trying to place data to client or a client is sending data to service. Most probably some of your users might have installed huge number of application, which results in a data size greater than 1MB (which is the size of IPC buffer).
I am afraid in this case, you will not be do anything better. But if you are doing something like, applyBatch, you can separate one large transaction to multiple smaller transactions.
Also have a look at this thread What to do on TransactionTooLargeException
It seems that as of Android 2.2, there is a new feature for sending crash reports, as mentioned in the links:
http://www.androidcentral.com/new-android-app-crash-report-tool-already-and-running
http://android-developers.blogspot.com/2010/05/google-feedback-for-android.html
http://developer.android.com/sdk/android-2.2-highlights.html
http://www.youtube.com/watch?v=o8unC9bA4O8
How do I use this feature? Is it automatic for each application downloaded from the market (aka Google Play Store)?
Where can I find more info about this feature?
Also, is it possible to customize what is being sent, perhaps by using DefaultExceptionHandler, and put our own description of the crash?
NOTE: i know that there are plenty of tools for sending crash reports (like ACRA) , but i wish to check first if it's possible to use what's already given.
EDIT: I've succeeded modifying the exception that is passed further, hoping that this will also change the report that is sent to the developer website of Google.
Here's a sample code that is relevant for this:
private static class DefaultExceptionHandler implements java.lang.Thread.UncaughtExceptionHandler
...
#Override
public void uncaughtException(Thread t, Throwable e)
{
final StackTraceElement[] exceptionStackTrace = e.getStackTrace();
Exception exception = new Exception("my new exception!", e);
final StackTraceElement[] newExceptionStackTrace = new StackTraceElement[exceptionStackTrace.length + 1];
System.arraycopy(exceptionStackTrace, 0, newExceptionStackTrace, 1, exceptionStackTrace.length);
newExceptionStackTrace[0] = new StackTraceElement("TEST CLASS", "TEST METHOD", "TEST FILE", 0);
exception.setStackTrace(newExceptionStackTrace);
_defaultUEH.uncaughtException(t, exception); //this will hopefully call the default handling of the exception for reporting
}
What you have described sounds like the build in feature, and as far as I know, you cannot customize this. The data will be send to the googlePlay dev account which uploaded the app. I have seen customizations made by Sense, or Custom Roms. The only way to get your own Logs, is to use the DefaultErrorHandler you mentioned. As a good practice I would check, if you can catch the error yourself, (maybe log it somewhere). If not I would rethrow this error, to give the user a chance to give you hints , what he has done
I have application using Android 2.1 which utilize LocationManager to get the altitude. But now, I need to obtain the altitude using SensorManager which requires API Level 9 (2.3).
How can I put the SensorManager.getAltitude(float, float) in my 2.1 android application by putting a condition and calling it by a function name (possible in normal Java)?
Thank you in advance
UPDATE 1
If you have noticed that my application need to be compiled using Android 2.1. That's why I'm looking for a way to call the function by name or in any other way that can be compiled.
You need to build against the highest api you require and then code alternate code paths conditionally for other levels you want to support
To check current API level at execution time, the latest recommendation from the Android docs is to do something like this:
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD)
{
...
Once you introduce this complexity though, you have to be very careful. There isn't currently an automatic way to check all code paths to make sure that all api level calls above the minSdkVersion have alternative calls to support all versions. Maybe someone can chime in if there exists a unit testing tool that might do something like this.
You can call the method using reflection and fail gracefully in case of errors (like missing class or methods). See java.lang.reflect
Other option is to compile code in level 9 but surround with try/catch to catch errors that would arise from execution on lower level. It could be fairly error prone, though, and I'd think twice about doing it.
Update
Here is test code
public void onCreate(Bundle savedInstanceState)
{
try {
// First we try reflection approach.
// Expected result
// in 2.3 we print some value in log but no exception
// in 2.2 we print NoSuchMethodException
// In both levels we get our screen displayed after catch
Method m = SensorManager.class.getMethod("getAltitude",Float.TYPE, Float.TYPE);
Float a = (Float)m.invoke(null, 0.0f, 0.0f);
Log.w("test","Result 1: " + a);
} catch (Throwable e) {
Log.e("test", "error 1",e);
}
try {
// Now we try compiling against 2.3
// Expected result
// in 2.3 we print some value in log but no exception
// in 2.2 we print NoSuchMethodError (Note that it is an error not exception but it's still caught)
// In both levels we get our screen displayed after catch
float b = SensorManager.getAltitude(0.0f, 0.0f);
Log.w("test","Result 2: " + b);
} catch (Throwable e) {
Log.e("test", "error 2",e);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
Results:
2.3
09-14 07:04:50.374: DEBUG/dalvikvm(589): Debugger has detached; object registry had 1 entries
09-14 07:04:50.924: WARN/test(597): Result 1: NaN
09-14 07:04:51.014: WARN/test(597): Result 2: NaN
09-14 07:04:51.384: INFO/ActivityManager(75): Displayed com.example/.MyActivity: +1s65ms
2.2
09-14 07:05:48.220: INFO/dalvikvm(382): Could not find method android.hardware.SensorManager.getAltitude, referenced from method com.example.MyActivity.onCreate
09-14 07:05:48.220: WARN/dalvikvm(382): VFY: unable to resolve static method 2: Landroid/hardware/SensorManager;.getAltitude (FF)F
09-14 07:05:48.220: DEBUG/dalvikvm(382): VFY: replacing opcode 0x71 at 0x0049
09-14 07:05:48.220: DEBUG/dalvikvm(382): VFY: dead code 0x004c-0064 in Lcom/example/MyActivity;.onCreate (Landroid/os/Bundle;)V
09-14 07:05:48.300: ERROR/test(382): error 1
java.lang.NoSuchMethodException: getAltitude
at java.lang.ClassCache.findMethodByName(ClassCache.java:308)
Skipped stack trace
09-14 07:05:48.300: ERROR/test(382): error 2
java.lang.NoSuchMethodError: android.hardware.SensorManager.getAltitude
at com.example.MyActivity.onCreate(MyActivity.java:35)
Skipped more stack trace
09-14 07:05:48.330: DEBUG/dalvikvm(33): GC_EXPLICIT freed 2 objects / 64 bytes in 180ms
09-14 07:05:48.520: INFO/ActivityManager(59): Displayed activity com.example/.MyActivity: 740 ms (total 740 ms)
You can take advantage of how class isn't loaded until it is accessed for an easy work around that doesn't require reflection. You use an inner class with static methods to use your new apis. Here is a simple example.
public static String getEmail(Context context){
try{
if(Build.VERSION.SDK_INT > 4) return COMPATIBILITY_HACK.getEmail(context);
else return "";
}catch(SecurityException e){
Log.w(TAG, "Forgot to ask for account permisisons");
return "";
}
}
//Inner class required so incompatibly phones won't through an error when this class is accessed.
//this is the island of misfit APIs
private static class COMPATIBILITY_HACK{
/**
* This takes api lvl 5+
* find first gmail address in account and return it
* #return
*/
public static String getEmail(Context context){
Account[] accounts = AccountManager.get(context).getAccountsByType("com.google");
if(accounts != null && accounts.length > 0) return accounts[0].name;
else return "";
}
}
When the question is "Do I have this class or method at the current API level?" then use branching like:
class SomeClass {
public void someMethod(){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD)
{
//use classes and/or methods that were added in GINGERBREAD
}
}
}
For this you need to use an Android library that is Gingerbread or above. Otherwise the code won't compile with the classes added in Gingerbread.
This solution is MUCH more cleaner than the disgusting reflection stuff. Note that the dalvik will log a (not-lethal) error stating that he cannot find the classes added in GINGERBREAD when trying to load SomeClass but the app won't crash. It would only crash if we would try to USE that specific class and enter the IF branch - but we don't do that (unless we are on GINGERBREAD or later).
Note that the solution also works when you have a class that were there forever but a new method was added in Gingerbread. In runtime if you are running on pre-Gingerbread you just don't enter the IF branch and don't call that method thus the app will not crash.
Here how you do it using reflection (Calling StrictMode class from the level where it is not available:
try {
Class<?> strictmode = Class.forName("android.os.StrictMode");
Method enableDefaults = strictmode.getMethod("enableDefaults");
enableDefaults.invoke(null, new Object[] {});
} catch (Exception e) {
Log.i(TAG, e.getMessage());
}
I haven't tried it - but it should be possible, using some code generation, to create a proxy library (per API level) that will wrap the entire native android.jar and whose implementation will try to invoke the methods from android.jar.
This proxy lib will use either the above mentioned internal-static-class way or reflection to make the dalvikvm lazily link to the requested method.
It will let the user access all the API she wants (assuming she'll check for correct API level) and prevent the unpleasant dalvikvm log messages. You could also embed each method's API level and throw a usable exception (BadApiLevelException or something)
(Anyone knows why Google/Android don't already do something like that?)