I have a bit of an issue with some Async tasks that are running in my android application. Due to the fact I am using some network IO, it can sometime's take longer than expected and block other async tasks running.
I need to keep my target and min sdk versions as they are but they are targeting targetSdkVersion="15" with a minSdkVersion="8". I require it so that when an Async task is called, I can check the devices SDK and if is greater than 11 it can call executeOnExecutor() as opposed to just execute to allow the device to run these in parallel and prevent this blocking operation.
Although I have a target SDK of 15, the device I am using has an SDK of 17.
However when calling:
MyAsyncTask(this).executeOnExecutor();
I get an error "The method executeOnExecutor() is undefined for the type" and the only option available to me is:
MyAsyncTask(this).execute();
MyAsyncTask is a class object that extends AsyncTask and overloads the standard methods onPreExecute, doInBackground, onProgressUpdate & onPostExecute.
I tried following some of the advice listed here... http://steveliles.github.io/android_s_asynctask.html
Set your build target to API Level 11 or higher. Note that "build target" != "target SDK" (android:targetSdkVersion). "Build target" is set in Project > Properties > Android on Eclipse, or project.properties for command-line builds.
For conditional use of executeOnExecutor(), another approach is to use a separate helper method:
#TargetApi(11)
static public <T> void executeAsyncTask(AsyncTask<T, ?, ?> task,
T... params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
else {
task.execute(params);
}
}
You would then use this method to kick off your tasks:
executeAsyncTask(new MyTask(), param1, param2);
(for however many parameters you wanted to pass to execute())
Related
I'm Creating a Alarm based Application with API version 23. But TimePicker getCurrentMinute,getCurrentHour method is Deprecated. Which method will be replace of this method ?
EDIT:
According to google documentation now we have to use getMinute() and getHour() method. But it's only working in api 23. what we have to do for other api?
Just check the Android official docs .. http://developer.android.com/reference/android/widget/TimePicker.html
it tells you everything
public Integer getCurrentMinute ()
Added in API level 1
This method was deprecated in API level 23.
Use getMinute()
Returns
the current minute
and
public Integer getCurrentHour ()
Added in API level 1
This method was deprecated in API level 23.
Use getHour()
Returns
the current hour in the range (0-23)
Android offers runtime checks for API version.
Most of the time in such cases, you'll want to run cases like
if (Build.VERSION.SDK_INT >= 23 )
myTimePickerView.getHour();
else
myTimePickerView.getCurrentHour();
#see http://developer.android.com/reference/android/os/Build.VERSION.html
i faced same issue. if you write your code such as
getCurrentHour()*(striked through) it doesnt make an issue
the code runs gets complied and runs saying
"complete build performed and run since..."
since if you use getHour or getMinute you have to put the version.SDK if else statement (which i tried but didnt work at that time for some reason)
and none the less increases code lines and complexity.
I'm developing an app that requires multiple files being downloaded simultaneously. I am creating an AsyncTask with its own HttpClient for each file, but the next file only starts downloading after the previous one finished.
Might it be a server-side issue?
It is because AsyncTask management changed in Honeycomb . Previously if you started i.e. 3 AsyncTasks, these were running simultaneously. Since HC, if your targetSdk is set to 12 or higher, these are queued and executed one by one (see this discussion). To work that around start your AsyncTasks that way:
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
instead of:
task.execute(params);
If you target also older Androids, you need conditional code:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
} else {
task.execute(params);
}
or wrap it in a separate helper class:
public class Utils {
public static <P, T extends AsyncTask<P, ?, ?>> void executeAsyncTask(T task) {
executeAsyncTask(task, (P[]) null);
}
#SuppressLint("NewApi")
public static <P, T extends AsyncTask<P, ?, ?>> void executeAsyncTask(T task, P... params) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
} else {
task.execute(params);
}
}
}
and usage would be i.e.:
Utils.executeAsyncTask( new MyAsyncTask() );
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution. Source
So depending on the version AsyncTask would not execute in parallel. For tasks like file download you should use thread pool using Executor
or you can use executeOnExecutor method..
It seems that you share an instance of HttpClient across your application and give your AsyncTasks their own methods. By the by, I'm fully aware that the link is for the older version, but the document doesn't seem to be updated for 4.x.
In one of our methods, we use smoothScrolling in a list view. As this method is not available before API Level 8 (FROYO), we used the TargetApi annotation to prevent the method from being called in previous SDK versions.
As you can see, we do use TargetApi annotation both in class definition and in statements that use the objects of the class. This is more than needed.
Our problem is that the TargetApi annotation is not taken into account and make our emulator crash in version ECLAIR (SDK 7). By tracing, we just realize that the code that should only be executed in versions 8+ is also executed in version 7.
Are we missing something?
This code is in a listener :
#TargetApi(8)
private final class MyOnMenuExpandListener implements OnMenuExpandListener {
#Override
public void onMenuExpanded( int position ) {
doScrollIfNeeded( position );
}
#Override
public void onMenuCollapsed( int position ) {
doScrollIfNeeded( position );
}
protected void doScrollIfNeeded( int position ) {
if ( mListViewDocuments.getLastVisiblePosition() - 2 < position ) {
mListViewDocuments.smoothScrollToPosition( position + 1 );
}
}
}
And the listener is registered this way :
#TargetApi(8)
private void allowSmothScrollIfSupported() {
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ) {
//This if should not be necessary with annotation but it is not taken into account by emulator
Log.d( LOG_TAG, "Smooth scroll support installed." );
folderContentAdapter.setOnMenuExpandListener( new MyOnMenuExpandListener() );
}
}
BTW, we run the code in debug mode, so the issue is not related to obfuscation removing annotations.
#TargetApi does not prevent any code from being run, it is merely for annotating code and preventing compiler errors for new APIs once you know you are only conditionally calling them.
You still need to add something along the lines of
if (Build.VERSION.SDK_INT > 7){
//...
}
With almost one year of more thinking about this, I would like to add a tiny complement to #Guykun 's answer :
The #TargetApi will be only be used by tools to say developers "Hey, don't use this method below XXX android SDK". Typically lint.
So, if you design a method like :
if (Build.VERSION.SDK_INT > 7){
//...
}
then you should add #TargetApi( 7 ) to your method's signature.
BUT, if you add an else statement, and provide an alternative that makes it work for all versions of Android like :
if (Build.VERSION.SDK_INT > 7){
//...
} else {
//...
}
then you should not add #TargetApi( 7 ) to your method's signature. Otherwise, other developers will think they can't use your method belw api level 7, but indeed, it would work for them as well.
So this annotation has to be used, for static analysis, to indicate the minimum api level supported by the method. As in :
#TargetApi( 7 )
public void foo() {
if (Build.VERSION.SDK_INT > 7){
//...
else if (Build.VERSION.SDK_INT > 10){
//...
}
}
and even better, use constants defined in android.Build.VERSION_CODES.*.
BTW, you would have noticed that this is useless for private methods indeed, except to get a cleaner code and help to promote the method public in the future.
To enforce lint error when using a method targeted towards higher Api Level, you can use RequiresApi instead of TargetApi and whenever you'll try to use the method without checking the version code, you'll get compilation error.
This is what the documentation says about RequiresApi
This is similar in purpose to the older #TargetApi annotation, but
more clearly expresses that this is a requirement on the caller,
rather than being used to "suppress" warnings within the method that
exceed the minSdkVersion.
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + Build.VERSION_CODES.ICE_CREAM_SANDWICH);
I write code like this, I used the sdk4.0 to compile this android program, so it didn't cause compile error. When I run this program in my phone that running android 2.3.4, it run well.
Why? I am confused that version 2.3.4 (api level 10) has Build.VERSION_CODES.ICE_CREAM_SANDWICH property? And when I used sdk2.3.4 will cause compile error.
More
I test some code like these below,
private ScaleGestureDetector mScaleGestureDetector;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
mScaleGestureDetector = new ScaleGestureDetector(this, new MyOnScaleGestureListener());
}
this code will run well on android 1.6 api level 4, but
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
Log.d(TAG, "getx(0): " + event.getX(0));
}
this program run failed on android 1.6 api level 4.
They both run on android 2.3.4 well.
why? (In ScaleGestureDetector class use the event.getX(0) (since api level 5) too)
I test some code more..
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Fragment f = new Fragment();
}
When I run it on android 1.6 emulator it throw java.lang.VerifyError, but on my phone running android 2.3.4 it throws java.lang.NoClassDefFoundError.
Why??
It's not as strange as it seems. It has to do with how Java treat constant values of primitives. During compilation, the value of the constant is put in the byte code, not a reference to the actual constant.
For example:
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + Build.VERSION_CODES.ICE_CREAM_SANDWICH);
will actucally be translated by the compiler to:
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + 14);
so the reference to the actual constant (and class) is removed.
Regarding the code that doesn't run for you, it has to do with that the MotionEvent.getX(int n) method wasn't available until api level 5. Before that, multitouch wasn't supported and thus no need for any other method than getX().
It doesn't matter if you actually call the method that doesn't exist. The error is appearing while the class is being loaded and verified by the platform. You most likely get a VerifyError in the log, since it will discover that you're trying to call a non-existent method during the verification.
On the other hand, if you try to use a class that doesn't exist, you will get a ClassNotFoundException instead. Note that sometimes, a class exists in Android even if the documentation doesn't say so. Some classes existed in early versions of Android but weren't exposed until later. Some have even gone the other way.
So:
Trying to use a class that doesn't exist - ClassNotFoundException
Trying to use a method that doesn't exist on a class that exists - VerifyError
(When it comes to using Fragments, they are available for earlier versions with the standalone Android Support Library)
isInitialStickyBroadcast() is obviously only available after 2.0 (SDK 5).
I'm getting this error:
"Uncaught handler: thread main exiting due to uncaught exception
java.lang.VerifyError"
It's only happening on 1.6. Android 2.0 and up doesn't have any problems, but that's the main point of all.
I can't catch the Error/Exception (java.lang.VerifyError), and I know it's being caused by calling isInitialStickyBroadcast() which is not available in SDK 4, that's why it's wrapped in the SDK check.
I just need this BroadcastReceiver to work on 2.0+ and not break in 1.6, it's an app in the market, the UNDOCK feature is needed for users on 2.0+ but obviously not in 1.6 but there is a fairly substantial number of users still on 1.6.
Here's an easy-to-read version of part of the code I'm using. Notice that it's wrapped in an SDK check to only run on 2.0+, but the VerifyError is still showing up.
private BroadcastReceiver mUndockedReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
//FROM ECLAIR FORWARD, BEFORE DONUT THIS INTENT WAS NOT IMPLEMENTED
if (Build.VERSION.SDK_INT >= 5)
{
if (!isInitialStickyBroadcast()) {
//Using constant instead of Intent.EXTRA_DOCK_STATE to avoid problems in older SDK versions
int dockState = intent.getExtras().getInt("android.intent.extra.DOCK_STATE", 1);
if (dockState == 0)
{
finish();
}
}
}
}
};
Your problem is that while you would not be executing isInitialStickyBroadcast(), the classloader attempts to resolve all methods when the class is loaded, so your SDK 4 devices fail at that point, since there is no isInitialStickyBroadcast().
You have two main options:
Use reflection.
Create two editions of your BroadcastReceiver, as public classes in their own files. One has the SDK 4 logic, one has the SDK 5+ logic. Register the one you want based on an SDK check at the time you call registerReceiver().