I am working with some Acitivitys , it's working pretty well. But i am having a strange delay on it.
And I figure it out that, it was about this part of the code, where I am loading stored image in the SDCard.
if(p.getAdress() != null){
Bitmap bitmap = BitmapFactory.decodeFile(p.getAdress());
new_image.setBackgroundDrawable(null);
new_image.setImageBitmap(bitmap);
}
Why this simple code is taking too long to execute?
How to solve it?
If I take this code off, everything works as i wished.
You shouldn't load large bitmaps directly on the UI thread.
Actually, the best reference to loading large Bitmaps efficiently can be found here.
And right here you can find good information on how you can load them using an AsyncTask.
This is the method they show you there (you can even download the sample!)
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
public void loadBitmap(int resId, ImageView imageView) {
if (cancelPotentialWork(resId, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable =
new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
Your can try this way. It help you
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
if(p.getAdress() != null){
Bitmap bitmap = BitmapFactory.decodeFile(p.getAdress());
new_image.setBackgroundDrawable(null);
new_image.setImageBitmap(bitmap);
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
Related
I have a slight problem. Need a nudge in the right direction.
I am doing a video editor like Vine and/or instagram. Where they show a timeline with screencaps from the video
It just adds more pictures depending on the videos duration. What i did for my app is that i added a recyclerView. This recyclerview has an adapter that calls the following function every time onBindViewHolder
public Bitmap getFrameFromCurrentVideo(int seconds) {
Bitmap bitmap = null;
if(mMediaMetadataRetriever != null) {
bitmap = mMediaMetadataRetriever.getFrameAtTime(seconds * 1000000, MediaMetadataRetriever.OPTION_CLOSEST);
}
return bitmap;
}
This works and it adds the proper amount of images that i want. But the problem is that it is too heavy on the UI thread. Since the recyclerView is recycling everything. It then lags up every time it has to get a frame.
So i thought that i have to do some async task and then cache the images. But what i read is that AsyncTask is not recommended for recycler views since it recycles.
So what should i do to enchance the performance? Any good idea?
This is what i did to solve my problem.
I created async task and memory cache my result.
My adapter checks if the image already exist. If it does. Then we skip doing the background work. Otherwise i do the async task and try to load the image. We also tag the view just in case the user scrolls while the task is not finished.
This helps us check if the Tag is different from what the task have. If it is the same. Then we can safely put the right image in the imageview.
Snippet from my adapter
#Override
public void onBindViewHolder(PostVideoRecyclerViewHolder holder, int position) {
holder.mImageView.getLayoutParams().width = mScreenWidth / mMaxItemsOnScreen;
holder.mImageView.setImageDrawable(null);
int second = position * 3 - 3;
String TAG = String.valueOf(second);
holder.mImageView.setTag(TAG);
Bitmap bitmap = mFragment.getBitmapFromMemCache(TAG);
if(bitmap == null) {
PostVideoBitmapWorkerTask task = new PostVideoBitmapWorkerTask(holder.mImageView, TAG, mFragment);
task.execute(second);
}
else {
holder.mImageView.setImageBitmap(bitmap);
}
}
My AsyncTask class
public class PostVideoBitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private ImageView mImageView;
private String TAG;
private PostVideoFeedFragment mFragment;
public PostVideoBitmapWorkerTask(ImageView imageView, String TAG, PostVideoFeedFragment fragment) {
mImageView = imageView;
this.TAG = TAG;
mFragment = fragment;
}
#Override
protected Bitmap doInBackground(Integer... params) {
Bitmap bitmap = mFragment.getFrameFromCurrentVideo(params[0]);
mFragment.addBitmapToCache(TAG,bitmap);
return bitmap;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if(mImageView.getTag().toString().equals(TAG)) {
mImageView.setImageBitmap(bitmap);
}
}
}
Snippet from my fragment class
public void addBitmapToCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
I can recommend https://developer.android.com/training/displaying-bitmaps/cache-bitmap.html if you want to read up on caching
And also https://developer.android.com/reference/android/os/AsyncTask.html for reading up on asyncTask's
I am using asynckTask to decode a file and onPostExecute I setImageBitmap and call progressDialog.dismiss() but after the progressDialog is dismissed the imageView take a few seconds to show the image. What I want is the progressDialog to disappear only when the image view is ready for the user. My code is below:
class MyTask extends AsyncTask<String, Integer, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private String mainImageString;
private Context context;
ProgressDialog progressDialog;
public MyTask(ImageView imageView, Context mContext) {
imageViewReference = new WeakReference<ImageView>(imageView);
context = mContext;
}
#Override
protected void onPreExecute() {
progressDialog= ProgressDialog.show(MyClass.this, "Progress Dialog Title Text","Process Description Text", true);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(String... params) {
mainImageString = params[0];
return BitmapFactory.decodeFile(mainImageString);
}
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
progressDialog.dismiss();
}
}
}
}
When you are setting your Bitmap basically you are "building" it on the UI thread. What I recommend is to call setImageBitmap in your doInBackground callback and then dismiss the dialog.
You can do this...
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
handler.postDelayed(new Runnable() {
public void run() {
progressDialog.dismiss();
}
}, 1000); //time for image to appear in image view
}
}
Please note this is not a foolproof way..but this what-easy way-currently I can think of..
Other hack I can think about is to extend ImageView and override onDraw().call super() in onDraw(),once the call to super() returns then dismiss the dialog box.This is more accurate way of doing it.
something like this:
onDraw(){
super();
onDismissDialogBox()//send an even to activity to dismiss dialog box
}
I have this little piece of code and I want to achieve this: program should set a wallpaper from linked image.
ImgDownload:
public class ImgDownload extends AsyncTask {
private String requestUrl;
private ImageView view;
private Bitmap pic;
private ImgDownload(String requestUrl, ImageView view) {
this.requestUrl = requestUrl;
this.view = view;
}
#Override
protected Object doInBackground(Object... objects) {
try {
URL url = new URL(requestUrl);
URLConnection conn = url.openConnection();
pic = BitmapFactory.decodeStream(conn.getInputStream());
} catch (Exception ex) {
}
return null;
}
#Override
protected void onPostExecute(Object o) {
view.setImageBitmap(pic);
}
}
main
public class MainActivity extends Activity {
private ImageView img;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
img= (ImageView)findViewById(R.id.img);
//!!!! This is where I am stuck :)
Object s = new ImgDownload("http://images1.wikia.nocookie.net/__cb20120402213849/masseffect/images/4/42/Uncharted_Worlds_Codex_Image.jpg",img );
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
How to instantiate/create this class in my mainActivity, so it could download img from link? Any help suggestions, thoughts, will be appreciated :)
You execute this AsyncTask like this:
ImgDownload downloader = new ImgDownload("http://images1.wikia.nocookie.net/__cb20120402213849/masseffect/images/4/42/Uncharted_Worlds_Codex_Image.jpg",img);
downloader.execute();
But I would not recommend using your code as it will produce memory leaks. For example try to rotate your device while it is downloading an image. I guarantee you your application will crash. Plus AsyncTask is a generic class. You could use that to make your code a little simpler. Here is my improved image download task:
public class ImgDownload extends AsyncTask<Void, Void, Bitmap> { // Use Generics
private final String requestUrl;
private final WeakReference<ImageView> imageViewReference; // Use WeakReference to prevent memory leaks
public ImgDownload(String requestUrl, ImageView view) {
this.requestUrl = requestUrl;
this.imageViewReference = new WeakReference<ImageView>(view);
}
#Override
protected Bitmap doInBackground(Void... objects) {
try {
URL url = new URL(requestUrl);
URLConnection conn = url.openConnection();
return BitmapFactory.decodeStream(conn.getInputStream()); // Return bitmap instead of using global variable
} catch (Exception ex) {
}
return null;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
ImageView imageView = imageViewReference.get();
if(imageView != null && bitmap != null) { // Check if image or ImageView are null
imageView.setImageBitmap(bitmap);
}
}
}
new ImgDownload("http://images1.wikia.nocookie.net/__cb20120402213849/masseffect/images/4/42/Uncharted_Worlds_Codex_Image.jpg", MainActivity.img).execute();
I've been having problems with this for quite some time now. I'm getting there little by little but I don't have much time to spend programming :(
So I'm having to load image from URLs for showing on a list view, and i'm almost there. They are lazy loading and the cache system i'm using works good.
The problem is that the downloaded images are in the wrong place when I start scrolling and I can't figure out where i'm going wrong.
The code is inspired from these two links:
This one for the layout idea.
http://blog.blundell-apps.com/imageview-with-loading-spinner/
and this one for the cache system.
http://android-developers.blogspot.fr/2010/07/multithreading-for-performance.html
So here's my code:
public class LoaderImageView extends LinearLayout
{
private static final String TAG = "LoderImageView";
private Context mContext;
private ImageView mImage;
private ProgressBar mSpinner;
/* The HashMap that contains the references to the different
* downloads currently running.
*/
public static HashMap<LoaderImageView, BitmapDownloaderTask> tasks =
new LinkedHashMap<LoaderImageView, BitmapDownloaderTask>();
public LoaderImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
mContext = context;
mImage = new ImageView(mContext);
mImage.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
mSpinner = new ProgressBar(mContext);
mSpinner.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
mSpinner.setIndeterminate(true);
addView(mSpinner);
addView(mImage);
Log.w(TAG, "Loading an imageView");
}
public void downloadImage(String url)
{
resetPurgeTimer();
//Log.w(TAG, "Loading: " + url);
if(url.equals(""))
{
mImage.setImageDrawable(mContext.getResources().getDrawable(R.drawable.male));
}
else
{
Bitmap bitmap = getBitmapFromCache(url);
if(bitmap == null)
{
/* The bitmap is not in the cache. */
cancelPotentialDownload(this);
/* Start the new download. */
BitmapDownloaderTask bdt = new BitmapDownloaderTask(this, url);
bdt.execute();
}
else
{
/*The bitmap is in the cache. */
mImage.setImageBitmap(bitmap);
mSpinner.setVisibility(View.GONE);
}
}
}
class BitmapDownloaderTask extends AsyncTask<Void, Void, Bitmap>
{
private String mUrl;
private LoaderImageView mLiv;
public BitmapDownloaderTask(LoaderImageView liv, String url)
{
mLiv = liv;
mUrl = url;
}
#Override
protected void onPreExecute()
{
LoaderImageView.tasks.put(mLiv, this);
mSpinner.setVisibility(View.VISIBLE);
mImage.setVisibility(View.GONE);
Log.w(TAG, "Starting an AsyncTask");
}
#Override
protected Bitmap doInBackground(Void... voids)
{
URL url = null;
try
{
url = new URL(mUrl);
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
return BitmapTools.fetchBitmap(url);
}
#Override
protected void onPostExecute(Bitmap bitmap)
{
BitmapDownloaderTask b = tasks.get(mLiv);
if(b == this)
{
LoaderImageView.tasks.remove(this);
mImage.setImageBitmap(bitmap);
}
if (isCancelled())
{
bitmap = null;
}
addBitmapToCache(mUrl, bitmap);
tasks.remove(mLiv);
mSpinner.setVisibility(View.GONE);
mImage.setVisibility(View.VISIBLE);
}
}
/* More methods related too the cache... */
Here the xml layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#drawable/list_item_selector" >
<com.myproject.liste.LoaderImageView
android:id="#+id/visites_image"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true" />
</RelativeLayout>
I'm creating the list with a BaseAdapter and ListActivity.
Also i'm loading the list by pages of data: I load 10 items, when the user scrolls down I load 10 more, and call notifyDataSetChange();
i have this class:
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageFromWeb ifw;
private String url;
private final WeakReference<ImageView> imageViewReference;
public DownloadImageTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground(String... params) {
url = params[0];
try {
return BitmapFactory.decodeStream(new URL(url).openConnection().getInputStream());
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(Bitmap result) {
if (isCancelled()) {
result = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(result);
}
}
}
#Override
protected void onPreExecute() {
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
---------> imageView.setImageResource(R.drawable.pw);
}
}
}
}
and the main activity:
public class ImageFromWeb extends Activity {
private String path = "http://....";
private ImageView imageView;
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.main);
ImageView mChart = (ImageView) findViewById(R.id.imview);
mChart.setTag(path);
new DownloadImageTask(mChart).execute(path);
}
}
I want to put in the point of arrow(in DownloadImageTask class) an alert dialog! How can i do this? Because this class isn't an activity.
thanks :)
change the constructor and pass a Context object
Context mContext;
public DownloadImageTask(ImageView imageView,Context mContext) {
imageViewReference = new WeakReference<ImageView>(imageView);
this.mContext = mContext;
}
Now you can use this Context to create dialogs
You can even cast mContext to your Activity class and call functions within your Activity
Move the Async Task to your activity and use that to call your DownloadImageTask class & methods. This will make your life a lot easier.
pass a Activity instance to the class where you want to display dialog, and check
if(!actvity.isFinishing){
//show dialog
}
You can have a static Context in your Application like this:
public static Context CurrentContext;
and a custom abstract Activity that sets currentContext upon creation like this:
public abstract class CustomActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApplication.CurrentContext = this;
}
}
Then you would get context like this:
AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(MyApplication.CurrentContext);
dlgBuilder.setTitle("Context Example");
dlgBuilder.setMessage("I am being shown from the application Static context!");
dlgBuilder.setNeutralButton("Ok", null);
dlgBuilder.show();
This way you never have to worry about context wether you are in a background task or directly in an Activity it will work for most cases.
hope this helps!