I'm seeking for solutions to load 20 items from SQLite faster than 5 seconds (which is the seconds that I'm loading right now.) - First of all, I'm using a custom Listview Adapter.
I load 5 items in 1 second. I tried to load 20 items and I load them in 5 seconds.
This are the fields I retrieve from database: int, String, String, int, Bytes, float, int.
As you may think, after getting the bytes I convert them into Bitmap.
Bitmap image = convertBlobToImage(cursor.getBlob(4));
// Using this function:
public Bitmap convertBlobToImage(byte[] value){
byte[] new_value = Base64.decode(value, 0);
return BitmapFactory.decodeByteArray(new_value, 0, new_value.length);
}
So, in my class fields, they are going to get not the bytes but already the bitmap.
One of the reasons of the amount of time to read, is probably the bitmap. I just did a test on Paint. I saved two equal images one in BMP and another in JPG. The JPG image have the size of 2,87KB and the BMP 420KB!!
With the code above, is that result I'm getting? And probably one of the solutions could be: http://www.mkyong.com/java/how-to-convert-byte-to-bufferedimage-in-java/ ?
What do you guys think? Thanks.
Edit:
I was searching and I found about onDestroy(). Also I didn't have the "runOnUiThread" implemented, and I put that way. But I think it didn't give me any better result. What do you think? Could this increase the performance?
#Override
protected void onDestroy() {
super.onDestroy();
listView.setAdapter(null);
}
// And I also tried to put runOnUiThread:
runOnUiThread(new Runnable() {
#Override
public void run() {
Bundle extras = getIntent().getExtras();
if (extras != null) {
DatabaseHandler db = new DatabaseHandler(Produtos.this);
display_products = db.get_all_oc_product(extras.getString("category_id"));
listView = (ListView) findViewById(R.id.product_listview);
inputSearch = (EditText) findViewById(R.id.product_inputSearch);
adapter = new itemAdapter(Produtos.this,R.layout.row, display_products);
listView.setAdapter(adapter);
}
}
});
Edit (2): I managed to decrease the time for 3 seconds on displaying 20 items. I accomplish this by closing all the connections to database after the queries. I was not doing this properly.
Correct way:
cursor db.query(...)
try{
// Code
} finally {
cursor.close();
db.close();
}
Edit (3): Further than the solution on Edit (2), one of the issues I had, which I was identified with, was the problem of the images.
So, I started to look at them and I saw images the 2000x1800 and 300kb and even more, and I found rapidly that was here the problem.
So, in the PHP webservice I developed a function to resize the images to half and converting them to jpg.
function resize($filePath){
list($width, $height) = getimagesize($filePath);
$percent = 0.5;
$newwidth = $width * $percent;
$newheight = $height * $percent;
$thumb = imagecreatetruecolor($newwidth, $newheight);
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
$source = null;
if($ext == "png"){
$source = imagecreatefrompng($filePath);
}else if($ext == "jpg" || $ext == "jpeg"){
$source = imagecreatefromjpeg($filePath);
}
// Resize
imagecopyresized($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
// Output
$temporary = "C:\\xampp\\htdocs\\MyExample\\images\\temporary\\" . basename($filePath);
imagejpeg($thumb, $temporary . ".jpg", 50);
ImageDestroy($thumb);
return $temporary . ".jpg";
}
With this solution, I decreased the time for a stunning 1 second loading 47 items!!
I'd recommend checking into one of the java.nio.* packages to load your images. NIO is non-blocking which means you can load images asynchronously, resulting in more work done per time.
NIO also used native buffers on the system (outside the jvm) so it can prevent java from having to read things into it's heap, which again, makes things faster (native). There's a lot more benefits from using NIO here as well.
Well It's a bit hard to help from the other side of this question, but some generic tips:
Use profiling of some sort to determine which part is slowing you down. Is it loading the actual cursor back that's a problem? or is it the byte conversion? It's hard to say unless you measure - otherwise you could waste a lot of time optimizing something that isn't slowing you down. The simplest profiling example:
long startTime = SystemClock.elapsedRealTime();
suspectedSlowFunctionCall();
long endTime = SystemClock.elapsedRealTime();
Log.d(TAG, "Method duration: " + (endTime - startTime));
As Mr. Murphy mentioned in the comments, there are also tools that help you with this task. The android developers site has a great post showing how to use traceview, which is included in the SDK.
General wisdom is to not store Images in the database if you can avoid it. It is a lot easier on you to debug and work with (and for the system to load) if you store a file path instead and save the file.
Android's BitmapFactory should be able to handle JPEGs. If you want to use JPG, feel free
If something is unfixably slow, don't do it on the UI Thread! use something like an AsyncTask to do the slow operation, and show your results to the user one by one as they get loaded. This is always preferable to an operation that stalls whilst the user waits confused.
Related
So I'm using Glide to load video thumbnails, but it takes a while to load a large number of videos to view, whats the best/fastest way to load a list of videos in users phone with each respective video thumbnail, in Recycler view.
Populating the list
public static ArrayList<String> getAllMedia(Context context) {
HashSet<String> videoItemHashSet = new HashSet<>();
String[] projection = { MediaStore.Video.VideoColumns.DATA ,MediaStore.Video.Media.DISPLAY_NAME};
Cursor cursor = context.getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);
try {
cursor.moveToFirst();
do{
videoItemHashSet.add((cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA))));
}while(cursor.moveToNext());
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
ArrayList<String> downloadedList = new ArrayList<>(videoItemHashSet);
return downloadedList;
}
The Glide Method
public static void displayImageOriginal(Context ctx, ImageView img, String url) {
try {
Glide.with(ctx).load(url)
.transition(DrawableTransitionOptions.withCrossFade())
.apply(RequestOptions.centerCropTransform().skipMemoryCache(false).diskCacheStrategy(DiskCacheStrategy.ALL))
.into(img);
} catch (Exception e) {
}
}
Here's the adapter view Binder
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
final YVideos obj = items.get(position);
if (holder instanceof OriginalViewHolder) {
OriginalViewHolder view = (OriginalViewHolder) holder;
view.video_title.setText(obj.title);
view.duration_size.setText(obj.getDurSize());
Tools.displayImageOriginal(ctx, view.video_thumbnail, obj.name);
} else {
SectionViewHolder view = (SectionViewHolder) holder;
view.title_section.setText(obj.title);
}
}
This all looks like standard RecyclerView usage, so provided your adapter is assigned to the RecyclerView in isolation, i.e. without definining any properties that limit performance, such as setItemViewCacheSize(), setMaxRecycledViews() and the likes, your issue is unlikely to be a client-side one.
Are you relying on Glide to generate the thumbnails for you, at runtime? If so, you'll be really putting the library through its paces with a lot of processing, since it has to work through the original quality, original resolution video on the fly. Would it not be possible to generate the thumbnails beforehand? These will load much faster and unburden your app from a lot of computation. If the thumbnails are going to stay the same forever, then why not compute them once and let every user benefit, rather than have every user compute the same result every time you wish to render?
It's likely to me that your issue comes from the fact that you're expecting to fetch the images and render them to the user whilst they're already looking at the content. This means that you load up the screen, and onBindViewHolder() is called, and you make an attempt to fetch the images from a network; but at this stage, the user is already looking at the list and ready to browse. In a way, you're a little too late, and your List will only be as fast as your network connection/image server.
What you can do is prepare the image cache before you ever try to draw the RecyclerView. You know before you reach that screen what range of images you wish to render; it would be possible to load these into Glide and define an appropriate diskStrategy before you're even ready to draw. This way, you can initialize the List of content the moment the user is looking. The predictive image caching strategy wouldn't end here; you know what rows of images you're ready to load next; so you can fetch those in the background too. The drawback with this approach is that it has the potential to waste a lot of bandwidth fetching images your user may never even scroll down to; therefore you may need to experiment with request rate limits.
There are also sneakier ways to go about solving this problem. Even the most well funded, well researched applications depend on simple tricks to work around the bottleneck of network performance... Have you considered using a placeholder animation to keep your app looking active? You'll be surprised how well these work!
I am currently attempting to "fix" an Android application intended to control a device over BLE. The application was written entirely by someone else, and I currently know very little Java. So if there are glaring flaws please just point them out and I'll fix them. I won't be able to give the rationale very everything you see here, but I'll do my best. While there are a myriad of problems and things to fix, my current issue is the data plotter. GraphView was implemented to create the plots. The issue is that the data plotted is not representative of the actual sampling rate. When using a separate BLE data logger I can verify that the data is being sent at the correct rate and it plots out correctly using excel.
These are created on startup.
graph1 = (GraphView) findViewById(R.id.graph1);
airPressureSeries = new LineGraphSeries<>();
fluidPressureSeries = new LineGraphSeries<>();
airPressureSeries.setDrawAsPath(true);
fluidPressureSeries.setDrawAsPath(true);
graph1.getViewport().setXAxisBoundsManual(true);
graph1.getViewport().setMinX(0);
graph1Num = 100;
graph1.getViewport().setMaxX(graph1Num);
graph2 = (GraphView) findViewById(R.id.graph2);
airTemperatureSeries = new LineGraphSeries<>();
fluidTemperatureSeries = new LineGraphSeries<>();
airTemperatureSeries.setDrawDataPoints(true);
fluidTemperatureSeries.setDrawDataPoints(true);
graph2.getViewport().setXAxisBoundsManual(true);
graph2.getViewport().setMinX(0);
graph2Num = graph1Num;
graph2.getViewport().setMaxX(graph2Num);
Then this is done when data plotting is to be shown.
plot1Dropdown.setText("Air Pressure");
graph1.addSeries(airPressureSeries);
plot1Title.setText(R.string.pressureTitle);
plot2Dropdown.setText("Fluid Pressure");
plot2Title.setText(R.string.pressureTitle);
graph2.addSeries(fluidPressureSeries);
This timer, which I don't quite understand and could be wrong, is started. They said it should run at 100Hz. The code inside it should just increment an x value for the plot and then grab a global variable which is updated elsewhere.
mTimer1 = new Runnable() {
#Override
public void run() {
graph1LastXValue += 1d;
airPressureSeries.appendData(new DataPoint(graph1LastXValue,
airPressureValue), true, graph1Num);
fluidPressureSeries.appendData(new DataPoint(graph1LastXValue,
fluidPressureValue), true, graph1Num);
graph2LastXValue += 1d;
airTemperatureSeries.appendData(new DataPoint(graph2LastXValue,
airTemperatureValue), true, graph2Num);
fluidTemperatureSeries.appendData(new DataPoint(graph2LastXValue
, fluidTemperatureValue), true, graph2Num);
mHandler.postDelayed(this, 5);
}
};
mHandler.postDelayed(mTimer1, 5);
The global variable is updated after new data has been received and parsed by an AsyncTask. In the doInBackground of the AsyncTask it assigns the parsed values to the global variables like this.
DeviceControlActivity.airPressureValue = airPressure;
DeviceControlActivity.airTemperatureValue = airTemperature;
DeviceControlActivity.fluidPressureValue = fluidPressure;
DeviceControlActivity.fluidTemperatureValue = fluidTemperature;
I have done a few things to check what's going on. I am certain that the timer is running fast enough to update the values. I am also certain that data is being received at the correct rate. I used logging to make sure of that. I also am sure that the async task in running and completing in a reasonable amount of time. In the post execute for it I told it to log the amount of time passed between pre and post execute function calls. It was consistently 1 ms which is fine for the application.
I think the issue has to do with how the values are set or retrieved. Based on the way the code inside the timer works it plots a new point even if the data has not been updated so I get regions of the plot that are all the same value for some amount of time greater than is reasonable.
Can someone point me in the right direction?
in the google's own volley image cache tutorial
// Returns a cache size equal to approximately three screens worth of images.
public static int getCacheSize(Context ctx) {
final DisplayMetrics displayMetrics = ctx.getResources().
getDisplayMetrics();
final int screenWidth = displayMetrics.widthPixels;
final int screenHeight = displayMetrics.heightPixels;
// 4 bytes per pixel
final int screenBytes = screenWidth * screenHeight * 4;
return screenBytes * 3;
}
the recommended cache is three screens worth of images which equals to 7mb. I have an social media app and there is a newsfeed inside it.
1-) My first question is what will happen after the cache is full?
2-) I am thinking about removing cache every one hour and thus the cache will include the newer content. Is that reasonable ? What is the image caching logic behind the apps which includes something like newsfeed(for example, instagram)?
3-) How can i remove the old cache of specific item and force it to download it again? I tried this solution but it did not work:
VolleySingleton.getInstance().getRequestQueue().getCache().remove(IMAGE_URL);
mNetworkImageView = (NetworkImageView) getView().findViewById(R.id.networkImageView);
mImageLoader = VolleySingleton.getInstance().getImageLoader();
mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);
There are a lots of clone question of my third question but none of them has been answered.
Thanks for your helps. :)
1.) There are 2 layers of cache in Volley, one is the in-memory cache (in RAM) and the other one is a disk cache. Once a cache is full, the oldest image (meaning the image that hasn't been accessed the longest) in that cache will be evicted when a new image is about to be cached to make room for the new items. When something is evicted from the in-memory cache, it is still present in the disk cache and can be loaded very quickly from disk if it is needed again. If an image is evicted from the disk cache, it would have to be redownloaded if it's needed again.
2.) This doesn't sound reasonable once you understood the answer to question 1. The cache automatically makes room for newer content and there is no reason to evict content manually. Manual eviction will in fact lower your cache's efficiency.
3.) Broadly speaking, this is not possible (without hacks), because it should not be needed. If an image resource (almost) always expires after a certain time, the server should announce this using HTTP headers when sending the resource to the client. For example using the max-age property of the cache-control header. There are lots of websites explaining this in detail, for example: http://www.mobify.com/blog/beginners-guide-to-http-cache-headers/. If an image resource almost never expires, you can consider changing its filename upon change and store that filename as a property. For example a user can have an avatar property containing the URL to the avatar. The avatar image can be cached indefinitely and you change the URL of the image if a new avatar gets uploaded.
For you 3rd question, I suggest that you read the following Google's documentation:
Request an Image
ImageRequest—a canned request for getting an image at a given URL and
calling back with a decoded bitmap. It also provides convenience
features like specifying a size to resize to. Its main benefit is that
Volley's thread scheduling ensures that expensive image operations
(decoding, resizing) automatically happen on a worker thread.
So if you use a ImageRequest only, you can refer to my answer at the following question:
Tell Volley not to use cached data but to initiate a new request?
Also in the Google's documentation:
ImageLoader—a helper class that handles loading and caching images
from remote URLs. ImageLoader is a an orchestrator for large numbers
of ImageRequests, for example when putting multiple thumbnails in a
ListView. ImageLoader provides an in-memory cache to sit in front of
the normal Volley cache,...
If you use NetworkImageView, you can refer to my answer at the following question:
Disable or Remove Cache in NetworkImageView- Volley
In which you will find that I use the following code inside VolleySingleton class:
mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
#Override
public Bitmap getBitmap(String url) {
return null;
}
#Override
public void putBitmap(String url, Bitmap bitmap) {
}
});
Hope it helps!
Sorry if I missed something obvious, I tried really hard but didn't find any clue to answer my question.
So, my task is to download several images using their URLs and store them in some folder. I tried different approaches but for several reasons I decided I should use AQuery for this.
The download is actually done in IntentService which is started from the activity or from AlarmManager. The part where I download images is a for loop for downloading the required amount of images from the generated URLs:
for (int i=0; i < required; i++) {
url = makeURL(i);
aq.download(url, imgDir, new AjaxCallback() {
#Override
public void callback(String url, Object object, AjaxStatus status) {
super.callback(url, object, status);
File file = (File) object;
if (file.length() < 300 * 1024) { //just an example test
file.delete();
}
}
});
AQuery starts a separate thread for each download so the code continues to execute before I can get any result. The problem is that images can be broken or do not fit for other reasons so I do the test after each download in a callback method. If the image does not pass one of the tests I delete the file and need to redownload the image from another URL. But since all downloads are done in an independent threads I can't catch the result of the downloads in the same IntentService where I have all the "spare" URLs.
Since my IntentService is a background thread itself I think I can do all the downloads in that thread, check the file after each download and try another URL if it's bad.
As I understand, I have two options here:
find a way to make aq.download use the same thread
don't use AQuery and take another approach for downloading
I would be very grateful for an advice. My experience is still very limited to make the correct decision. Thanks in advance!
i am trying to simulate a live view using a canon Camera.
I am interacting with the cam using the CanonSDK, i get an image every a short period in order to simulate a video frame by frame. This works fine, i am using java to do the backend and send the images trough BlazeDS to flex.
The problem is not getting the image, the problem is that when i load a new image using something like:
image.source=my_new_image;
the new image is loaded but it produces a short white blink and it ruins the video...
So i would like to know if the is a way to update an image on flex avoiding the blinking problem, or if i could make a video streaming from java and pick it up with flex...
Thanks in advance!!!
The easy way is to use a technique called double buffering, using two Loaders - one for the image which is visible, and one for the image which is being loaded and is invisible. When the image has completed loading it becomes visible, and the other one becomes invisible and the process repeats.
In terms of efficiency, it would be better to at least use a socket connection to the server for transferring the image bytes, preferably in AMF format since it has little overhead. This is all fairly possible in BlazeDS with some scripting.
For better efficiency you may try using a real-time frame or video encoder on the server, however decoding the video on the client will be challenging. For best performance it will be better to use the built-in video decoder and a streaming server such as Flash Media Server.
UPDATE (example script):
This example loads images over HTTP. A more efficient approach would be to use an AMF socket (mentioned above) to transfer the image, then use Loader.loadBytes() to display it.
private var loaderA:Loader;
private var loaderB:Loader;
private var foregroundLoader:Loader;
private var backgroundLoader:Loader;
public function Main()
{
loaderA = new Loader();
loaderB = new Loader();
foregroundLoader = loaderA;
backgroundLoader = loaderB;
loadNext();
}
private function loadNext():void
{
trace("loading");
backgroundLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler);
backgroundLoader.load(new URLRequest("http://www.phpjunkyard.com/randim/randim.php?type=1"));
}
private function loaderCompleteHandler(event:Event):void
{
trace("loaded");
var loaderInfo:LoaderInfo = event.target as LoaderInfo;
var loader:Loader = loaderInfo.loader;
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loaderCompleteHandler);
if (contains(foregroundLoader))
removeChild(foregroundLoader);
var temp:Loader = foregroundLoader;
foregroundLoader = backgroundLoader;
backgroundLoader = temp;
addChild(foregroundLoader);
loadNext();
}