I'm using spring-kafka-2.2.7-RELEASE. I'm trying to capture the event when a consumer is ready to consume message and trying to use ConsumerAwareRebalanceListener but it's not working. Please suggest.
#Component
public class ConsumerAwareRebalanceListenerImpl implements ConsumerAwareRebalanceListener {
public void ConsumerAwareRebalanceListenerImpl(){
System.out.println(" In ConsumerAwareRebalanceListenerImpl constructor");
}
#Override
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
}
#Override
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
partitions.forEach( item -> {
TestConsumerConstants.consumerEventsMap.put("key-"+item.partition(), item.partition());
});
}
#Override
public void onPartitionsAssigned(Consumer<?, ?> consumer, Collection<TopicPartition> partitions){
TestConsumerConstants.consumerEventsMap.put(consumer.toString(), partitions);
}
}
I fixed the problem and it's working now. All I had to do is, set the containerProperty 'setConsumerRebalanceListener' of the ConcurrentKafkaListenerContainerFactory to my custom class instance as shown below
ConcurrentKafkaListenerContainerFactory factory = new
ConcurrentKafkaListenerContainerFactory<>();
factory.getContainerProperties().setConsumerRebalanceListener(kafkaConsumerRebalanceListener);
I've been thinking about the correct use of context cause I have a problem when I use Volley.newRequestQueue(getBaseContext());
I don't know the correct context that I have to use, I've watched a tutorial and in the video the man use a Fragment but I am using an Activity so its diferent and also the same autor of the video wrote that we had to change the context if we want to use the same code in an Activity.
I've tried with:
getAplicationcontext()
getContext()
getBasedContext()
getAplication()
The worst it's that my aplication doesn´t tell the error, it said: it has found the user: asd#gmalcom, that means that aparently registration is succesfull but when I see the database there is nothing...
public class another extends AppCompatActivity implements
Response.Listener<JSONObject>,Response.ErrorListener{
RequestQueue rq;
JsonRequest jrqt;
EditText txtnombre,txtapellido,txtcel,txtCorreo,txtContra,txtRecontraReg;
Button btnIngresar;
TextView txtCrear,txtolvido;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_another);
txtnombre=(EditText) findViewById(R.id.edtxtnomReg);
txtapellido=(EditText) findViewById(R.id.edtxtApellReg);
txtcel=(EditText) findViewById(R.id.edtxtCelReg);
txtContra=(EditText) findViewById(R.id.edtxtContraReg);
txtCorreo=(EditText) findViewById(R.id.edtxtCorreoReg);
btnIngresar=(Button) findViewById(R.id.btnReg);
txtCrear=(TextView) findViewById(R.id.txtCrearCuenta);
txtolvido=(TextView) findViewById(R.id.txtcontraOlvidadLog);
//revisar esto
**rq=Volley.newRequestQueue(getBaseContext());**
btnIngresar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
registrar_usuario();
}
});
}
private void registrar_usuario() {
String urli="http://*************************************.php?
nombre_cli="+txtnombre.getText().toString()+
"&apellido_cli="+txtContra.getText().toString()+
"&correo_cli="+txtCorreo.getText().toString()+
"&celular_cli="+txtcel.getText().toString()+
"&contrasena_cli="+txtContra.getText().toString();
jrqt=new JsonObjectRequest(Request.Method.GET,urli,null,this,this);
rq.add(jrqt);
}
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(this,"No se pudo registrar el usuario
"+error.toString(),Toast.LENGTH_SHORT).show();
}
#Override
public void onResponse(JSONObject response) {
Clientes cli=new Clientes();
Toast.makeText(this,"Se ha encontrado el
usuario"+txtCorreo.getText().toString(),Toast.LENGTH_SHORT).show();
//limpiartextview();
}
private void limpiartextview() {
txtnombre.setText("");
txtapellido.setText("");
txtCorreo.setText("");
txtcel.setText("");
txtContra.setText("");
}
}
I’m creating my first Firebase App. One of its requirements is that it run when the network is not available. The Firebase guide states:
Enabling disk persistence allows our app to also keep all of its state even after an app restart. We can enable disk persistence with just one line of code.
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
With disk persistence enabled, our synced data and writes will be persisted to disk across app restarts and our app should work seamlessly in offline situations.
Another requirement is to use Google Sign In. So in my MainActivity I check if the User is signed in, if not, I launch the SignInActivity. (The SignInActivity is from the Firebase examples.) The SignInActivity works, the user gets logged in, and MainActivity is launched for a second time. Now my app crashes on the code line FirebaseDatabase.getInstance().setPersistenceEnabled(true); with the following message:
Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.
Now if I restart my app, the User is signed in, the SignInActivity is not launched, my app runs fine.
Any suggestions of how I avoid this crash after the User signs in?
As I was posting this question, I received a suggestion to relocate FirebaseDatabase.getInstance().setPersistenceEnabled(true);
to my “Application class”. I get exactly the same result … SignInActivity starts, completes, and I get a crash on the setPersistenceEnabled.
Below is my MainActivity onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.
// Crash here upon returning from SignInActivity.
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
mFirebaseDbReference = FirebaseDatabase.getInstance().getReference();
// Initialize Firebase Auth
mFirebaseAuth = FirebaseAuth.getInstance();
mFirebaseUser = mFirebaseAuth.getCurrentUser();
if (mFirebaseUser == null) {
// Not signed in, launch the Sign In activity
Timber.tag("MainActivity").i("onCreate(): User not signed in, launching SignInActivity");
startActivity(new Intent(this, SignInActivity.class));
finish();
} else {
mUsername = mFirebaseUser.getDisplayName();
Timber.tag("MainActivity").i("onCreate(): User \"%s\" signed in.", mUsername);
if (mFirebaseUser.getPhotoUrl() != null) {
mPhotoUrl = mFirebaseUser.getPhotoUrl().toString();
}
}
A FirebaseApp is initialized by a ContentProvider so it is not initialized at the time onCreate() is called.
Get your FirebaseDatabase like this:
public class Utils {
private static FirebaseDatabase mDatabase;
public static FirebaseDatabase getDatabase() {
if (mDatabase == null) {
mDatabase = FirebaseDatabase.getInstance();
mDatabase.setPersistenceEnabled(true);
}
return mDatabase;
}
}
Then call Utils.getDatabase() from any activity you want.
Read more in this article
I fixed this exception by using setPersistenceEnabled(true) in my Application class.
public class MApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
}
In AndroidManifest.xml, set the application name as MApplication:
<application
android:name=".MApplication"
... />
I was facing a similar problem and using a static variable seemed to resolve the issue for me. So at first my code looked something like this
#Override
protected void onCreate(Bundle savedInstanceState) {
//..code
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
FirebaseDatabase database = FirebaseDatabase.getInstance();
//..code
}
and now it looks more like
static boolean calledAlready = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
//..code
if (!calledAlready)
{
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
calledAlready = true;
}
FirebaseDatabase database = FirebaseDatabase.getInstance();
//..code
}
Hope it helps!
I bit late but today i got this issue, I solved by adding
static {
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
to my Activity
To me, it is easier to be handled by creating a separate class for Firebase. This is because Firebase has its own instance and if you are using it in more than one activity, there's the possibility for it to crash if you call setPersistenceEnabled again in another activity.
Another good thing is that you can pass your context or parameters into the FirebaseHandler constructor if required. Or if you have fixed location in the database, they can be called easy without the .child("location") boilerplate.
Example:
public class FirebaseHandler {
// parameters
private Context context;
private String userKey;
private DatabaseReference databaseReference;
private static boolean isPersistenceEnabled = false;
private static String fixedLocationA = "locationA";
private static String fixedLocationB = "locationB";
public FirebaseHandler(Context context, String userKey) {
this.context = context; // context can be used to call PreferenceManager etc.
this.userKey = userKey;
if (!isPersistenceEnabled) {
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
isPersistenceEnabled = true;
}
databaseReference = FirebaseDatabase.getInstance().getReference().child(userKey);
}
public DatabaseReference getRefA() {
return databaseReference.child(fixedLocationA);
}
public DatabaseReference getRefB() {
return databaseReference.child(fixedLocationB);
}
}
This can then be called in any Activity as below.
public class MyActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get instance
FirebaseHandler firebaseHandler = new FirebaseHander(this, "userKey");
// to set value
firebaseHandler.getRefA().setValue("value");
// to set listener
firebaseHandler.getRefB().addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// TODO here....
// also, can remove listener if required
if (certain condition) {
firebaseHandler.getRefB().removeEventListener(this);
}
}
}
}
}
I'm facing some problem too, but this is my temporary solution for my app.
Create BaseActivity extends AppcompatActivity and override onCreate, put setPersistenceEnabled there.
public class BaseActivity extends AppCompatActivity {
private static String TAG = "BaseActivity";
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try{
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
Log.d(TAG,FirebaseDatabase.getInstance().toString());
}catch (Exception e){
Log.w(TAG,"SetPresistenceEnabled:Fail"+FirebaseDatabase.getInstance().toString());
e.printStackTrace();
}
}
}
And change MainActivity to extend BaseActivity
public class MainActivity extends BaseActivity
EDIT: Follow #imakeApps answer
public class BaseActivity extends AppCompatActivity {
private static String TAG = "BaseActivity";
static boolean isInitialized = false;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try{
if(!isInitialized){
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
isInitialized = true;
}else {
Log.d(TAG,"Already Initialized");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
Solved it by making the Firebase reference a static class field like this:
public class MainActivity extends AppCompatActivity
private static FirebaseDatabase fbDatabase;
#Override
protected void onCreate(Bundle savedInstanceState) {
if(fbDatabase == null) {
fbDatabase = FirebaseDatabase.getInstance();
fbDatabase.setPersistenceEnabled(true);
}
It's no problem to create new Firebase references (without setPersistenceEnabled(true)) in other activities too.
If you don't like the static fields, this did the trick for me:
if (FirebaseApp.getApps(context).isEmpty()) {
FirebaseApp.initializeApp(context);
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
This can be caused be more that one process initializing twice firebase or Multidex apps. For more info see this: https://github.com/firebase/quickstart-android/issues/15
I wouldn't recommend using Application to store the data because like its written in CodePath
There is always data and information that is needed in many places within your app. This might be a session token, the result of an expensive computation, etc. It might be tempting to use the application instance in order to avoid the overhead of passing objects between activities or keeping those in persistent storage.
However, you should never store mutable instance data inside the Application object because if you assume that your data will stay there, your application will inevitably crash at some point with a NullPointerException. The application object is not guaranteed to stay in memory forever, it will get killed. Contrary to popular belief, the app won’t be restarted from scratch. Android will create a new Application object and start the activity where the user was before to give the illusion that the application was never killed in the first place.
Thats the reason I would recommend using a Singleton like this:
public class DataBaseUtil {
private static FirebaseDatabase mDatabase;
public static FirebaseDatabase getDatabase() {
if (mDatabase == null) {
mDatabase = FirebaseDatabase.getInstance();
mDatabase.setPersistenceEnabled(true);
}
return mDatabase;
}}
just use it in your code then like
private FirebaseDatabase fdb = DataBaseUtil.getDatabase();
Create a class called Util.java
and add following code
public class Util {
private static FirebaseDatabase mData;
public static FirebaseDatabase getDatabase() {
if (mData == null) {
mData = FirebaseDatabase.getInstance();
mData.setPersistenceEnabled(true);
}
return mData;
}
}
Now replace FirebaseDatabase.getIntance() with Util.getDatabase() every time in each activity. Calling just once will get the error!
I was facing same issue. i changed code as below.
BEFORE(Causing Crash)
var rootRef = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
FIRDatabase.database().persistenceEnabled = true
}
AFTER (Resolved Crash)
var rootRef:FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
FIRDatabase.database().persistenceEnabled = true
rootRef = FIRDatabase.database().reference()
}
In Menifest
android:name=".AppName
Create java Class that extends Application
public class AppName extends Application {
#Override
public void onCreate() {
super.onCreate();
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
Make sure that .setpersistenceenabled(true) is not happening twice, while sign in by Google in your case, second care setPersistenceEnabled(true) is must be called before any instance of firebase called this resolve my issue.
For Kotlin Try this:
class DatabaseUtil {
companion object {
private val firebaseDatabase: FirebaseDatabase = FirebaseDatabase.getInstance()
init {
firebaseDatabase.setPersistenceEnabled(true)
}
fun getDatabase() : FirebaseDatabase {
return firebaseDatabase
}
}
}
Simply move you code in ExampleFragment.class from onCreateView method to onCreate method:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
....
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_home, container, false);
The error message describes the problem:
Calls to setPersistenceEnabled() must be made before any other usage
of FirebaseDatabase instance.
The fix to this problem is described into the documentation: As in SDK 2.x, persistence of the disk must be enabled before other calls to the database are made.
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
https://firebase.google.com/support/guides/firebase-android
You can use:
FirebaseDatabase.getInstance().setPersistenceEnabled(true);
before using FirebaseDatabase.getInstance().getReference();
How to Start react-native inside of a fragment?
While putting react-native inside Fragment, onCreateView function is unable to return View from mReactRootView.
View view = inflater.inflate(mReactRootView. , container, false);
I've managed to figure this out with much trial and error. I've seen this question asked around the internet and thought that this was the best place to post the answer. Here is how to do with the latest version of React (0.29 as of this writing):
The first thing we'll do is create an abstract ReactFragment class that we will use throughout our app:
public abstract class ReactFragment extends Fragment {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
// This method returns the name of our top-level component to show
public abstract String getMainComponentName();
#Override
public void onAttach(Context context) {
super.onAttach(context);
mReactRootView = new ReactRootView(context);
mReactInstanceManager =
((MyApplication) getActivity().getApplication())
.getReactNativeHost()
.getReactInstanceManager();
}
#Override
public ReactRootView onCreateView(LayoutInflater inflater, ViewGroup group, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
return mReactRootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mReactRootView.startReactApplication(
mReactInstanceManager,
getMainComponentName(),
null
);
}
}
We'll now be able to create fragments that render React Native components, e.g.:
public class HelloFragment extends ReactFragment {
#Override
public String getMainComponentName() {
return "hellocomponent"; // name of our React Native component we've registered
}
}
A little more work is required, though. Our parent Activity needs to pass some things into the ReactInstanceManager in order for the React Native lifecycle to work properly. Here's what I ended up with:
public class FragmentActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
/*
* Get the ReactInstanceManager, AKA the bridge between JS and Android
* We use a singleton here so we can reuse the instance throughout our app
* instead of constantly re-instantiating and re-downloading the bundle
*/
private ReactInstanceManager mReactInstanceManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
/**
* Get the reference to the ReactInstanceManager
*/
mReactInstanceManager =
((MyApplication) getApplication()).getReactNativeHost().getReactInstanceManager();
/*
* We can instantiate a fragment to show for Activity programmatically,
* or using the layout XML files.
* This doesn't necessarily have to be a ReactFragment, any Fragment type will do.
*/
Fragment viewFragment = new HelloFragment();
getFragmentManager().beginTransaction().add(R.id.container, viewFragment).commit();
}
#Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
/*
* Any activity that uses the ReactFragment or ReactActivty
* Needs to call onHostPause() on the ReactInstanceManager
*/
#Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause();
}
}
/*
* Same as onPause - need to call onHostResume
* on our ReactInstanceManager
*/
#Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
}
Finally, you'll notice the reference to (MyApplication) throughout the code; this is a global Application object to contain our ReactInstanceManager, AKA the bridge between Android and React Native. This is the pattern that the React Native projects use internally, so I simply copied it. Here's how it's implemented:
public class MyApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
#Override
public boolean getUseDeveloperSupport() {
return true;
}
#Override
public List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
};
#Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
}
The trickiest bit was figuring out the lifecycle between the Fragment and the Activity; the ReactRootView needs a reference to the Activity context in order to instantiate, so making sure that getActivity() would not be null was important. Also, registering the onHostPause() and onHostResume() in the parent Activity was unintuitive at first, but ultimately proved simpler once the ReactNativeInstanceManager was abstracted away into a global instead of keeping it on the Activity or Fragment.
Hope this helps someone else out there!
There are libraries available that handle this for you.
One that I use is react-native-android-fragment
As per the instructions on the linked GitHub repository:
Add the following line to your build.gradle compile 'com.github.hudl:react-native-android-fragment:v0.43.2'.
e.g.
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
// Version will correspond to its dependnecy on React Native
compile 'com.github.hudl:react-native-android-fragment:v0.43.2'
}
Build you react code into the fragment
Fragment reactFragment = new ReactFragment.Builder()
.setComponentName("HelloWorld")
.setLaunchOptions(launchOptions) // A Bundle of launch options
.build();
Place the Fragment in a FrameLayout that you would have in your XML layout file. In my case, the FrameLayout ID is react_holder.
getSupportFragmentManager()
.beginTransaction()
.add(R.id.react_holder, reactFragment)
.commit();
There is now an official ReactFragment available here that can be used to host react native inside a fragment.
Just make sure you have your react native host setup correctly, as the fragment tries to access the react native host on the application level, or overload it in a subclass:
// inside the ReactFragment
protected ReactNativeHost getReactNativeHost() {
return ((ReactApplication) getActivity().getApplication()).getReactNativeHost();
}
you can then create the fragment using:
val reactNativeProcessFragment = ReactFragment.Builder()
.setComponentName("nameUsedIn_AppRegistry.registerComponent")
.build()
I have a WearableActivity as my main activity and I want to use fragments for different layouts. The trouble is when I try to get a reference to the main activity using getActivity() in the fragment, Android Studio says that I cannot cast from FragmentActivity to my MainWearActivity(which extends WearableActivity).
My MainWearActivity class:
public class MainWearActivity extends WearableActivity implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener{
private WatchViewStub mContainerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_wear);
mContainerView = (WatchViewStub) findViewById(R.id.mainContainer);
mContainerView.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
#Override
public void onLayoutInflated(WatchViewStub watchViewStub) {
}
});
}
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
#Override
public void onEnterAmbient(Bundle ambientDetails) {
super.onEnterAmbient(ambientDetails);
}
#Override
public void onUpdateAmbient() {
super.onUpdateAmbient();
}
#Override
public void onExitAmbient() {
super.onExitAmbient();
}
}
My fragment class:
public class NavigationFragment extends Fragment {
View view;
MainWearActivity mMainWearActivity;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.navigation_fragment, container, false);
mMainWearActivity = (MainWearActivity) getActivity(); // ERROR HERE
return view;
}
}
When I try to the result of cast getActivity() to my main activity it won't allow me. Can anyone help me to get around this? I need a reference to my main activity so that I can use it as a context. Thanks
EDIT
String's answer below solved the problem. An additional note I'd like to add however, is that if you are using the fragment pager adapter, you will have to add "compile 'com.android.support:support-v13:23.1.1'" to your build.gradle, and use the support v13 pager adapter which allows you to return an android.app.Fragment from GetItem().
Is your NavigationFragment an instance of android.support.v4.app.Fragment, by any chance? I'm guessing so, because that's the Fragment class that expects getActivity() to return a FragmentActivity.
If my guess is correct, what you need to do is find the line in your NavigationFragment.java file that reads:
import android.support.v4.app.Fragment;
and change it to
import android.app.Fragment;
You may need to change a few other imports and calls similarly, to remove reference to the support library and its classes. But there's no need to be using support fragments on Wear - all the devices are well new enough to have fragments natively.
You should implement the AmbientMode.AmbientCallbackProvider instead of using WearableActivity.
It is the new preferred method and it still gives you the onEnterAmbient(), onAmbientUpdate(), and onExitAmbient() but also lets you use Activity (or any sub classes... FragementActivity, etc.). It also allows you to support the Architecture components.
Official docs call out the details.