How can I do this using Glide? I want to cache image to use it another time also. Thanks in advance.
You can load an image in a RelativeLayout like this. I'm just showing you the hard part which is setting an image to the background.
For Glide version before 4.X
Glide.with(this).load(imageViewPath).asBitmap().into(new SimpleTarget<Bitmap>(relLayoutWidth, relLayoutHeight) {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
Drawable drawable = new BitmapDrawable(context.getResources(), resource);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
yourRelativeLayout.setBackground(drawable);
}
}
});
For caching, refer to this page: Caching and Cache Invalidation.
Update for Glide v4 onward:
GlideApp.with(this).load(R.drawable.backgroundimage).into(new SimpleTarget<Drawable>() {
#Override
public void onResourceReady(Drawable resource, Transition<? super Drawable> transition) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
yourRelativeLayout.setBackground(resource);
}
}
});
Currently, at version Glide version 4.9.0, you can set a background for Relative Layout as:-
Glide.with(MainActivity.this)
.load(IMAGE_URL)
.into(new CustomTarget<Drawable>() {
#RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
#Override
public void onResourceReady(#NonNull Drawable resource, #Nullable Transition<? super Drawable> transition) {
mLinearLayout.setBackground(resource);
}
#Override
public void onLoadCleared(#Nullable Drawable placeholder) {
}
});
I think the best way to achieve it's works with your own ViewTarget implementation, because this class has specific methods to be handled by Glide automatically in different scenarios.
The abstract implementation for ViewGroup (LinearLayout, RelativeLayout and so on).
public abstract class ViewGroupTarget<Z> extends ViewTarget<ViewGroup, Z> implements GlideAnimation.ViewAdapter {
public ViewGroupTarget(ViewGroup view) {
super(view);
}
/**
* Returns the current {#link android.graphics.drawable.Drawable} being displayed in the view using
* {#link android.widget.ImageView#getDrawable()}.
*/
#Override
public Drawable getCurrentDrawable() {
return view.getBackground();
}
/**
* Sets the given {#link android.graphics.drawable.Drawable} on the view using
* {#link android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* #param drawable {#inheritDoc}
*/
#Override
public void setDrawable(Drawable drawable) {
view.setBackground(drawable);
}
/**
* Sets the given {#link android.graphics.drawable.Drawable} on the view using
* {#link android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* #param placeholder {#inheritDoc}
*/
#Override
public void onLoadStarted(Drawable placeholder) {
view.setBackground(placeholder);
}
/**
* Sets the given {#link android.graphics.drawable.Drawable} on the view using
* {#link android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* #param errorDrawable {#inheritDoc}
*/
#Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
view.setBackground(errorDrawable);
}
/**
* Sets the given {#link android.graphics.drawable.Drawable} on the view using
* {#link android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable)}.
*
* #param placeholder {#inheritDoc}
*/
#Override
public void onLoadCleared(Drawable placeholder) {
view.setBackground(placeholder);
}
#Override
public void onResourceReady(Z resource, GlideAnimation<? super Z> glideAnimation) {
this.setResource(resource);
}
protected abstract void setResource(Z resource);
}
The specific implementation, in this case for LinearLayout.
public class LinearLayoutTarget extends ViewGroupTarget<Bitmap> {
private Context context;
public LinearLayoutTarget(Context context, LinearLayout linearLayout) {
super(linearLayout);
this.context = context;
}
/**
* Sets the {#link android.graphics.Bitmap} on the view using
* {#link android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)}.
*
* #param resource The bitmap to display.
*/
#Override
protected void setResource(Bitmap resource) {
view.setBackground(new BitmapDrawable(context.getResources(), resource));
}
}
To work with.
Glide.with(this.getApplicationContext())
.load(R.drawable.your_image)
.asBitmap()
.into(new LinearLayoutTarget(this.getApplicationContext(), (LinearLayout) yourLinearLayoutInstanceHere));
Or even more simple working without Bitmap.
The specific implementation.
public class LinearLayoutTarget extends ViewGroupTarget<Drawable> {
public LinearLayoutTarget(LinearLayout linearLayout) {
super(linearLayout);
}
/**
* Sets the {#link android.graphics.Bitmap} on the view using
* {#link android.widget.ImageView#setImageBitmap(android.graphics.Bitmap)}.
*
* #param resource The bitmap to display.
*/
#Override
protected void setResource(Drawable resource) {
view.setBackground(resource);
}
}
To work with.
Glide.with(this.getApplicationContext())
.load(R.drawable.your_image)
.into(new LinearLayoutTarget((LinearLayout) yourLinearLayoutInstanceHere));
Here's the code for Kotlin
Glide 4.9.x
Since the SimpleTarget is deprecated now we have to use CustomTarget as below
Glide.with(this).load(UCrop.getOutput(data)).into(object :
CustomTarget<Drawable>() {
override fun onLoadCleared(placeholder: Drawable?) {
TODO("Not yet implemented")
}
override fun onResourceReady(
resource: Drawable,
transition: Transition<in Drawable>?
) {
ib_pet_profile_image.background=resource
}
})
(SimpleTarget class is Deprecated So using CustomTarget class to Load image)
You can load an image in a RelativeLayout like this. I'm just showing you the hard part which is setting an image to the background.
For Glide version before 4.X (Without Deprecation)
Glide.with(this).load(ServiceGenerator.BASE_URL + url).into(new CustomTarget<Drawable>() {
#Override
public void onResourceReady(#NonNull Drawable resource, #Nullable Transition<? super Drawable> transition) {
yourRelativeLayout.setBackground(resource);
}
#Override
public void onLoadCleared(#Nullable Drawable placeholder) {
}
});
Glide.with(ctx).asBitmap().load(url).centerCrop().into(object: CustomTarget<Bitmap>(){
override fun onLoadCleared(placeholder: Drawable?) {
}
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
val wRatio = view.width/resource.width.toFloat()
val hRatio = view.height / resource.height.toFloat()
val minRatio = min(wRatio,hRatio)
val destBitmap = Bitmap.createScaledBitmap( resource,(resource.width*minRatio).toInt(), (resource.height*minRatio).toInt(),false)
view.background = BitmapDrawable(resources,destBitmap)
}
})
This works for me
PS: There would be an error when the bitmap is too large
java.lang.RuntimeException: Canvas: trying to draw too large bitmap.
So you'd better scale that bitmap like what i did in above code
Thanks all. All of your answers helped me out. I also find useful solutions with Glide's official documentation. I have switched over to Glide App Module in order to have common usage of it.
https://medium.com/#nuhkocaa/manage-all-your-glides-in-a-single-class-with-glidemodule-on-android-4856ee4983a1
I needed to set the background of an ImageView (so I could set the foreground to a different image).
I eventually found https://github.com/bumptech/glide/issues/938#issuecomment-176150770, but that's a bit out of date. Through experimentation I altered it to the following to work with Glide 4 (similar to some of the other answers here):
/** #see ImageViewTarget
*/
abstract class ViewBackgroundTarget<Z>(view: View) : CustomViewTarget<View?, Z>(view) {
override fun onLoadFailed(errorDrawable: Drawable?) {
}
override fun onResourceCleared(placeholder: Drawable?) {
// This line is ESSENTIAL or else there will be occasional crashes like:
// "java.lang.NullPointerException: Attempt to invoke virtual method
// 'boolean android.graphics.Bitmap.isRecycled()' on a null object reference"
view!!.background = placeholder
}
}
/** #see BitmapImageViewTarget
*/
class BitmapViewBackgroundTarget(view: View) : ViewBackgroundTarget<Bitmap>(view) {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
view!!.background = BitmapDrawable(view.resources, resource)
}
}
/** #see DrawableImageViewTarget
*/
class DrawableViewBackgroundTarget(view: View) : ViewBackgroundTarget<Drawable>(view) {
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
view!!.background = resource
if (resource is GifDrawable) {
resource.setLoopCount(LOOP_FOREVER)
resource.start()
}
}
}
use this with
Glide.with(myImageView)
.load(imageUrl)
.into(DrawableViewBackgroundTarget(myImageView))
I assume this would work easily for a RelativeLayout or other View types as well.
Related
Am just starting to learn Adroind and am coming up with an Audio player app and am stuck, I dont know how am going to insert the audio files in my Recycler Adapter for it to play. can someone help me out please?
public class AudioRecyclerViewAdapter extends RecyclerView.Adapter<AudioRecyclerViewAdapter.ViewHolder> {
// Global variables
ArrayList audios;
MainActivity mainActivity;
public AudioRecyclerViewAdapter(
// Local variables
MainActivity mainActivity, ArrayList<Audio> audios
) {
this.audios = audios;
this.mainActivity = mainActivity;
}
#NonNull
#Override
public AudioRecyclerViewAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
// Link to audio item layout
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.audio_item, parent, false);
return new ViewHolder(v);
}
/**
* Change item layout views
* #param viewHolder
* #param position
*/
#Override
public void onBindViewHolder(#NonNull AudioRecyclerViewAdapter.ViewHolder viewHolder, int position) {
// Get audio
Audio audio = audios.get(position);
// Update views
viewHolder.titleTextView.setText(audio.getTitle());
viewHolder.artworkHolder.setImageResource(audio.getImage());
viewHolder.audioLinearLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// Check if clicked song is active song.
if(audio.equals(mainActivity.activeAudio)) {
if(audio.isPlaying) {
viewHolder.playIcon.setImageResource(R.drawable.ic_pause_circle_outline_24);
mainActivity.pauseAudio(mainActivity.activeAudio);
} else {
viewHolder.playIcon.setImageResource(R.drawable.ic_play_circle_outline_24);
mainActivity.playAudio(mainActivity.activeAudio);
}
} else {
// Pause previous song
mainActivity.pauseAudio(audio);
// Update to current song
viewHolder.playIcon.setImageResource(R.drawable.ic_pause_circle_outline_24);
mainActivity.setActiveAudio(audio);
}
}
});
}
#Override
public int getItemCount() {
return audios.size();
}
/**
* Access item layout views
*/
public class ViewHolder extends RecyclerView.ViewHolder {
LinearLayout audioLinearLayout;
TextView titleTextView;
ImageView playIcon;
ImageView artworkHolder;
RecyclerView audioRecyclerView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
audioLinearLayout = itemView.findViewById(R.id.playing_audio_linear_layout);
titleTextView = itemView.findViewById(R.id.playing_title_text_view);
playIcon = itemView.findViewById(R.id.playing_play_pause_image_button);
artworkHolder = itemView.findViewById(R.id.playing_audio_image_view);
audioRecyclerView=itemView.findViewById(R.id.audio_recycler_view);
}
}
}
The following Tabbed Activity is created by Android Studio 1.2.2 Wizard, it works well in API 9, but someboyd told me that ActionBarActivity is deprecated,
so I hope to replace public class MainActivity extends ActionBarActivity implements ActionBar.TabListener with public class MainActivity extends AppCompatActivity implements ActionBar.TabListener , and stay all other code the same, is it OK?
Thanks!
package com.example.cuiwei.myapplication;
import java.util.Locale;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.FragmentPagerAdapter;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity implements ActionBar.TabListener {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {#link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {#link android.support.v4.app.FragmentStatePagerAdapter}.
*/
SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set up the action bar.
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
// When swiping between different sections, select the corresponding
// tab. We can also use ActionBar.Tab#select() to do this if we have
// a reference to the Tab.
mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
// Create a tab with text corresponding to the page title defined by
// the adapter. Also specify this Activity object, which implements
// the TabListener interface, as the callback (listener) for when
// this tab is selected.
actionBar.addTab(
actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
// When the given tab is selected, switch to the corresponding page in
// the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
}
/**
* A {#link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
return PlaceholderFragment.newInstance(position + 1);
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";
/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
}
}
You can safely replace ActionBarActivity with AppCompatActivity.
As you can see in v7-appcompat source code from version v22.1.0 ActionBarActivity simply extends AppCompatActivity:
/**
* #deprecated Use {#link android.support.v7.app.AppCompatActivity} instead.
*/
#Deprecated
public class ActionBarActivity extends AppCompatActivity {
}
I need to detect when a TextView is made visible and when its made invisible. Is there any way to do this in Android? I've to make some processing when a TextView in my application changes visibility. Actually, it's visibility is being updated at a number of places and I'm looking to avoid calls at all of these places.
I don't know if this will answer your question, but if you want to listen to textview visibility changes, I suggest customizing TextView and implement your own listener for it.
public class TextViewExtension extends TextView {
protected OnVisibilityChange mChangeListener = null;
public interface OnVisibilityChange {
void onChange(TextViewExtension mTextView, int mPrevVisibility, int mNewVisibility);
}
public TextViewExtension(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
/* (non-Javadoc)
* #see android.view.View#setVisibility(int)
*/
#Override
public void setVisibility(int visibility) {
// TODO Auto-generated method stub
super.setVisibility(visibility);
if (mChangeListener != null) {
mChangeListener.onChange(this, getVisibility(), visibility);
}
}
public void setOnVisibilityChange(OnVisibilityChange mChangeListener) {
this.mChangeListener = mChangeListener;
}
}
Here are examples for further implementation if you are curious
Hope it helps :)
visibility="gone"
in your xml fil
static boolean textviewon=false;
if(textviewon){
textview.setvisibility(View.Visible);
}else
textview.setvisibility(View.Invisible);
put above condition in your code
I want to set image and text in imageview and textview in the OnPostMethod of AsyncClass, like I am doing some network operations in "do in background" method, and on a particular response I just want to populate imageviews and textviews with particular images and texts.
Can someone tell me how to set image in OnPostMethod. I wonder if someone can tell me the mistake I am making in the following code
public class testing_async extends Activity {
private ProgressDialog progressDialog;
String s;
ImageView im;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.testing_async);
new GetCategoryList().execute(s);
im = (ImageView) findViewById(R.id.im1);
}
//
// THE AsyncTask Class for GetCategoryList....
//
class GetCategoryList extends AsyncTask<String, String, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
// showDialog(progress_bar_type);
progressDialog = ProgressDialog.show(testing_async.this, "",
"Loading...");
}
#Override
protected String doInBackground(String... f_url) {
/*
* LinearLayout layout = new LinearLayout(testing_async.this);
* layout = new LinearLayout(testing_async.this);
* layout.setLayoutParams(new ViewGroup.LayoutParams(
* ViewGroup.LayoutParams.FILL_PARENT,
* ViewGroup.LayoutParams.FILL_PARENT));
*
* RelativeLayout inner_layout = new
* RelativeLayout(testing_async.this); inner_layout = new
* RelativeLayout(testing_async.this); layout.setLayoutParams(new
* ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT,
* ViewGroup.LayoutParams.FILL_PARENT));
*
* ImageView im = new ImageView(testing_async.this);
* im.setImageResource(R.drawable.logo_background);
* RelativeLayout.LayoutParams layoutParams90 = new
* RelativeLayout.LayoutParams( 225, 250); layoutParams90.leftMargin
* = 45; layoutParams90.topMargin = 45;
* im.setLayoutParams(layoutParams90); inner_layout.addView(im);
* layout.addView(inner_layout);
*/
return null;
}
protected void onProgressUpdate(String... progress) {
}
#Override
protected void onPostExecute(String file_url) {
im.setBackgroundResource(R.drawable.back);
progressDialog.dismiss();
}
}
}
Context mContext;
ImageView im;
public GetCategoryList(Context context, ImageView myIm) {
this.mContext = context;
this.im = myIml;
}
Add the custom constructor above to your async task. This way you have a context and a reference to your imageView. It will be needed in order to get the resource.
Second, all the commented code should be placed inside "onCreate".
Finally, you can assign a drawable to your imageView using
im.setBackgroundResource(mContext.getResources().getDrawable(R.drawable.bBack));
Hope this helps.
P.S. NEVER change layout in "doInBackground". Your app will crash.
if you are setting image
then you have to give im.setBackgroundResource(R.drawable.bBack);
you are setting id not image in your code.
I consider an ActivePivot instance to compute CVA (Credit Valuation Adjustment).
I have to apply a piece of logic on a large number of cells (20k for each counter-party), each being associated to a float array of size 10k. Even if ActivePivot is massively multithreaded, an ABasicPostProcessor will be applied in a mono-threaded way for each range location. How could I make it compute through my point location in a multi-threaded way?
I built the following class, which specialize ABasicPostProcessor (a Core class enabling fast implementation of per-point post-processor) by just adding the calls to doEvaluation in a multi-threaded way.
Given an ABasicPostProcessor specialisation, one simply has to extend AParallelBasicPostProcessor in order to gain parallel evaluation!
/**
* Specialization of ABasicPostProcessor which will call doEvaluation in a
* multithreaded way
*
* #author BLA
*/
public abstract class AParallelBasicPostProcessor<OutputType> extends ABasicPostProcessor<OutputType> {
private static final long serialVersionUID = -3453966549173516186L;
public AParallelBasicPostProcessor(String name, IActivePivot pivot) {
super(name, pivot);
}
#Override
public void evaluate(ILocation location, final IAggregatesRetriever retriever) throws QuartetException {
// Retrieve required aggregates
final ICellSet cellSet = retriever.retrieveAggregates(Collections.singleton(location), Arrays.asList(prefetchMeasures));
// Prepare a List
List<ALocatedRecursiveTask<OutputType>> tasks = new ArrayList<ALocatedRecursiveTask<OutputType>>();
// Create the procedure to hold the parallel sub-tasks
final ICellsProcedure subTasksGeneration = makeSubTasksGenerationProcedure(tasks);
cellSet.forEachLocation(subTasksGeneration, underlyingMeasures);
ForkJoinTask.invokeAll(tasks);
for (ALocatedRecursiveTask<OutputType> task : tasks) {
OutputType returnValue;
try {
returnValue = task.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
// re-throw the root cause of the ExecutionException
throw new RuntimeException(e.getCause());
}
// We can write only non-null aggregates
if (null != returnValue) {
writeInRetriever(retriever, task.getLocation(), returnValue);
}
}
}
protected void writeInRetriever(IAggregatesRetriever retriever, ILocation location, OutputType returnValue) {
retriever.write(location, returnValue);
}
protected ICellsProcedure makeSubTasksGenerationProcedure(List<ALocatedRecursiveTask<OutputType>> futures) {
return new SubTasksGenerationProcedure(futures);
}
/**
* {#link ICellsProcedure} registering a {#link ALocatedRecursiveTask} per
* point location
*/
protected class SubTasksGenerationProcedure implements ICellsProcedure {
protected List<ALocatedRecursiveTask<OutputType>> futures;
public SubTasksGenerationProcedure(List<ALocatedRecursiveTask<OutputType>> futures) {
this.futures = futures;
}
#Override
public boolean execute(final ILocation pointLocation, int rowId, Object[] measures) {
// clone the array of measures as it is internally used as a buffer
final Object[] clone = measures.clone();
futures.add(makeLocatedFuture(pointLocation, clone));
return true;
}
}
protected ALocatedRecursiveTask<OutputType> makeLocatedFuture(ILocation pointLocation, Object[] measures) {
return new LocatedRecursiveTask(pointLocation, measures);
}
/**
* A specialization of RecursiveTask by associating it to a
* {#link ILocation}
*
* #author BLA
*
*/
protected static abstract class ALocatedRecursiveTask<T> extends RecursiveTask<T> {
private static final long serialVersionUID = -6014943980790547011L;
public abstract ILocation getLocation();
}
/**
* Default implementation of {#link ALocatedRecursiveTask}
*
* #author BLA
*
*/
protected class LocatedRecursiveTask extends ALocatedRecursiveTask<OutputType> {
private static final long serialVersionUID = 676859831679236794L;
protected ILocation pointLocation;
protected Object[] measures;
public LocatedRecursiveTask(ILocation pointLocation, Object[] measures) {
this.pointLocation = pointLocation;
this.measures = measures;
if (pointLocation.isRange()) {
throw new RuntimeException(this.getClass() + " accepts only point location: " + pointLocation);
}
}
#Override
protected OutputType compute() {
try {
// The custom evaluation will be computed in parallel
return AParallelBasicPostProcessor.this.doEvaluation(pointLocation, measures);
} catch (QuartetException e) {
throw new RuntimeException(e);
}
}
#Override
public ILocation getLocation() {
return pointLocation;
}
}
}
The ActivePivot query engine is heavily multithreaded, the invocation of several post processors within a single query is done in parallel (unless one depends on the result of another of course). When the same post processor is executed several times over the locations involved in the query, that's also done in parallel. So before rolling up your sleeves, it is worth checking whether there isn't a more obvious bottleneck in your query plan.
Now the invocation of one post processor over one location is indeed an indivisible workload in the ActivePivot query engine. And in the case aggregates are not just numbers that sum in nanoseconds, but large or structured objects like vectors, there may be room for parallelism driven performance boost.
The ActivePivot query engine is built on top of a fork/join pool (http://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html). It means that your post processor code is always called from within the fork join pool, and that makes it possible to fork your own sub-tasks, then join them. That is considered an expert trick, don't try that without a fair understanding of how the fork join pool works.
Let's consider a post processor that for each evaluated location computes the maximum of several measures:
package com.quartetfs.pivot.sandbox.postprocessor.impl;
import com.quartetfs.biz.pivot.IActivePivot;
import com.quartetfs.biz.pivot.ILocation;
import com.quartetfs.biz.pivot.postprocessing.impl.ABasicPostProcessor;
import com.quartetfs.fwk.QuartetException;
import com.quartetfs.fwk.QuartetExtendedPluginValue;
/**
*
* Post processor that computes the MAX of several measures.
*
* #author Quartet FS
*
*/
#QuartetExtendedPluginValue(interfaceName = "com.quartetfs.biz.pivot.postprocessing.IPostProcessor", key = MaxPostProcessor.TYPE)
public class MaxPostProcessor extends ABasicPostProcessor<Double> {
/** serialVersionUID */
private static final long serialVersionUID = -8886545079342151420L;
/** Plugin type */
public static final String TYPE = "MAX";
public MaxPostProcessor(String name, IActivePivot pivot) {
super(name, pivot);
}
#Override
public String getType() { return TYPE; }
#Override
protected Double doEvaluation(ILocation location, Object[] measures) throws QuartetException {
double max = ((Number) measures[0]).doubleValue();
for(int i = 1; i < measures.length; i++) {
max = Math.max(max, ((Number) measures[i]).doubleValue());
}
return max;
}
}
In that post processor the leaf locations resulting from the evaluated range location will be computed one after the other. You can decide to create tasks instead, and execute those tasks in parallel through the fork join pool. I hope the following will get you started:
package com.quartetfs.pivot.sandbox.postprocessor.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import jsr166y.ForkJoinTask;
import jsr166y.RecursiveTask;
import com.quartetfs.biz.pivot.IActivePivot;
import com.quartetfs.biz.pivot.ILocation;
import com.quartetfs.biz.pivot.cellset.ICellSet;
import com.quartetfs.biz.pivot.cellset.ICellsProcedure;
import com.quartetfs.biz.pivot.query.aggregates.IAggregatesRetriever;
import com.quartetfs.fwk.QuartetException;
import com.quartetfs.fwk.QuartetExtendedPluginValue;
/**
*
* Post processor that computes the MAX of several measures,
* evaluation of locations is performed in parallel.
*
* #author Quartet FS
*
*/
#QuartetExtendedPluginValue(interfaceName = "com.quartetfs.biz.pivot.postprocessing.IPostProcessor", key = ParallelMaxPostProcessor.TYPE)
public class ParallelMaxPostProcessor extends MaxPostProcessor {
/** serialVersionUID */
private static final long serialVersionUID = -8886545079342151420L;
/** Plugin type */
public static final String TYPE = "PMAX";
public ParallelMaxPostProcessor(String name, IActivePivot pivot) {
super(name, pivot);
}
#Override
public String getType() { return TYPE; }
#Override
public void evaluate(ILocation location, IAggregatesRetriever retriever)throws QuartetException {
try {
// Retrieve required aggregates
ICellSet cellSet = retriever.retrieveAggregates(Collections.singleton(location), Arrays.asList(prefetchMeasures));
// Evaluate the cell set to create tasks
ParallelEvaluationProcedure evalProcedure = new ParallelEvaluationProcedure();
cellSet.forEachLocation(evalProcedure);
// Execute the tasks in parallel and write results
evalProcedure.writeResults(retriever);
} catch(Exception e) {
throw new QuartetException("Evaluation of " + this + " on location " + location + " failed.", e);
}
}
/**
* Procedure evaluated on the cell set.
*/
protected class ParallelEvaluationProcedure implements ICellsProcedure {
/** List of tasks */
protected final List<MaxComputation> tasks = new ArrayList<ParallelMaxPostProcessor.MaxComputation>();
#Override
public boolean execute(ILocation location, int rowId, Object[] measures) {
Object[] numbers = measures.clone();
tasks.add(new MaxComputation(location, numbers));
return true; // continue
}
/** Once all the tasks are executed, write results */
public void writeResults(IAggregatesRetriever retriever) throws Exception {
// Invoke all the tasks in parallel
// using the fork join pool that runs the post processor.
ForkJoinTask.invokeAll(tasks);
for(MaxComputation task : tasks) {
retriever.write(task.location, task.get());
}
}
}
/**
* Max computation task. It illustrates our example well
* but in real-life this would be too little
* of a workload to deserve parallel execution.
*/
protected class MaxComputation extends RecursiveTask<Double> {
/** serialVersionUID */
private static final long serialVersionUID = -5843737025175189495L;
final ILocation location;
final Object[] numbers;
public MaxComputation(ILocation location, Object[] numbers) {
this.location = location;
this.numbers = numbers;
}
#Override
protected Double compute() {
try {
return doEvaluation(location, numbers);
} catch (QuartetException e) {
completeExceptionally(e);
return null;
}
}
}
}